diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/method/SearchMaterialSqlMethodExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/method/SearchMaterialSqlMethodExecutor.java
index 6e2b16517adcf6ac6b5a2d20cb9657cc2bd5c5c4..a3f8260f8141151fbba2bc5628d8755cd5749f9e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/method/SearchMaterialSqlMethodExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/executor/method/SearchMaterialSqlMethodExecutor.java
@@ -16,12 +16,17 @@
 
 package ch.ethz.sis.openbis.generic.server.api.v3.executor.method;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.executor.common.ISearchObjectExecutor;
+import ch.ethz.sis.openbis.generic.server.api.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.executor.material.ISearchMaterialIdExecutor;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.ITranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.sql.IMaterialSqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialFetchOptions;
@@ -31,9 +36,7 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.search.MaterialSearchCriter
  * @author pkupczyk
  */
 @Component
-public class SearchMaterialSqlMethodExecutor extends
-        AbstractSearchMethodExecutor<Material, Long, MaterialSearchCriterion, MaterialFetchOptions>
-        implements ISearchMaterialMethodExecutor
+public class SearchMaterialSqlMethodExecutor extends AbstractMethodExecutor implements ISearchMaterialMethodExecutor
 {
 
     @Autowired
@@ -43,15 +46,62 @@ public class SearchMaterialSqlMethodExecutor extends
     private IMaterialSqlTranslator translator;
 
     @Override
-    protected ISearchObjectExecutor<MaterialSearchCriterion, Long> getSearchExecutor()
+    public List<Material> search(final String sessionToken, final MaterialSearchCriterion criterion, final MaterialFetchOptions fetchOptions)
     {
-        return searchExecutor;
+        return executeInContext(sessionToken, new IMethodAction<List<Material>>()
+            {
+                @Override
+                public List<Material> execute(IOperationContext context)
+                {
+                    List<Long> results = search(context, criterion, fetchOptions);
+                    return translate(context, results, fetchOptions);
+                }
+            });
     }
 
-    @Override
-    protected ITranslator<Long, Material, MaterialFetchOptions> getTranslator()
+    @SuppressWarnings("unchecked")
+    private List<Long> search(IOperationContext context, MaterialSearchCriterion criterion, MaterialFetchOptions fetchOptions)
     {
-        return translator;
+        if (fetchOptions.getCacheMode() != null)
+        {
+            List<Long> ids = (List<Long>) context.getSession().getAttributes().get(getClass().getName() + "_ids");
+
+            if (ids == null)
+            {
+                ids = searchExecutor.search(context, criterion);
+                context.getSession().getAttributes().put(getClass().getName() + "_ids", ids);
+            }
+
+            return ids;
+        } else
+        {
+            return searchExecutor.search(context, criterion);
+        }
     }
 
+    private List<Material> translate(IOperationContext context, List<Long> peList, MaterialFetchOptions fetchOptions)
+    {
+        if (peList == null || peList.isEmpty())
+        {
+            return Collections.emptyList();
+        }
+
+        TranslationContext translationContext = null;
+
+        if (fetchOptions.getCacheMode() != null)
+        {
+            translationContext = (TranslationContext) context.getSession().getAttributes().get(getClass().getName() + "_context");
+            if (translationContext == null)
+            {
+                translationContext = new TranslationContext(context.getSession());
+                context.getSession().getAttributes().put(getClass().getName() + "_context", translationContext);
+            }
+        } else
+        {
+            translationContext = new TranslationContext(context.getSession());
+        }
+
+        Map<Long, Material> peToObjectMap = translator.translate(translationContext, peList, fetchOptions);
+        return new ArrayList<Material>(peToObjectMap.values());
+    }
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/AbstractCachingTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/AbstractCachingTranslator.java
index e8ca3efc05a86b8ab22c6a9a917f25fca7363f81..881f6772987a2e7a8451e9aaf7b43e98cca86210 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/AbstractCachingTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/AbstractCachingTranslator.java
@@ -16,17 +16,21 @@
 
 package ch.ethz.sis.openbis.generic.server.api.v3.translator;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.FetchOptions;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
@@ -56,38 +60,123 @@ public abstract class AbstractCachingTranslator<I, O, F> extends AbstractTransla
         }
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     protected final Map<I, O> doTranslate(TranslationContext context, Collection<I> allInputs, F fetchOptions)
     {
-        Map<I, O> translated = new LinkedHashMap<I, O>();
-        Map<I, O> updated = new HashMap<I, O>();
-        TranslationCache cache = context.getTranslationCache();
+        Map<I, Long> idsMap = new HashMap<>();
+        for (I input : allInputs)
+        {
+            idsMap.put(input, getId(input));
+        }
 
-        Collection<I> inputs = doShouldTranslate(context, allInputs, fetchOptions);
+        Map<I, O> translated = null;
 
-        for (I input : inputs)
+        if (context.getTranslationCache().hasTranslatedCollection(getClass().getName(), idsMap.values(), fetchOptions))
+        {
+            translated = (Map<I, O>) context.getTranslationCache().getTranslatedCollection(getClass().getName(), idsMap.values(), fetchOptions);
+        } else
         {
-            if (cache.hasTranslatedObject(getClass().getName(), getId(input)))
+            translated = new LinkedHashMap<I, O>();
+            Map<I, O> updated = new HashMap<I, O>();
+            TranslationCache cache = context.getTranslationCache();
+
+            Collection<I> inputs = doShouldTranslate(context, allInputs, fetchOptions);
+
+            for (I input : inputs)
             {
-                handleAlreadyTranslatedInput(context, input, translated, updated, fetchOptions);
-            } else
+                if (cache.hasTranslatedObject(getClass().getName(), getId(input)))
+                {
+                    handleAlreadyTranslatedInput(context, input, translated, updated, fetchOptions);
+                } else
+                {
+                    handleNewInput(context, input, translated, updated, fetchOptions);
+                }
+            }
+
+            if (false == updated.isEmpty())
             {
-                handleNewInput(context, input, translated, updated, fetchOptions);
+                Object relations = getObjectsRelations(context, updated.keySet(), fetchOptions);
+
+                for (Map.Entry<I, O> updatedEntry : updated.entrySet())
+                {
+                    updateObject(context, updatedEntry.getKey(), updatedEntry.getValue(), relations, fetchOptions);
+                }
             }
+
+            context.getTranslationCache().putTranslatedCollection(getClass().getName(), idsMap.values(), fetchOptions,
+                    (Map<Object, Object>) translated);
         }
 
-        if (false == updated.isEmpty())
+        translated = sort(context, translated, fetchOptions);
+        translated = page(context, translated, fetchOptions);
+
+        return translated;
+    }
+
+    private Map<I, O> sort(TranslationContext context, final Map<I, O> map, F fetchOptions)
+    {
+        final Comparator<O> comparator = getObjectComparator(context, fetchOptions);
+
+        if (comparator != null)
         {
-            Relations relations = getObjectsRelations(context, updated.keySet(), fetchOptions);
-            relations.load();
+            Map<O, I> reversedMap = new HashMap<O, I>();
+            for (Map.Entry<I, O> entry : map.entrySet())
+            {
+                reversedMap.put(entry.getValue(), entry.getKey());
+            }
 
-            for (Map.Entry<I, O> updatedEntry : updated.entrySet())
+            List<O> sortedList = new ArrayList<O>(map.values());
+            Collections.sort(sortedList, comparator);
+
+            Map<I, O> sortedMap = new LinkedHashMap<I, O>();
+
+            for (O item : sortedList)
             {
-                updateObject(context, updatedEntry.getKey(), updatedEntry.getValue(), relations, fetchOptions);
+                sortedMap.put(reversedMap.get(item), item);
             }
+
+            return sortedMap;
+        } else
+        {
+            return map;
         }
+    }
 
-        return translated;
+    private Map<I, O> page(TranslationContext context, final Map<I, O> map, F fetchOptions)
+    {
+        // TODO make all fetch options classes extends FetchOptions
+        if (fetchOptions instanceof FetchOptions)
+        {
+            Integer pageIndex = ((FetchOptions) fetchOptions).getPageIndex();
+            Integer pageSize = ((FetchOptions) fetchOptions).getPageSize();
+
+            if (pageIndex != null && pageSize != null)
+            {
+
+                Map<I, O> pagedMap = new LinkedHashMap<I, O>();
+
+                int index = 0;
+                for (Map.Entry<I, O> entry : map.entrySet())
+                {
+                    // TODO break is index > pageIndex + pageSize
+                    if (index >= pageIndex && index < pageIndex + pageSize)
+                    {
+                        pagedMap.put(entry.getKey(), entry.getValue());
+                    }
+
+                    index++;
+                }
+
+                return pagedMap;
+            } else
+            {
+                return map;
+            }
+        } else
+        {
+            return map;
+        }
     }
 
     @SuppressWarnings("unchecked")
@@ -203,20 +292,22 @@ public abstract class AbstractCachingTranslator<I, O, F> extends AbstractTransla
 
         for (I input : checked)
         {
-            cache.putShouldTranslateObject(getClass().getName(), getId(input), true);
+            Long id = getId(input);
+            cache.putShouldTranslateObject(getClass().getName(), id, true);
             if (operationLog.isDebugEnabled())
             {
-                operationLog.debug("Should translate object with id: " + getId(input));
+                operationLog.debug("Should translate object with id: " + id);
             }
         }
 
         toCheck.removeAll(checked);
         for (I input : toCheck)
         {
-            cache.putShouldTranslateObject(getClass().getName(), getId(input), false);
+            Long id = getId(input);
+            cache.putShouldTranslateObject(getClass().getName(), id, false);
             if (operationLog.isDebugEnabled())
             {
-                operationLog.debug("Should NOT translate object with id: " + getId(input));
+                operationLog.debug("Should NOT translate object with id: " + id);
             }
         }
 
@@ -254,7 +345,7 @@ public abstract class AbstractCachingTranslator<I, O, F> extends AbstractTransla
     /**
      * Implementation of this method should create a translated version of the input object. Only basic attributes of the input object should be
      * translated here. Parts that have a corresponding fetch option should be translated in the
-     * {@link AbstractCachingTranslator#updateObject(TranslationContext, Object, Object, Relations, Object)} method.
+     * {@link AbstractCachingTranslator#updateObject(TranslationContext, Object, Object, Object, Object)} method.
      */
     protected abstract O createObject(TranslationContext context, I input, F fetchOptions);
 
@@ -262,19 +353,19 @@ public abstract class AbstractCachingTranslator<I, O, F> extends AbstractTransla
      * Override this method if you want to fetch related objects for all the inputs at once. This way you can greatly improve the performance of the
      * translation.
      */
-    protected Relations getObjectsRelations(TranslationContext context, Collection<I> inputs, F fetchOptions)
+    protected Object getObjectsRelations(TranslationContext context, Collection<I> inputs, F fetchOptions)
     {
-        return new Relations();
+        return new Object();
     }
 
-    protected Relation createRelation(Class<? extends Relation> relationClass, Object... relationParameters)
+    protected Comparator<O> getObjectComparator(TranslationContext context, F fetchOptions)
     {
-        return applicationContext.getBean(relationClass, relationParameters);
+        return null;
     }
 
     /**
      * Implementation of this method should update the translated version of the input object to meet the fetch options.
      */
-    protected abstract void updateObject(TranslationContext context, I input, O output, Relations relations, F fetchOptions);
+    protected abstract void updateObject(TranslationContext context, I input, O output, Object relations, F fetchOptions);
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/Relations.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/Relations.java
deleted file mode 100644
index ced26db868be677b5d6c2bae25678ff4bcb16595..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/Relations.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2014 ETH Zuerich, Scientific IT Services
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.ethz.sis.openbis.generic.server.api.v3.translator;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-
-import ch.systemsx.cisd.common.logging.LogCategory;
-import ch.systemsx.cisd.common.logging.LogFactory;
-
-/**
- * @author pkupczyk
- */
-@SuppressWarnings({ "unchecked" })
-public class Relations
-{
-
-    private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, Relations.class);
-
-    private Map<Class<? extends Relation>, Relation> relationMap = new HashMap<Class<? extends Relation>, Relation>();
-
-    public void add(Relation relation)
-    {
-        relationMap.put(relation.getClass(), relation);
-    }
-
-    public <T extends Relation> T get(Class<T> relationClass)
-    {
-        return (T) relationMap.get(relationClass);
-    }
-
-    public void load()
-    {
-        for (Relation relation : relationMap.values())
-        {
-            operationLog.debug("Started loading a relation: " + relation);
-            relation.load();
-            operationLog.debug("Finished loading a relation: " + relation);
-        }
-    }
-}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/ToManyRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/ToManyRelation.java
deleted file mode 100644
index 125ef78d269ab572cdbddf0f9ee317ee924917e7..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/ToManyRelation.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2014 ETH Zuerich, Scientific IT Services
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.ethz.sis.openbis.generic.server.api.v3.translator;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author pkupczyk
- */
-public abstract class ToManyRelation<OWNER, ORIGINAL, TRANSLATED, FETCH_OPTIONS> implements Relation
-{
-
-    private TranslationContext context;
-
-    private FETCH_OPTIONS fetchOptions;
-
-    private Map<OWNER, Collection<TRANSLATED>> translatedMap;
-
-    public ToManyRelation(TranslationContext context, FETCH_OPTIONS fetchOptions)
-    {
-        this.context = context;
-        this.fetchOptions = fetchOptions;
-    }
-
-    @Override
-    public void load()
-    {
-        translatedMap = getTranslatedMap();
-    }
-
-    private Map<OWNER, Collection<TRANSLATED>> getTranslatedMap()
-    {
-        Map<OWNER, Collection<ORIGINAL>> ownerToOriginalCollectionMap = getOriginalMap();
-        Set<ORIGINAL> originalSet = new HashSet<ORIGINAL>();
-
-        for (Map.Entry<OWNER, Collection<ORIGINAL>> entry : ownerToOriginalCollectionMap.entrySet())
-        {
-            if (entry.getValue() != null)
-            {
-                for (ORIGINAL original : entry.getValue())
-                {
-                    if (original != null)
-                    {
-                        originalSet.add(original);
-                    }
-                }
-            }
-        }
-
-        Map<ORIGINAL, TRANSLATED> originalToTranslatedMap = getTranslatedMap(context, originalSet, fetchOptions);
-        Map<OWNER, Collection<TRANSLATED>> result = new HashMap<OWNER, Collection<TRANSLATED>>();
-
-        for (Map.Entry<OWNER, Collection<ORIGINAL>> ownerToOriginalCollectionEntry : ownerToOriginalCollectionMap.entrySet())
-        {
-            OWNER owner = ownerToOriginalCollectionEntry.getKey();
-            Collection<ORIGINAL> originalCollection = ownerToOriginalCollectionEntry.getValue();
-
-            if (originalCollection != null)
-            {
-                Collection<TRANSLATED> translatedCollection = null;
-
-                if (originalCollection instanceof List)
-                {
-                    translatedCollection = new LinkedList<TRANSLATED>();
-                } else if (originalCollection instanceof Set)
-                {
-                    translatedCollection = new LinkedHashSet<TRANSLATED>();
-                } else
-                {
-                    throw new IllegalArgumentException("Collection of type: " + originalCollection.getClass() + " is not supported.");
-                }
-
-                for (ORIGINAL original : originalCollection)
-                {
-                    if (original != null)
-                    {
-                        TRANSLATED translated = originalToTranslatedMap.get(original);
-                        if (translated != null)
-                        {
-                            translatedCollection.add(translated);
-                        }
-                    } else
-                    {
-                        translatedCollection.add(null);
-                    }
-                }
-
-                result.put(owner, translatedCollection);
-            } else
-            {
-                result.put(owner, null);
-            }
-        }
-
-        return result;
-    }
-
-    public List<TRANSLATED> getTranslatedList(OWNER owner)
-    {
-        return (List<TRANSLATED>) translatedMap.get(owner);
-    }
-
-    public Set<TRANSLATED> getTranslatedSet(OWNER owner)
-    {
-        return (Set<TRANSLATED>) translatedMap.get(owner);
-    }
-
-    protected abstract Map<OWNER, Collection<ORIGINAL>> getOriginalMap();
-
-    @SuppressWarnings("hiding")
-    protected abstract Map<ORIGINAL, TRANSLATED> getTranslatedMap(TranslationContext context, Collection<ORIGINAL> originalCollection,
-            FETCH_OPTIONS fetchOptions);
-
-}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/ToOneRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/ToOneRelation.java
deleted file mode 100644
index 373b47c5cfb40a3479981569d3f8d7223466d871..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/ToOneRelation.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2014 ETH Zuerich, Scientific IT Services
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.ethz.sis.openbis.generic.server.api.v3.translator;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author pkupczyk
- */
-public abstract class ToOneRelation<OWNER, ORIGINAL, TRANSLATED, FETCH_OPTIONS> implements Relation
-{
-
-    private TranslationContext context;
-
-    private FETCH_OPTIONS fetchOptions;
-
-    private Map<OWNER, TRANSLATED> translatedMap;
-
-    public ToOneRelation(TranslationContext context, FETCH_OPTIONS fetchOptions)
-    {
-        this.context = context;
-        this.fetchOptions = fetchOptions;
-    }
-
-    @Override
-    public void load()
-    {
-        translatedMap = getTranslatedMap();
-    }
-
-    private Map<OWNER, TRANSLATED> getTranslatedMap()
-    {
-        Map<OWNER, ORIGINAL> ownerToOriginalMap = getOriginalMap();
-        Set<ORIGINAL> originalSet = new HashSet<ORIGINAL>();
-
-        for (ORIGINAL original : ownerToOriginalMap.values())
-        {
-            if (original != null)
-            {
-                originalSet.add(original);
-            }
-        }
-
-        Map<ORIGINAL, TRANSLATED> originalToTranslatedMap = getTranslatedMap(context, originalSet, fetchOptions);
-        Map<OWNER, TRANSLATED> ownerToTranslatedMap = new LinkedHashMap<OWNER, TRANSLATED>();
-
-        for (Map.Entry<OWNER, ORIGINAL> entry : ownerToOriginalMap.entrySet())
-        {
-            OWNER owner = entry.getKey();
-            ORIGINAL original = entry.getValue();
-            TRANSLATED translated = originalToTranslatedMap.get(original);
-            ownerToTranslatedMap.put(owner, translated);
-        }
-
-        return ownerToTranslatedMap;
-    }
-
-    public TRANSLATED getTranslated(OWNER owner)
-    {
-        return translatedMap.get(owner);
-    }
-
-    protected abstract Map<OWNER, ORIGINAL> getOriginalMap();
-
-    @SuppressWarnings("hiding")
-    protected abstract Map<ORIGINAL, TRANSLATED> getTranslatedMap(TranslationContext context, Collection<ORIGINAL> originalCollection,
-            FETCH_OPTIONS fetchOptions);
-
-}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationCache.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationCache.java
index 306e6df9798738a1a5e13525fad11cf88b981705..ae1309d33f976537ba246882041d792a9a3afe61 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationCache.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationCache.java
@@ -1,5 +1,6 @@
 package ch.ethz.sis.openbis.generic.server.api.v3.translator;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -24,6 +25,11 @@ public class TranslationCache
      */
     private Map<String, Object> translatedObjects = new HashMap<String, Object>();
 
+    /**
+     * Map storing already translated collections (value) for the given namespace, object ids (key) and fetchOptions
+     */
+    private Map<String, Map<Object, Object>> translatedCollections = new HashMap<String, Map<Object, Object>>();
+
     public boolean hasShouldTranslateObject(String namespace, Long objectId)
     {
         return shouldTranslateObjects.containsKey(getObjectKey(namespace, objectId));
@@ -39,6 +45,21 @@ public class TranslationCache
         shouldTranslateObjects.put(getObjectKey(namespace, objectId), shouldTranslate);
     }
 
+    public boolean hasTranslatedCollection(String namespace, Collection<Long> objectIds, Object fetchOptions)
+    {
+        return translatedCollections.containsKey(getObjectsKey(namespace, objectIds, fetchOptions));
+    }
+
+    public Map<Object, Object> getTranslatedCollection(String namespace, Collection<Long> objectIds, Object fetchOptions)
+    {
+        return translatedCollections.get(getObjectsKey(namespace, objectIds, fetchOptions));
+    }
+
+    public void putTranslatedCollection(String namespace, Collection<Long> objectIds, Object fetchOptions, Map<Object, Object> objects)
+    {
+        translatedCollections.put(getObjectsKey(namespace, objectIds, fetchOptions), objects);
+    }
+
     public boolean hasTranslatedObject(String namespace, Long objectId)
     {
         return translatedObjects.containsKey(getObjectKey(namespace, objectId));
@@ -72,4 +93,11 @@ public class TranslationCache
     {
         return namespace + "." + objectId;
     }
+
+    private String getObjectsKey(String namespace, Collection<Long> objectIds, Object fetchOptions)
+    {
+        // TODO compare fetch options
+        return namespace + "." + objectIds.toString();
+    }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationResults.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationResults.java
new file mode 100644
index 0000000000000000000000000000000000000000..88a338957c744c9e0150aa964d17f2c8e88d21ed
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/TranslationResults.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 ETH Zuerich, Scientific IT Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.api.v3.translator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectHolder;
+
+/**
+ * @author pkupczyk
+ */
+@SuppressWarnings({ "unchecked" })
+public class TranslationResults
+{
+
+    private Map<Class<?>, Object> resultMap = new HashMap<Class<?>, Object>();
+
+    public <I, O, F> void put(Class<? extends ITranslator<I, ObjectHolder<O>, F>> translatorClass, Map<I, ObjectHolder<O>> result)
+    {
+        resultMap.put(translatorClass, result);
+    }
+
+    public <I, O, F> O get(Class<? extends ITranslator<I, ObjectHolder<O>, F>> translatorClass, I input)
+    {
+        Map<I, ObjectHolder<O>> result = (Map<I, ObjectHolder<O>>) resultMap.get(translatorClass);
+        ObjectHolder<O> holder = result.get(input);
+        if (holder != null)
+        {
+            return holder.getObject();
+        } else
+        {
+            return null;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/attachment/AttachmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/attachment/AttachmentTranslator.java
index 6f449c963aae09150a0c3ad7ad67691f3fb3d401..581d34b9c49846b7162f70d5939580fcf43f0328 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/attachment/AttachmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/attachment/AttachmentTranslator.java
@@ -12,7 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.attachment.Attachment;
@@ -69,7 +68,7 @@ public class AttachmentTranslator extends AbstractCachingTranslator<AttachmentPE
     }
 
     @Override
-    protected void updateObject(TranslationContext context, AttachmentPE attachment, Attachment result, Relations relations,
+    protected void updateObject(TranslationContext context, AttachmentPE attachment, Attachment result, Object relations,
             AttachmentFetchOptions fetchOptions)
     {
         if (fetchOptions.hasRegistrator())
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectBaseRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectBaseRelation.java
deleted file mode 100644
index f8e9f5e170b312679e33cbf559602353e8f0bc00..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectBaseRelation.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2015 ETH Zuerich, CISD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql;
-
-import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relation;
-
-/**
- * @author pkupczyk
- */
-public abstract class ObjectBaseRelation<RECORD extends ObjectBaseRecord> implements Relation
-{
-
-    private Collection<Long> objectIds;
-
-    private Map<Long, RECORD> recordMap = new HashMap<Long, RECORD>();
-
-    public ObjectBaseRelation(Collection<Long> objectIds)
-    {
-        this.objectIds = objectIds;
-    }
-
-    @Override
-    public void load()
-    {
-        List<RECORD> records = load(new LongOpenHashSet(objectIds));
-
-        for (RECORD record : records)
-        {
-            recordMap.put(record.id, record);
-        }
-    }
-
-    protected abstract List<RECORD> load(@SuppressWarnings("hiding")
-    LongOpenHashSet objectIds);
-
-    public RECORD getRecord(Long objectId)
-    {
-        return recordMap.get(objectId);
-    }
-
-}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectBaseTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectBaseTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..b61d4751acc1ebf5729ecc4203f055fc015f0a34
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectBaseTranslator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql;
+
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class ObjectBaseTranslator<RECORD extends ObjectBaseRecord> extends
+        AbstractCachingTranslator<Long, ObjectHolder<RECORD>, Void>
+{
+
+    @Override
+    protected ObjectHolder<RECORD> createObject(TranslationContext context, Long input, Void fetchOptions)
+    {
+        return new ObjectHolder<RECORD>();
+    }
+
+    @Override
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> materialIds, Void fetchOptions)
+    {
+        List<RECORD> records = loadRecords(new LongOpenHashSet(materialIds));
+
+        Map<Long, RECORD> recordMap = new HashMap<Long, RECORD>();
+        for (RECORD record : records)
+        {
+            recordMap.put(record.id, record);
+        }
+
+        return recordMap;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void updateObject(TranslationContext context, Long materialId, ObjectHolder<RECORD> result, Object relations, Void fetchOptions)
+    {
+        Map<Long, RECORD> recordMap = (Map<Long, RECORD>) relations;
+        RECORD record = recordMap.get(materialId);
+        result.setObject(record);
+    }
+
+    protected abstract List<RECORD> loadRecords(LongOpenHashSet objectIds);
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectHolder.java
similarity index 51%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectHolder.java
index 2f9e88ed56f07d1fe0b35250cf61bfbb69e82a49..7fbe48d7d2f40483b73cc5394186eac966001738 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectHolder.java
@@ -14,40 +14,33 @@
  * limitations under the License.
  */
 
-package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.property;
-
-import java.util.Collection;
-import java.util.Map;
-
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relation;
+package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql;
 
 /**
  * @author pkupczyk
  */
-public abstract class PropertyRelation implements Relation
+public class ObjectHolder<T>
 {
 
-    private Collection<Long> entityIds;
-
-    private Map<Long, Map<String, String>> propertiesMap;
+    private T object;
 
-    public PropertyRelation(Collection<Long> entityIds)
+    public ObjectHolder()
     {
-        this.entityIds = entityIds;
     }
 
-    protected abstract Map<Long, Map<String, String>> loadProperties(@SuppressWarnings("hiding")
-    Collection<Long> entityIds);
+    public ObjectHolder(T object)
+    {
+        this.object = object;
+    }
 
-    @Override
-    public void load()
+    public T getObject()
     {
-        propertiesMap = loadProperties(entityIds);
+        return object;
     }
 
-    public Map<String, String> getProperties(Long entityId)
+    public void setObject(T object)
     {
-        return propertiesMap.get(entityId);
+        this.object = object;
     }
 
-}
\ No newline at end of file
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToManyRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToManyRelation.java
deleted file mode 100644
index 09af9eff6bd2509041deb93700e94cd90b8356e7..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToManyRelation.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2015 ETH Zuerich, CISD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql;
-
-import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relation;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
-
-/**
- * @author pkupczyk
- */
-public abstract class ObjectToManyRelation<RELATED_OBJECT, RELATED_FETCH_OPTIONS> implements Relation
-{
-
-    private TranslationContext context;
-
-    private Collection<Long> objectIds;
-
-    private RELATED_FETCH_OPTIONS relatedFetchOptions;
-
-    private Map<Long, Collection<RELATED_OBJECT>> objectIdToRelatedMap = new HashMap<Long, Collection<RELATED_OBJECT>>();
-
-    public ObjectToManyRelation(TranslationContext context, Collection<Long> objectIds, RELATED_FETCH_OPTIONS relatedFetchOptions)
-    {
-        this.context = context;
-        this.objectIds = objectIds;
-        this.relatedFetchOptions = relatedFetchOptions;
-    }
-
-    @Override
-    public void load()
-    {
-        List<ObjectRelationRecord> records = load(new LongOpenHashSet(objectIds));
-
-        Collection<Long> relatedIds = new HashSet<Long>();
-        for (ObjectRelationRecord record : records)
-        {
-            relatedIds.add(record.relatedId);
-        }
-
-        Map<Long, RELATED_OBJECT> relatedIdToRelated = translate(context, relatedIds, relatedFetchOptions);
-
-        for (ObjectRelationRecord record : records)
-        {
-            Collection<RELATED_OBJECT> relatedCollection = objectIdToRelatedMap.get(record.objectId);
-
-            if (relatedCollection == null)
-            {
-                relatedCollection = createCollection();
-                objectIdToRelatedMap.put(record.objectId, relatedCollection);
-            }
-
-            relatedCollection.add(relatedIdToRelated.get(record.relatedId));
-        }
-    }
-
-    @SuppressWarnings("hiding")
-    protected abstract List<ObjectRelationRecord> load(LongOpenHashSet objectIds);
-
-    @SuppressWarnings("hiding")
-    protected abstract Map<Long, RELATED_OBJECT> translate(TranslationContext context, Collection<Long> relatedIds,
-            RELATED_FETCH_OPTIONS relatedFetchOptions);
-
-    protected abstract Collection<RELATED_OBJECT> createCollection();
-
-    public List<RELATED_OBJECT> getRelatedList(Long objectId)
-    {
-        return (List<RELATED_OBJECT>) objectIdToRelatedMap.get(objectId);
-    }
-
-    public Set<RELATED_OBJECT> getRelatedSet(Long objectId)
-    {
-        return (Set<RELATED_OBJECT>) objectIdToRelatedMap.get(objectId);
-    }
-
-}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToManyRelationTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToManyRelationTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ace56084a42f5f7c3dc73f713197f478143b377
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToManyRelationTranslator.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql;
+
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class ObjectToManyRelationTranslator<RELATED_OBJECT, RELATED_FETCH_OPTIONS> extends
+        AbstractCachingTranslator<Long, ObjectHolder<Collection<RELATED_OBJECT>>, RELATED_FETCH_OPTIONS>
+{
+
+    @Override
+    protected ObjectHolder<Collection<RELATED_OBJECT>> createObject(TranslationContext context, Long objectId,
+            RELATED_FETCH_OPTIONS relatedFetchOptions)
+    {
+        return new ObjectHolder<Collection<RELATED_OBJECT>>();
+    }
+
+    @Override
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> objectIds, RELATED_FETCH_OPTIONS relatedFetchOptions)
+    {
+        List<ObjectRelationRecord> records = loadRecords(new LongOpenHashSet(objectIds));
+
+        Collection<Long> relatedIds = new HashSet<Long>();
+        for (ObjectRelationRecord record : records)
+        {
+            relatedIds.add(record.relatedId);
+        }
+
+        Map<Long, RELATED_OBJECT> relatedIdToRelated = translateRelated(context, relatedIds, relatedFetchOptions);
+        Map<Long, Collection<RELATED_OBJECT>> objectIdToRelatedMap = new HashMap<Long, Collection<RELATED_OBJECT>>();
+
+        for (ObjectRelationRecord record : records)
+        {
+            Collection<RELATED_OBJECT> relatedCollection = objectIdToRelatedMap.get(record.objectId);
+
+            if (relatedCollection == null)
+            {
+                relatedCollection = createCollection();
+                objectIdToRelatedMap.put(record.objectId, relatedCollection);
+            }
+
+            RELATED_OBJECT relatedObject = relatedIdToRelated.get(record.relatedId);
+            if (relatedObject != null)
+            {
+                relatedCollection.add(relatedObject);
+            }
+        }
+
+        for (Long objectId : objectIds)
+        {
+            if (false == objectIdToRelatedMap.containsKey(objectId))
+            {
+                objectIdToRelatedMap.put(objectId, createCollection());
+            }
+        }
+
+        return objectIdToRelatedMap;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void updateObject(TranslationContext context, Long objectId, ObjectHolder<Collection<RELATED_OBJECT>> result, Object relations,
+            RELATED_FETCH_OPTIONS fetchOptions)
+    {
+        Map<Long, Collection<RELATED_OBJECT>> objectIdToRelatedMap = (Map<Long, Collection<RELATED_OBJECT>>) relations;
+        Collection<RELATED_OBJECT> related = objectIdToRelatedMap.get(objectId);
+        result.setObject(related);
+    }
+
+    protected abstract List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds);
+
+    protected abstract Map<Long, RELATED_OBJECT> translateRelated(TranslationContext context, Collection<Long> relatedIds,
+            RELATED_FETCH_OPTIONS relatedFetchOptions);
+
+    protected abstract Collection<RELATED_OBJECT> createCollection();
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToOneRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToOneRelationTranslator.java
similarity index 50%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToOneRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToOneRelationTranslator.java
index 517b9ed31ebf8752e15589665b0e08511fadf897..58c0f69633092ae658cda95d48295735dbeb26aa 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToOneRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/common/sql/ObjectToOneRelationTranslator.java
@@ -24,34 +24,26 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 
 /**
  * @author pkupczyk
  */
-public abstract class ObjectToOneRelation<RELATED_OBJECT, RELATED_FETCH_OPTIONS> implements Relation
+public abstract class ObjectToOneRelationTranslator<RELATED_OBJECT, RELATED_FETCH_OPTIONS> extends
+        AbstractCachingTranslator<Long, ObjectHolder<RELATED_OBJECT>, RELATED_FETCH_OPTIONS>
 {
 
-    private TranslationContext context;
-
-    private Collection<Long> objectIds;
-
-    private RELATED_FETCH_OPTIONS relatedFetchOptions;
-
-    private Map<Long, RELATED_OBJECT> objectIdToRelatedMap = new HashMap<Long, RELATED_OBJECT>();
-
-    public ObjectToOneRelation(TranslationContext context, Collection<Long> objectIds, RELATED_FETCH_OPTIONS relatedFetchOptions)
+    @Override
+    protected ObjectHolder<RELATED_OBJECT> createObject(TranslationContext context, Long objectId, RELATED_FETCH_OPTIONS relatedFetchOptions)
     {
-        this.context = context;
-        this.objectIds = objectIds;
-        this.relatedFetchOptions = relatedFetchOptions;
+        return new ObjectHolder<RELATED_OBJECT>();
     }
 
     @Override
-    public void load()
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> objectIds, RELATED_FETCH_OPTIONS relatedFetchOptions)
     {
-        List<ObjectRelationRecord> records = load(new LongOpenHashSet(objectIds));
+        List<ObjectRelationRecord> records = loadRecords(new LongOpenHashSet(objectIds));
 
         Collection<Long> relatedIds = new HashSet<Long>();
         for (ObjectRelationRecord record : records)
@@ -59,24 +51,29 @@ public abstract class ObjectToOneRelation<RELATED_OBJECT, RELATED_FETCH_OPTIONS>
             relatedIds.add(record.relatedId);
         }
 
-        Map<Long, RELATED_OBJECT> relatedIdToRelated = translate(context, relatedIds, relatedFetchOptions);
+        Map<Long, RELATED_OBJECT> relatedIdToRelated = translateRelated(context, relatedIds, relatedFetchOptions);
 
+        Map<Long, RELATED_OBJECT> objectIdToRelatedMap = new HashMap<Long, RELATED_OBJECT>();
         for (ObjectRelationRecord record : records)
         {
             objectIdToRelatedMap.put(record.objectId, relatedIdToRelated.get(record.relatedId));
         }
-    }
-
-    @SuppressWarnings("hiding")
-    protected abstract List<ObjectRelationRecord> load(LongOpenHashSet objectIds);
 
-    @SuppressWarnings("hiding")
-    protected abstract Map<Long, RELATED_OBJECT> translate(TranslationContext context, Collection<Long> relatedIds,
-            RELATED_FETCH_OPTIONS relatedFetchOptions);
+        return objectIdToRelatedMap;
+    }
 
-    public RELATED_OBJECT getRelated(Long objectId)
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void updateObject(TranslationContext context, Long objectId, ObjectHolder<RELATED_OBJECT> result, Object relations,
+            RELATED_FETCH_OPTIONS relatedFetchOptions)
     {
-        return objectIdToRelatedMap.get(objectId);
+        Map<Long, RELATED_OBJECT> objectIdToRelatedMap = (Map<Long, RELATED_OBJECT>) relations;
+        result.setObject(objectIdToRelatedMap.get(objectId));
     }
 
+    protected abstract List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds);
+
+    protected abstract Map<Long, RELATED_OBJECT> translateRelated(TranslationContext context, Collection<Long> relatedIds,
+            RELATED_FETCH_OPTIONS relatedFetchOptions);
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTranslator.java
index 1b5111861673e3404a216dde824720e328b07c56..d7265dceb7c9b5e5362c8c885a75855728d37651 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTranslator.java
@@ -26,9 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset.sql.DataSetHistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset.sql.DataSetHistoryTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment.IExperimentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.IMaterialPropertyTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
@@ -75,6 +75,9 @@ public class DataSetTranslator extends AbstractCachingTranslator<DataPE, DataSet
     @Autowired
     private IExternalDataTranslator externalDataTranslator;
 
+    @Autowired
+    private DataSetHistoryTranslator historyTranslator;
+
     @Override
     protected boolean shouldTranslate(TranslationContext context, DataPE input, DataSetFetchOptions fetchOptions)
     {
@@ -97,9 +100,9 @@ public class DataSetTranslator extends AbstractCachingTranslator<DataPE, DataSet
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<DataPE> dataSets, DataSetFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<DataPE> dataSets, DataSetFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
         if (fetchOptions.hasHistory())
         {
@@ -108,15 +111,18 @@ public class DataSetTranslator extends AbstractCachingTranslator<DataPE, DataSet
             {
                 dataSetIds.add(dataSet.getId());
             }
-            relations.add(createRelation(DataSetHistoryRelation.class, context, dataSetIds, fetchOptions.withHistory()));
+            relations.put(DataSetHistoryTranslator.class, historyTranslator.translate(context, dataSetIds, fetchOptions.withHistory()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, DataPE dataPe, DataSet result, Relations relations, DataSetFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, DataPE dataPe, DataSet result, Object objectRelations,
+            DataSetFetchOptions fetchOptions)
     {
+        TranslationResults relations = (TranslationResults) objectRelations;
+
         if (fetchOptions.hasChildren())
         {
             Map<DataPE, DataSet> children = translate(context, dataPe.getChildren(), fetchOptions.withChildren());
@@ -211,8 +217,7 @@ public class DataSetTranslator extends AbstractCachingTranslator<DataPE, DataSet
 
         if (fetchOptions.hasHistory())
         {
-            DataSetHistoryRelation relation = relations.get(DataSetHistoryRelation.class);
-            result.setHistory(relation.getRelated(dataPe.getId()));
+            result.setHistory(relations.get(DataSetHistoryTranslator.class, dataPe.getId()));
             result.getFetchOptions().withHistoryUsing(fetchOptions.withHistory());
         }
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTypeTranslator.java
index a52d66f48276de7ece1cee152d99d95d7c85684d..49584b887a7f87cbea0237d6cc2cf091bb4965f1 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/DataSetTypeTranslator.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.dataset.DataSetKind;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.dataset.DataSetType;
@@ -49,7 +48,7 @@ public class DataSetTypeTranslator extends AbstractCachingTranslator<DataSetType
     }
 
     @Override
-    protected void updateObject(TranslationContext context, DataSetTypePE input, DataSetType output, Relations relations,
+    protected void updateObject(TranslationContext context, DataSetTypePE input, DataSetType output, Object relations,
             DataSetTypeFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/ExternalDataTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/ExternalDataTranslator.java
index 12e0c5e0aaa0f17390c5c5f6166924ba9cc2bd02..8731cc8f23987096a81ef9626fd1e92f8407ad8e 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/ExternalDataTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/ExternalDataTranslator.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.vocabulary.IVocabularyTermTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.dataset.ArchivingStatus;
@@ -113,7 +112,7 @@ public class ExternalDataTranslator extends AbstractCachingTranslator<ExternalDa
     }
 
     @Override
-    protected void updateObject(TranslationContext context, ExternalDataPE data, ExternalData result, Relations relations,
+    protected void updateObject(TranslationContext context, ExternalDataPE data, ExternalData result, Object relations,
             ExternalDataFetchOptions fetchOptions)
     {
         if (fetchOptions.hasFileFormatType())
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/FileFormatTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/FileFormatTypeTranslator.java
index 0c86db9020454973544ead9687307bb2502a1f8c..f7a912ee6a4f80a85dd42c0fcf84898509a5cd5b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/FileFormatTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/FileFormatTypeTranslator.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.dataset.FileFormatType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.dataset.FileFormatTypeFetchOptions;
@@ -45,7 +44,7 @@ public class FileFormatTypeTranslator extends AbstractCachingTranslator<FileForm
     }
 
     @Override
-    protected void updateObject(TranslationContext context, FileFormatTypePE type, FileFormatType result, Relations relations,
+    protected void updateObject(TranslationContext context, FileFormatTypePE type, FileFormatType result, Object relations,
             FileFormatTypeFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/LocatorTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/LocatorTypeTranslator.java
index f9d3330a9be8bdcf4579fe70279dcaa886e251e5..727f468652d2dd2677aeea7b0450a5b4675c3631 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/LocatorTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/LocatorTypeTranslator.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.dataset.LocatorType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.dataset.LocatorTypeFetchOptions;
@@ -46,7 +45,7 @@ public class LocatorTypeTranslator extends AbstractCachingTranslator<LocatorType
     }
 
     @Override
-    protected void updateObject(TranslationContext context, LocatorTypePE input, LocatorType output, Relations relations,
+    protected void updateObject(TranslationContext context, LocatorTypePE input, LocatorType output, Object relations,
             LocatorTypeFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/sql/DataSetHistoryRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/sql/DataSetHistoryTranslator.java
similarity index 87%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/sql/DataSetHistoryRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/sql/DataSetHistoryTranslator.java
index fd834f21cf1b7b2fcb2d89a4156d1f651740d35f..2b71f3d348d38894958452352eecad8feff171c4 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/sql/DataSetHistoryRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/dataset/sql/DataSetHistoryTranslator.java
@@ -25,13 +25,10 @@ import java.util.Map;
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryPropertyRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistorySqlTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelationshipRecord;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.DataSetRelationType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.RelationHistoryEntry;
@@ -47,18 +44,12 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.RelationType;
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class DataSetHistoryRelation extends HistoryRelation
+public class DataSetHistoryTranslator extends HistorySqlTranslator
 {
 
     @Autowired
     private IDAOFactory daoFactory;
 
-    public DataSetHistoryRelation(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
-    {
-        super(context, entityIds, fetchOptions);
-    }
-
     @Override
     protected List<HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds)
     {
@@ -74,9 +65,10 @@ public class DataSetHistoryRelation extends HistoryRelation
     }
 
     @Override
-    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap)
+    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
-        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap);
+        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap, fetchOptions);
 
         DataSetRelationshipRecord dataSetRecord = (DataSetRelationshipRecord) record;
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/deletion/DeletionTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/deletion/DeletionTranslator.java
index 215d6a59e6141792cc770d6c6e53d7f264aad2a2..059e453cdf8dcaede8eedaf15df2e9c314c84e40 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/deletion/DeletionTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/deletion/DeletionTranslator.java
@@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.deletion.DeletedObject;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.deletion.Deletion;
@@ -72,7 +71,7 @@ public class DeletionTranslator extends
 
     @Override
     protected void updateObject(TranslationContext context, ch.systemsx.cisd.openbis.generic.shared.basic.dto.Deletion input, Deletion output,
-            Relations relations, DeletionFetchOptions fetchOptions)
+            Object relations, DeletionFetchOptions fetchOptions)
     {
         if (fetchOptions.hasDeletedObjects())
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTranslator.java
index f28a568d467a2bedae64483607415f9c5f6c0946..23afddaaf9316edcff6dd47e1eed913b03d26f63 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTranslator.java
@@ -27,11 +27,11 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.attachment.IAttachmentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset.IDataSetTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment.sql.ExperimentHistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment.sql.ExperimentHistoryTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.IMaterialPropertyTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.project.IProjectTranslator;
@@ -87,6 +87,9 @@ public class ExperimentTranslator extends AbstractCachingTranslator<ExperimentPE
     @Autowired
     private ITagTranslator tagTranslator;
 
+    @Autowired
+    private ExperimentHistoryTranslator historyTranslator;
+
     @Override
     protected boolean shouldTranslate(TranslationContext context, ExperimentPE input, ExperimentFetchOptions fetchOptions)
     {
@@ -109,9 +112,10 @@ public class ExperimentTranslator extends AbstractCachingTranslator<ExperimentPE
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<ExperimentPE> experiments, ExperimentFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<ExperimentPE> experiments,
+            ExperimentFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
         if (fetchOptions.hasHistory())
         {
@@ -120,16 +124,18 @@ public class ExperimentTranslator extends AbstractCachingTranslator<ExperimentPE
             {
                 experimentIds.add(experiment.getId());
             }
-            relations.add(createRelation(ExperimentHistoryRelation.class, context, experimentIds, fetchOptions.withHistory()));
+            relations.put(ExperimentHistoryTranslator.class, historyTranslator.translate(context, experimentIds, fetchOptions.withHistory()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, ExperimentPE experiment, Experiment result, Relations relations,
+    protected void updateObject(TranslationContext context, ExperimentPE experiment, Experiment result, Object objectRelations,
             ExperimentFetchOptions fetchOptions)
     {
+        TranslationResults relations = (TranslationResults) objectRelations;
+
         if (fetchOptions.hasType())
         {
             result.setType(typeTranslator.translate(context, experiment.getExperimentType(), fetchOptions.withType()));
@@ -196,8 +202,7 @@ public class ExperimentTranslator extends AbstractCachingTranslator<ExperimentPE
 
         if (fetchOptions.hasHistory())
         {
-            ExperimentHistoryRelation relation = relations.get(ExperimentHistoryRelation.class);
-            result.setHistory(relation.getRelated(experiment.getId()));
+            result.setHistory(relations.get(ExperimentHistoryTranslator.class, experiment.getId()));
             result.getFetchOptions().withHistoryUsing(fetchOptions.withHistory());
         }
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTypeTranslator.java
index dbf5587149ecfe53f0afae28efb39439e7de47ee..c41f05799294276d40538e27b217da7f851a5cec 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/ExperimentTypeTranslator.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.ExperimentType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.experiment.ExperimentTypeFetchOptions;
@@ -47,7 +46,7 @@ public class ExperimentTypeTranslator extends AbstractCachingTranslator<Experime
     }
 
     @Override
-    protected void updateObject(TranslationContext context, ExperimentTypePE input, ExperimentType output, Relations relations,
+    protected void updateObject(TranslationContext context, ExperimentTypePE input, ExperimentType output, Object relations,
             ExperimentTypeFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/sql/ExperimentHistoryRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/sql/ExperimentHistoryTranslator.java
similarity index 84%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/sql/ExperimentHistoryRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/sql/ExperimentHistoryTranslator.java
index 5b1782412b23d181d5e34ecff288e25d6ff97c80..739fcf2b6c2805706c3bcfa6bd0448f691a9a6e3 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/sql/ExperimentHistoryRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/experiment/sql/ExperimentHistoryTranslator.java
@@ -24,13 +24,10 @@ import java.util.Map;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryPropertyRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistorySqlTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelationshipRecord;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.ExperimentRelationType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.RelationHistoryEntry;
@@ -44,15 +41,9 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.sample.SamplePermId;
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class ExperimentHistoryRelation extends HistoryRelation
+public class ExperimentHistoryTranslator extends HistorySqlTranslator
 {
 
-    public ExperimentHistoryRelation(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
-    {
-        super(context, entityIds, fetchOptions);
-    }
-
     @Override
     protected List<HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds)
     {
@@ -68,9 +59,10 @@ public class ExperimentHistoryRelation extends HistoryRelation
     }
 
     @Override
-    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap)
+    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
-        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap);
+        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap, fetchOptions);
 
         ExperimentRelationshipRecord experimentRecord = (ExperimentRelationshipRecord) record;
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/HistoryTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/HistoryTranslator.java
index 539b5f92eb82eb9d891ffd8af773e0645908d4c0..54c9d57b19a6c440e051a47d085c4a90208752bc 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/HistoryTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/HistoryTranslator.java
@@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.DataSetRelationType;
@@ -231,7 +230,7 @@ public class HistoryTranslator extends AbstractCachingTranslator<IEntityInformat
     }
 
     @Override
-    protected void updateObject(TranslationContext context, IEntityInformationHolderDTO entity, List<HistoryEntry> entries, Relations relations,
+    protected void updateObject(TranslationContext context, IEntityInformationHolderDTO entity, List<HistoryEntry> entries, Object relations,
             HistoryEntryFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/sql/HistoryRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/sql/HistorySqlTranslator.java
similarity index 70%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/sql/HistoryRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/sql/HistorySqlTranslator.java
index bf3462f29c7265a206bbeeddf36f084535ff8af9..9ce42513e8c8c10271993e6ddf15a4734f353a02 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/sql/HistoryRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/history/sql/HistorySqlTranslator.java
@@ -27,8 +27,9 @@ import java.util.Set;
 
 import org.springframework.beans.factory.annotation.Autowired;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectHolder;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.sql.IPersonSqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.HistoryEntry;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.PropertyHistoryEntry;
@@ -39,35 +40,24 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.history.Histor
 /**
  * @author pkupczyk
  */
-public abstract class HistoryRelation implements Relation
+public abstract class HistorySqlTranslator extends AbstractCachingTranslator<Long, ObjectHolder<List<HistoryEntry>>, HistoryEntryFetchOptions>
 {
 
     @Autowired
     private IPersonSqlTranslator personTranslator;
 
-    private TranslationContext context;
-
-    private Collection<Long> entityIds;
-
-    private HistoryEntryFetchOptions fetchOptions;
+    protected abstract List<? extends HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds);
 
-    private Map<Long, List<HistoryEntry>> entriesMap = new HashMap<Long, List<HistoryEntry>>();
+    protected abstract List<? extends HistoryRelationshipRecord> loadRelationshipHistory(Collection<Long> entityIds);
 
-    public HistoryRelation(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
+    @Override
+    protected ObjectHolder<List<HistoryEntry>> createObject(TranslationContext context, Long entityId, HistoryEntryFetchOptions fetchOptions)
     {
-        this.context = context;
-        this.entityIds = entityIds;
-        this.fetchOptions = fetchOptions;
+        return new ObjectHolder<List<HistoryEntry>>();
     }
 
-    @SuppressWarnings("hiding")
-    protected abstract List<? extends HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds);
-
-    @SuppressWarnings("hiding")
-    protected abstract List<? extends HistoryRelationshipRecord> loadRelationshipHistory(Collection<Long> entityIds);
-
     @Override
-    public void load()
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
     {
         List<? extends HistoryPropertyRecord> properties = loadPropertyHistory(entityIds);
         List<? extends HistoryRelationshipRecord> relationships = loadRelationshipHistory(entityIds);
@@ -100,17 +90,40 @@ public abstract class HistoryRelation implements Relation
             authorMap = personTranslator.translate(context, authorIds, fetchOptions.withAuthor());
         }
 
+        Map<Long, List<HistoryEntry>> entriesMap = new HashMap<Long, List<HistoryEntry>>();
+
         if (properties != null)
         {
-            createPropertyEntries(properties, authorMap);
+            createPropertyEntries(entriesMap, properties, authorMap, fetchOptions);
         }
         if (relationships != null)
         {
-            createRelationshipEntries(relationships, authorMap);
+            createRelationshipEntries(entriesMap, relationships, authorMap, fetchOptions);
         }
+
+        for (Long entityId : entityIds)
+        {
+            if (false == entriesMap.containsKey(entityId))
+            {
+                entriesMap.put(entityId, Collections.<HistoryEntry> emptyList());
+            }
+        }
+
+        return entriesMap;
     }
 
-    private void createPropertyEntries(List<? extends HistoryPropertyRecord> records, Map<Long, Person> authorMap)
+    @Override
+    @SuppressWarnings("unchecked")
+    protected void updateObject(TranslationContext context, Long entityId, ObjectHolder<List<HistoryEntry>> result, Object relations,
+            HistoryEntryFetchOptions fetchOptions)
+    {
+        Map<Long, List<HistoryEntry>> entriesMap = (Map<Long, List<HistoryEntry>>) relations;
+        result.setObject(entriesMap.get(entityId));
+    }
+
+    private void createPropertyEntries(Map<Long, List<HistoryEntry>> entriesMap, List<? extends HistoryPropertyRecord> records,
+            Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
         for (HistoryPropertyRecord record : records)
         {
@@ -122,11 +135,12 @@ public abstract class HistoryRelation implements Relation
                 entriesMap.put(record.entityId, entries);
             }
 
-            entries.add(createPropertyEntry(record, authorMap));
+            entries.add(createPropertyEntry(record, authorMap, fetchOptions));
         }
     }
 
-    protected PropertyHistoryEntry createPropertyEntry(HistoryPropertyRecord record, Map<Long, Person> authorMap)
+    protected PropertyHistoryEntry createPropertyEntry(HistoryPropertyRecord record, Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
         PropertyHistoryEntry entry = new PropertyHistoryEntry();
         entry.setFetchOptions(new HistoryEntryFetchOptions());
@@ -156,7 +170,8 @@ public abstract class HistoryRelation implements Relation
         return entry;
     }
 
-    private void createRelationshipEntries(List<? extends HistoryRelationshipRecord> records, Map<Long, Person> authorMap)
+    private void createRelationshipEntries(Map<Long, List<HistoryEntry>> entriesMap, List<? extends HistoryRelationshipRecord> records,
+            Map<Long, Person> authorMap, HistoryEntryFetchOptions fetchOptions)
     {
         for (HistoryRelationshipRecord record : records)
         {
@@ -168,11 +183,12 @@ public abstract class HistoryRelation implements Relation
                 entriesMap.put(record.entityId, entries);
             }
 
-            entries.add(createRelationshipEntry(record, authorMap));
+            entries.add(createRelationshipEntry(record, authorMap, fetchOptions));
         }
     }
 
-    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap)
+    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
         RelationHistoryEntry entry = new RelationHistoryEntry();
         entry.setFetchOptions(new HistoryEntryFetchOptions());
@@ -188,15 +204,4 @@ public abstract class HistoryRelation implements Relation
         return entry;
     }
 
-    public List<HistoryEntry> getRelated(Long entityId)
-    {
-        if (entriesMap.containsKey(entityId))
-        {
-            return entriesMap.get(entityId);
-        } else
-        {
-            return Collections.emptyList();
-        }
-    }
-
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialPropertyTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialPropertyTranslator.java
index e7882107f0db8173dd52e1eb9cd202f6eef7e7c3..8638eded402ba586d9cde6a16bf6f684834aa73c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialPropertyTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialPropertyTranslator.java
@@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialFetchOptions;
@@ -68,7 +67,7 @@ public class MaterialPropertyTranslator extends
     }
 
     @Override
-    protected void updateObject(TranslationContext context, IEntityPropertiesHolder input, Map<String, Material> output, Relations relations,
+    protected void updateObject(TranslationContext context, IEntityPropertiesHolder input, Map<String, Material> output, Object relations,
             MaterialFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTranslator.java
index a53cdbf4d73c86685bf3a4b2bc0174a96ea3cf8f..461679ec4efe52b0bd18693b5d13a425e0ce3740 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTranslator.java
@@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.IHistoryTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
@@ -74,7 +73,7 @@ public class MaterialTranslator extends AbstractCachingTranslator<MaterialPE, Ma
     }
 
     @Override
-    protected void updateObject(TranslationContext context, MaterialPE materialPe, Material result, Relations relations,
+    protected void updateObject(TranslationContext context, MaterialPE materialPe, Material result, Object relations,
             MaterialFetchOptions fetchOptions)
     {
         if (fetchOptions.hasProperties())
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTypeTranslator.java
index d022fa8964e7aa45ebba3aeb2527ae4be72e028c..132d61669ca694ae241309cc8a9844bafaa6b930 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/MaterialTypeTranslator.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialTypeFetchOptions;
@@ -46,7 +45,7 @@ public class MaterialTypeTranslator extends AbstractCachingTranslator<MaterialTy
     }
 
     @Override
-    protected void updateObject(TranslationContext context, MaterialTypePE input, MaterialType output, Relations relations,
+    protected void updateObject(TranslationContext context, MaterialTypePE input, MaterialType output, Object relations,
             MaterialTypeFetchOptions fetchOptions)
     {
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialBaseRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialBaseTranslator.java
similarity index 69%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialBaseRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialBaseTranslator.java
index ef3a963b018ca9cfa15bcd3edbb875318d30c968..320ddfd720d94b9307e66b5b4a93b18c75eda1d0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialBaseRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialBaseTranslator.java
@@ -18,34 +18,26 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.sql
 
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 
-import java.util.Collection;
 import java.util.List;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseTranslator;
 
 /**
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialBaseRelation extends ObjectBaseRelation<MaterialBaseRecord>
+public class MaterialBaseTranslator extends ObjectBaseTranslator<MaterialBaseRecord>
 {
 
-    public MaterialBaseRelation(Collection<Long> objectIds)
-    {
-        super(objectIds);
-    }
-
     @Override
-    protected List<MaterialBaseRecord> load(LongOpenHashSet objectIds)
+    protected List<MaterialBaseRecord> loadRecords(LongOpenHashSet objectIds)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
         return query.getMaterials(new LongOpenHashSet(objectIds));
     }
-}
+
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialHistoryRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialHistoryTranslator.java
similarity index 71%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialHistoryRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialHistoryTranslator.java
index 2d9c99e8078c4e577ae17ac65dea0c623f28c159..d7b4a83da2212d51d82b1d8e2e90043da3e481ef 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialHistoryRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialHistoryTranslator.java
@@ -23,29 +23,19 @@ import java.util.List;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryPropertyRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistorySqlTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelationshipRecord;
-import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.history.HistoryEntryFetchOptions;
 
 /**
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialHistoryRelation extends HistoryRelation
+public class MaterialHistoryTranslator extends HistorySqlTranslator
 {
 
-    public MaterialHistoryRelation(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
-    {
-        super(context, entityIds, fetchOptions);
-    }
-
     @Override
     protected List<HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialMaterialPropertyRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialMaterialPropertyTranslator.java
similarity index 65%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialMaterialPropertyRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialMaterialPropertyTranslator.java
index 8d0eef5e842ec000d55d0546343dbb71f8bb5627..71fadd0d81fc521394d64543b8d1d03b60658d65 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialMaterialPropertyRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialMaterialPropertyTranslator.java
@@ -27,12 +27,11 @@ import java.util.Map;
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectHolder;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialFetchOptions;
 
@@ -40,33 +39,23 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.Mater
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialMaterialPropertyRelation implements Relation
+public class MaterialMaterialPropertyTranslator extends AbstractCachingTranslator<Long, ObjectHolder<Map<String, Material>>, MaterialFetchOptions>
 {
 
     @Autowired
     private IMaterialSqlTranslator materialTranslator;
 
-    private TranslationContext context;
-
-    private Collection<Long> materialIds;
-
-    private MaterialFetchOptions fetchOptions;
-
-    private Map<Long, Map<String, Material>> materialProperties = new HashMap<Long, Map<String, Material>>();
-
-    public MaterialMaterialPropertyRelation(TranslationContext context, Collection<Long> materialIds, MaterialFetchOptions fetchOptions)
+    @Override
+    protected ObjectHolder<Map<String, Material>> createObject(TranslationContext context, Long objectId, MaterialFetchOptions fetchOptions)
     {
-        this.context = context;
-        this.materialIds = materialIds;
-        this.fetchOptions = fetchOptions;
+        return new ObjectHolder<Map<String, Material>>();
     }
 
     @Override
-    public void load()
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> objectIds, MaterialFetchOptions fetchOptions)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
-        List<MaterialMaterialPropertyRecord> records = query.getMaterialProperties(new LongOpenHashSet(materialIds));
+        List<MaterialMaterialPropertyRecord> records = query.getMaterialProperties(new LongOpenHashSet(objectIds));
         Collection<Long> propertyValues = new HashSet<Long>();
 
         for (MaterialMaterialPropertyRecord record : records)
@@ -75,6 +64,7 @@ public class MaterialMaterialPropertyRelation implements Relation
         }
 
         Map<Long, Material> materials = materialTranslator.translate(context, propertyValues, fetchOptions);
+        Map<Long, Map<String, Material>> materialProperties = new HashMap<Long, Map<String, Material>>();
 
         for (MaterialMaterialPropertyRecord record : records)
         {
@@ -86,11 +76,17 @@ public class MaterialMaterialPropertyRelation implements Relation
             }
             properties.put(record.propertyCode, materials.get(record.propertyValue));
         }
+
+        return materialProperties;
     }
 
-    public Map<String, Material> getMaterialProperties(Long materialId)
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void updateObject(TranslationContext context, Long objectId, ObjectHolder<Map<String, Material>> result, Object relations,
+            MaterialFetchOptions fetchOptions)
     {
-        return materialProperties.get(materialId);
+        Map<Long, Map<String, Material>> materialProperties = (Map<Long, Map<String, Material>>) relations;
+        result.setObject(materialProperties.get(objectId));
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialPropertyRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialPropertySqlTranslator.java
similarity index 87%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialPropertyRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialPropertySqlTranslator.java
index 6f40f462d23cf253c3044466128358259dadbb99..8169f8c8f5f3372efac15f57c47c7f8ab0ed1bb9 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialPropertyRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialPropertySqlTranslator.java
@@ -23,27 +23,19 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Component;
-
 import net.lemnik.eodsql.QueryTool;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.property.PropertyRelation;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.property.sql.PropertySqlTranslator;
 
 /**
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialPropertyRelation extends PropertyRelation
+public class MaterialPropertySqlTranslator extends PropertySqlTranslator
 {
 
-    public MaterialPropertyRelation(Collection<Long> entityIds)
-    {
-        super(entityIds);
-    }
-
     @Override
     protected Map<Long, Map<String, String>> loadProperties(Collection<Long> entityIds)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialRegistratorRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialRegistratorTranslator.java
similarity index 71%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialRegistratorRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialRegistratorTranslator.java
index bfa9eabf8608825096d845ee065af4f0dbecee0a..36f829bde7eee7acbc3824d21ffed30fe060858c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialRegistratorRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialRegistratorTranslator.java
@@ -25,13 +25,11 @@ import java.util.Map;
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelationTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.sql.IPersonSqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonFetchOptions;
@@ -40,27 +38,21 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonF
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialRegistratorRelation extends ObjectToOneRelation<Person, PersonFetchOptions>
+public class MaterialRegistratorTranslator extends ObjectToOneRelationTranslator<Person, PersonFetchOptions>
 {
 
     @Autowired
     private IPersonSqlTranslator personTranslator;
 
-    public MaterialRegistratorRelation(TranslationContext context, Collection<Long> objectIds, PersonFetchOptions relatedFetchOptions)
-    {
-        super(context, objectIds, relatedFetchOptions);
-    }
-
     @Override
-    protected List<ObjectRelationRecord> load(LongOpenHashSet objectIds)
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
         return query.getRegistratorIds(objectIds);
     }
 
     @Override
-    protected Map<Long, Person> translate(TranslationContext context, Collection<Long> relatedIds, PersonFetchOptions relatedFetchOptions)
+    protected Map<Long, Person> translateRelated(TranslationContext context, Collection<Long> relatedIds, PersonFetchOptions relatedFetchOptions)
     {
         return personTranslator.translate(context, relatedIds, relatedFetchOptions);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialSqlTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialSqlTranslator.java
index 50d0ef150ff791f8534002b8ca6b42ad8563d100..26eb3a2c44ebf5a39c968306243caf79e6a4b261 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialSqlTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialSqlTranslator.java
@@ -17,14 +17,17 @@
 package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.sql;
 
 import java.util.Collection;
+import java.util.Comparator;
+import java.util.Set;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.property.PropertyRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.Material;
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.tag.Tag;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.material.MaterialPermId;
 
@@ -35,6 +38,27 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.material.MaterialPermId;
 public class MaterialSqlTranslator extends AbstractCachingTranslator<Long, Material, MaterialFetchOptions> implements IMaterialSqlTranslator
 {
 
+    @Autowired
+    private MaterialBaseTranslator baseTranslator;
+
+    @Autowired
+    private MaterialTypeRelationTranslator typeTranslator;
+
+    @Autowired
+    private MaterialPropertySqlTranslator propertyTranslator;
+
+    @Autowired
+    private MaterialMaterialPropertyTranslator materialPropertyTranslator;
+
+    @Autowired
+    private MaterialRegistratorTranslator registratorTranslator;
+
+    @Autowired
+    private MaterialTagTranslator tagsTranslator;
+
+    @Autowired
+    private MaterialHistoryTranslator historyTranslator;
+
     @Override
     protected Material createObject(TranslationContext context, Long materialId, MaterialFetchOptions fetchOptions)
     {
@@ -44,51 +68,53 @@ public class MaterialSqlTranslator extends AbstractCachingTranslator<Long, Mater
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<Long> materialIds, MaterialFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<Long> materialIds, MaterialFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
-        relations.add(createRelation(MaterialBaseRelation.class, materialIds));
+        relations.put(MaterialBaseTranslator.class, baseTranslator.translate(context, materialIds, null));
 
         if (fetchOptions.hasType())
         {
-            relations.add(createRelation(MaterialTypeRelation.class, context, materialIds, fetchOptions.withType()));
+            relations.put(MaterialTypeRelationTranslator.class, typeTranslator.translate(context, materialIds, fetchOptions.withType()));
         }
 
         if (fetchOptions.hasProperties())
         {
-            relations.add(createRelation(MaterialPropertyRelation.class, materialIds));
+            relations.put(MaterialPropertySqlTranslator.class, propertyTranslator.translate(context, materialIds, fetchOptions.withProperties()));
         }
 
         if (fetchOptions.hasMaterialProperties())
         {
-            relations.add(createRelation(MaterialMaterialPropertyRelation.class, context, materialIds, fetchOptions.withMaterialProperties()));
+            relations.put(MaterialMaterialPropertyTranslator.class,
+                    materialPropertyTranslator.translate(context, materialIds, fetchOptions.withMaterialProperties()));
         }
 
         if (fetchOptions.hasRegistrator())
         {
-            relations.add(createRelation(MaterialRegistratorRelation.class, context, materialIds, fetchOptions.withRegistrator()));
+            relations.put(MaterialRegistratorTranslator.class,
+                    registratorTranslator.translate(context, materialIds, fetchOptions.withRegistrator()));
         }
 
         if (fetchOptions.hasTags())
         {
-            relations.add(createRelation(MaterialTagsRelation.class, context, materialIds, fetchOptions.withTags()));
+            relations.put(MaterialTagTranslator.class, tagsTranslator.translate(context, materialIds, fetchOptions.withTags()));
         }
 
         if (fetchOptions.hasHistory())
         {
-            relations.add(createRelation(MaterialHistoryRelation.class, context, materialIds, fetchOptions.withHistory()));
+            relations.put(MaterialHistoryTranslator.class, historyTranslator.translate(context, materialIds, fetchOptions.withHistory()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, Long materialId, Material result, Relations relations,
+    protected void updateObject(TranslationContext context, Long materialId, Material result, Object objectRelations,
             MaterialFetchOptions fetchOptions)
     {
-        MaterialBaseRelation baseRelation = relations.get(MaterialBaseRelation.class);
-        MaterialBaseRecord baseRecord = baseRelation.getRecord(materialId);
+        TranslationResults relations = (TranslationResults) objectRelations;
+        MaterialBaseRecord baseRecord = relations.get(MaterialBaseTranslator.class, materialId);
 
         result.setPermId(new MaterialPermId(baseRecord.code, baseRecord.typeCode));
         result.setCode(baseRecord.code);
@@ -97,45 +123,81 @@ public class MaterialSqlTranslator extends AbstractCachingTranslator<Long, Mater
 
         if (fetchOptions.hasType())
         {
-            MaterialTypeRelation relation = relations.get(MaterialTypeRelation.class);
-            result.setType(relation.getRelated(materialId));
+            result.setType(relations.get(MaterialTypeRelationTranslator.class, materialId));
             result.getFetchOptions().withTypeUsing(fetchOptions.withType());
         }
 
         if (fetchOptions.hasProperties())
         {
-            PropertyRelation relation = relations.get(MaterialPropertyRelation.class);
-            result.setProperties(relation.getProperties(materialId));
+            result.setProperties(relations.get(MaterialPropertySqlTranslator.class, materialId));
             result.getFetchOptions().withPropertiesUsing(fetchOptions.withProperties());
         }
 
         if (fetchOptions.hasMaterialProperties())
         {
-            MaterialMaterialPropertyRelation relation = relations.get(MaterialMaterialPropertyRelation.class);
-            result.setMaterialProperties(relation.getMaterialProperties(materialId));
+            result.setMaterialProperties(relations.get(MaterialMaterialPropertyTranslator.class, materialId));
             result.getFetchOptions().withMaterialPropertiesUsing(fetchOptions.withMaterialProperties());
         }
 
         if (fetchOptions.hasRegistrator())
         {
-            MaterialRegistratorRelation relation = relations.get(MaterialRegistratorRelation.class);
-            result.setRegistrator(relation.getRelated(materialId));
+            result.setRegistrator(relations.get(MaterialRegistratorTranslator.class, materialId));
             result.getFetchOptions().withRegistratorUsing(fetchOptions.withRegistrator());
         }
 
         if (fetchOptions.hasTags())
         {
-            MaterialTagsRelation relation = relations.get(MaterialTagsRelation.class);
-            result.setTags(relation.getRelatedSet(materialId));
+            result.setTags((Set<Tag>) relations.get(MaterialTagTranslator.class, materialId));
             result.getFetchOptions().withTagsUsing(fetchOptions.withTags());
         }
 
         if (fetchOptions.hasHistory())
         {
-            MaterialHistoryRelation relation = relations.get(MaterialHistoryRelation.class);
-            result.setHistory(relation.getRelated(materialId));
+            result.setHistory(relations.get(MaterialHistoryTranslator.class, materialId));
             result.getFetchOptions().withHistoryUsing(fetchOptions.withHistory());
         }
 
     }
+
+    @Override
+    protected Comparator<Material> getObjectComparator(TranslationContext context, final MaterialFetchOptions fetchOptions)
+    {
+        if (fetchOptions.getSortBy() == null)
+        {
+            return null;
+        }
+
+        if (fetchOptions.getSortBy().isCode())
+        {
+            return new Comparator<Material>()
+                {
+                    @Override
+                    public int compare(Material o1, Material o2)
+                    {
+                        if (fetchOptions.getSortBy().code().isAsc())
+                        {
+                            return o1.getCode().compareTo(o2.getCode());
+                        } else
+                        {
+                            return -o1.getCode().compareTo(o2.getCode());
+                        }
+                    }
+                };
+        }
+
+        if (fetchOptions.getSortBy().isRegistrationDate())
+        {
+            return new Comparator<Material>()
+                {
+                    @Override
+                    public int compare(Material o1, Material o2)
+                    {
+                        return -o1.getRegistrationDate().compareTo(o2.getRegistrationDate());
+                    }
+                };
+        }
+
+        return null;
+    }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTagsRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTagTranslator.java
similarity index 71%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTagsRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTagTranslator.java
index 469ce544379aa51db3283fea8917d66423d67557..c3cf3909e38b18a27d0ef4f947c63c95ddc8d909 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTagsRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTagTranslator.java
@@ -19,20 +19,18 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.sql
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToManyRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToManyRelationTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.tag.sql.ITagSqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.tag.Tag;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.tag.TagFetchOptions;
@@ -41,27 +39,21 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.tag.TagFetchOp
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialTagsRelation extends ObjectToManyRelation<Tag, TagFetchOptions>
+public class MaterialTagTranslator extends ObjectToManyRelationTranslator<Tag, TagFetchOptions>
 {
 
     @Autowired
     private ITagSqlTranslator tagTranslator;
 
-    public MaterialTagsRelation(TranslationContext context, Collection<Long> objectIds, TagFetchOptions relatedFetchOptions)
-    {
-        super(context, objectIds, relatedFetchOptions);
-    }
-
     @Override
-    protected List<ObjectRelationRecord> load(LongOpenHashSet objectIds)
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
         return query.getTagIds(objectIds);
     }
 
     @Override
-    protected Map<Long, Tag> translate(TranslationContext context, Collection<Long> relatedIds, TagFetchOptions relatedFetchOptions)
+    protected Map<Long, Tag> translateRelated(TranslationContext context, Collection<Long> relatedIds, TagFetchOptions relatedFetchOptions)
     {
         return tagTranslator.translate(context, relatedIds, relatedFetchOptions);
     }
@@ -69,7 +61,7 @@ public class MaterialTagsRelation extends ObjectToManyRelation<Tag, TagFetchOpti
     @Override
     protected Collection<Tag> createCollection()
     {
-        return new HashSet<Tag>();
+        return new LinkedHashSet<Tag>();
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeBaseRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeBaseTranslator.java
similarity index 68%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeBaseRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeBaseTranslator.java
index f444f22651b101c77ff3cbbf0130cae1a3094739..4c88b2a5cca876d7387ab19e0f215019c0df5785 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeBaseRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeBaseTranslator.java
@@ -18,32 +18,23 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.sql
 
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 
-import java.util.Collection;
 import java.util.List;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseTranslator;
 
 /**
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialTypeBaseRelation extends ObjectBaseRelation<MaterialTypeBaseRecord>
+public class MaterialTypeBaseTranslator extends ObjectBaseTranslator<MaterialTypeBaseRecord>
 {
 
-    public MaterialTypeBaseRelation(Collection<Long> objectIds)
-    {
-        super(objectIds);
-    }
-
     @Override
-    protected List<MaterialTypeBaseRecord> load(LongOpenHashSet objectIds)
+    protected List<MaterialTypeBaseRecord> loadRecords(LongOpenHashSet objectIds)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
         return query.getTypes(new LongOpenHashSet(objectIds));
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeRelationTranslator.java
similarity index 70%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeRelationTranslator.java
index 0c9faee4ed6223cec56c55ae515084e79a822ecf..03f82ebd87b43a25beaa953390a525a36756ec64 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeRelationTranslator.java
@@ -25,13 +25,11 @@ import java.util.Map;
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelationTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialTypeFetchOptions;
 
@@ -39,27 +37,22 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.Mater
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class MaterialTypeRelation extends ObjectToOneRelation<MaterialType, MaterialTypeFetchOptions>
+public class MaterialTypeRelationTranslator extends ObjectToOneRelationTranslator<MaterialType, MaterialTypeFetchOptions>
 {
 
     @Autowired
     private IMaterialTypeSqlTranslator typeTranslator;
 
-    public MaterialTypeRelation(TranslationContext context, Collection<Long> objectIds, MaterialTypeFetchOptions relatedFetchOptions)
-    {
-        super(context, objectIds, relatedFetchOptions);
-    }
-
     @Override
-    protected List<ObjectRelationRecord> load(LongOpenHashSet objectIds)
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
         return query.getTypeIds(objectIds);
     }
 
     @Override
-    protected Map<Long, MaterialType> translate(TranslationContext context, Collection<Long> relatedIds, MaterialTypeFetchOptions relatedFetchOptions)
+    protected Map<Long, MaterialType> translateRelated(TranslationContext context, Collection<Long> relatedIds,
+            MaterialTypeFetchOptions relatedFetchOptions)
     {
         return typeTranslator.translate(context, relatedIds, relatedFetchOptions);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeSqlTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeSqlTranslator.java
index 7968fdf03ad4f106a89944b6938fa38c2056edfa..ac0c08a3c8326552e361ea59997100ed1244b46d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeSqlTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/material/sql/MaterialTypeSqlTranslator.java
@@ -18,11 +18,12 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.sql
 
 import java.util.Collection;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.material.MaterialType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.entitytype.EntityTypePermId;
@@ -35,6 +36,9 @@ public class MaterialTypeSqlTranslator extends AbstractCachingTranslator<Long, M
         IMaterialTypeSqlTranslator
 {
 
+    @Autowired
+    private MaterialTypeBaseTranslator baseTranslator;
+
     @Override
     protected MaterialType createObject(TranslationContext context, Long typeId, MaterialTypeFetchOptions fetchOptions)
     {
@@ -44,21 +48,21 @@ public class MaterialTypeSqlTranslator extends AbstractCachingTranslator<Long, M
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<Long> typeIds, MaterialTypeFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<Long> typeIds, MaterialTypeFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
-        relations.add(createRelation(MaterialTypeBaseRelation.class, typeIds));
+        relations.put(MaterialTypeBaseTranslator.class, baseTranslator.translate(context, typeIds, null));
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, Long typeId, MaterialType result, Relations relations,
+    protected void updateObject(TranslationContext context, Long typeId, MaterialType result, Object objectRelations,
             MaterialTypeFetchOptions fetchOptions)
     {
-        MaterialTypeBaseRelation baseRelation = relations.get(MaterialTypeBaseRelation.class);
-        MaterialTypeBaseRecord baseRecord = baseRelation.getRecord(typeId);
+        TranslationResults relations = (TranslationResults) objectRelations;
+        MaterialTypeBaseRecord baseRecord = relations.get(MaterialTypeBaseTranslator.class, typeId);
 
         result.setPermId(new EntityTypePermId(baseRecord.code));
         result.setCode(baseRecord.code);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/PersonTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/PersonTranslator.java
index a7b41f50d1ee530ef028fe12b620b113ece78ce2..ea59765160ba02dd2e4f4e1234323538e681d261 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/PersonTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/PersonTranslator.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.space.ISpaceTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
@@ -56,7 +55,7 @@ public class PersonTranslator extends AbstractCachingTranslator<PersonPE, Person
     }
 
     @Override
-    protected void updateObject(TranslationContext context, PersonPE person, Person result, Relations relations, PersonFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, PersonPE person, Person result, Object relations, PersonFetchOptions fetchOptions)
     {
         if (fetchOptions.hasSpace())
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonBaseRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonBaseTranslator.java
similarity index 69%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonBaseRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonBaseTranslator.java
index 3231eaa6507b647aa46c332b0a3039b819185fd0..e63185e048a9ad250ef20b74bf6bec0f2aa42bc8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonBaseRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonBaseTranslator.java
@@ -18,32 +18,23 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.sql;
 
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 
-import java.util.Collection;
 import java.util.List;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseTranslator;
 
 /**
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class PersonBaseRelation extends ObjectBaseRelation<PersonBaseRecord>
+public class PersonBaseTranslator extends ObjectBaseTranslator<PersonBaseRecord>
 {
 
-    public PersonBaseRelation(Collection<Long> objectIds)
-    {
-        super(objectIds);
-    }
-
     @Override
-    protected List<PersonBaseRecord> load(LongOpenHashSet objectIds)
+    protected List<PersonBaseRecord> loadRecords(LongOpenHashSet objectIds)
     {
         PersonQuery query = QueryTool.getManagedQuery(PersonQuery.class);
         return query.getPersons(new LongOpenHashSet(objectIds));
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSpaceRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSpaceTranslator.java
similarity index 72%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSpaceRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSpaceTranslator.java
index a051f92fccfb5dda81e6e5636be74440900cb017..83b296778434a08086ce3b8dda92a2306ae8e69a 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSpaceRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSpaceTranslator.java
@@ -25,13 +25,11 @@ import java.util.Map;
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelationTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.space.sql.ISpaceSqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.space.Space;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.space.SpaceFetchOptions;
@@ -40,27 +38,21 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.space.SpaceFet
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class PersonSpaceRelation extends ObjectToOneRelation<Space, SpaceFetchOptions>
+public class PersonSpaceTranslator extends ObjectToOneRelationTranslator<Space, SpaceFetchOptions>
 {
 
     @Autowired
     private ISpaceSqlTranslator spaceTranslator;
 
-    public PersonSpaceRelation(TranslationContext context, Collection<Long> objectIds, SpaceFetchOptions relatedFetchOptions)
-    {
-        super(context, objectIds, relatedFetchOptions);
-    }
-
     @Override
-    protected List<ObjectRelationRecord> load(LongOpenHashSet objectIds)
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
     {
         PersonQuery query = QueryTool.getManagedQuery(PersonQuery.class);
         return query.getSpaces(objectIds);
     }
 
     @Override
-    protected Map<Long, Space> translate(TranslationContext context, Collection<Long> relatedIds, SpaceFetchOptions relatedFetchOptions)
+    protected Map<Long, Space> translateRelated(TranslationContext context, Collection<Long> relatedIds, SpaceFetchOptions relatedFetchOptions)
     {
         return spaceTranslator.translate(context, relatedIds, relatedFetchOptions);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSqlTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSqlTranslator.java
index d5b4dc2c5f932c9b1f8da17957b2f0521f958a92..dab1cddfeb5d410c4e42a3662e22563ad7777e8f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSqlTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/person/sql/PersonSqlTranslator.java
@@ -18,11 +18,12 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.sql;
 
 import java.util.Collection;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonFetchOptions;
 
@@ -33,6 +34,12 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonF
 public class PersonSqlTranslator extends AbstractCachingTranslator<Long, Person, PersonFetchOptions> implements IPersonSqlTranslator
 {
 
+    @Autowired
+    private PersonBaseTranslator baseTranslator;
+
+    @Autowired
+    private PersonSpaceTranslator spaceTranslator;
+
     @Override
     protected Person createObject(TranslationContext context, Long personId, PersonFetchOptions fetchOptions)
     {
@@ -42,25 +49,26 @@ public class PersonSqlTranslator extends AbstractCachingTranslator<Long, Person,
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<Long> personIds, PersonFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<Long> personIds, PersonFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
-        relations.add(createRelation(PersonBaseRelation.class, personIds));
+        relations.put(PersonBaseTranslator.class, baseTranslator.translate(context, personIds, null));
 
         if (fetchOptions.hasSpace())
         {
-            relations.add(createRelation(PersonSpaceRelation.class, context, personIds, fetchOptions.withSpace()));
+            relations.put(PersonSpaceTranslator.class, spaceTranslator.translate(context, personIds, fetchOptions.withSpace()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, Long personId, Person result, Relations relations, PersonFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, Long personId, Person result, Object objectRelations,
+            PersonFetchOptions fetchOptions)
     {
-        PersonBaseRelation baseRelation = relations.get(PersonBaseRelation.class);
-        PersonBaseRecord baseRecord = baseRelation.getRecord(personId);
+        TranslationResults relations = (TranslationResults) objectRelations;
+        PersonBaseRecord baseRecord = relations.get(PersonBaseTranslator.class, personId);
 
         result.setFirstName(baseRecord.firstName);
         result.setLastName(baseRecord.lastName);
@@ -71,8 +79,7 @@ public class PersonSqlTranslator extends AbstractCachingTranslator<Long, Person,
 
         if (fetchOptions.hasSpace())
         {
-            PersonSpaceRelation relation = relations.get(PersonSpaceRelation.class);
-            result.setSpace(relation.getRelated(personId));
+            result.setSpace(relations.get(PersonSpaceTranslator.class, personId));
             result.getFetchOptions().withSpaceUsing(fetchOptions.withSpace());
         }
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/ProjectTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/ProjectTranslator.java
index c8252d306874ab9409b175d877ea9a4a578c4d67..9f7ae5977e6cc75f1e962561d4efd6bb3ed7741d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/ProjectTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/ProjectTranslator.java
@@ -27,12 +27,12 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.attachment.IAttachmentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment.IExperimentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.project.sql.ProjectHistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.project.sql.ProjectHistoryTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.space.ISpaceTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.attachment.Attachment;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.Experiment;
@@ -63,6 +63,9 @@ public class ProjectTranslator extends AbstractCachingTranslator<ProjectPE, Proj
     @Autowired
     private IAttachmentTranslator attachmentTranslator;
 
+    @Autowired
+    private ProjectHistoryTranslator historyTranslator;
+
     @Override
     protected boolean shouldTranslate(TranslationContext context, ProjectPE input, ProjectFetchOptions fetchOptions)
     {
@@ -86,9 +89,9 @@ public class ProjectTranslator extends AbstractCachingTranslator<ProjectPE, Proj
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<ProjectPE> projects, ProjectFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<ProjectPE> projects, ProjectFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
         if (fetchOptions.hasHistory())
         {
@@ -97,15 +100,18 @@ public class ProjectTranslator extends AbstractCachingTranslator<ProjectPE, Proj
             {
                 projectIds.add(project.getId());
             }
-            relations.add(createRelation(ProjectHistoryRelation.class, context, projectIds, fetchOptions.withHistory()));
+            relations.put(ProjectHistoryTranslator.class, historyTranslator.translate(context, projectIds, fetchOptions.withHistory()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, ProjectPE project, Project result, Relations relations, ProjectFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, ProjectPE project, Project result, Object objectRelations,
+            ProjectFetchOptions fetchOptions)
     {
+        TranslationResults relations = (TranslationResults) objectRelations;
+
         if (fetchOptions.hasSpace())
         {
             result.setSpace(spaceTranslator.translate(context, project.getSpace(), fetchOptions.withSpace()));
@@ -147,8 +153,7 @@ public class ProjectTranslator extends AbstractCachingTranslator<ProjectPE, Proj
 
         if (fetchOptions.hasHistory())
         {
-            ProjectHistoryRelation relation = relations.get(ProjectHistoryRelation.class);
-            result.setHistory(relation.getRelated(project.getId()));
+            result.setHistory(relations.get(ProjectHistoryTranslator.class, project.getId()));
             result.getFetchOptions().withHistoryUsing(fetchOptions.withHistory());
         }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/sql/ProjectHistoryRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/sql/ProjectHistoryTranslator.java
similarity index 82%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/sql/ProjectHistoryRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/sql/ProjectHistoryTranslator.java
index 14e91ccc54b71bc3c1bd49da4c1da36d39740d10..d61b34149b8460003555a0e690d05cb47d6fa0d0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/sql/ProjectHistoryRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/project/sql/ProjectHistoryTranslator.java
@@ -24,14 +24,11 @@ import java.util.Map;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryPropertyRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelation;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelationshipRecord;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistorySqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.ProjectRelationType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.RelationHistoryEntry;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
@@ -43,15 +40,9 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.space.SpacePermId;
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class ProjectHistoryRelation extends HistoryRelation
+public class ProjectHistoryTranslator extends HistorySqlTranslator
 {
 
-    public ProjectHistoryRelation(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
-    {
-        super(context, entityIds, fetchOptions);
-    }
-
     @Override
     protected List<HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds)
     {
@@ -66,9 +57,10 @@ public class ProjectHistoryRelation extends HistoryRelation
     }
 
     @Override
-    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap)
+    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
-        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap);
+        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap, fetchOptions);
 
         ProjectRelationshipRecord projectRecord = (ProjectRelationshipRecord) record;
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyTranslator.java
index d356a93815817fccbdd15be6a47145936672be25..5e604cdcbf044af7c33eaffdad3eff8d1c0402a1 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/PropertyTranslator.java
@@ -25,7 +25,6 @@ import javax.annotation.Resource;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.property.PropertyFetchOptions;
 import ch.systemsx.cisd.openbis.generic.server.ComponentNames;
@@ -72,7 +71,7 @@ public class PropertyTranslator extends AbstractCachingTranslator<IEntityPropert
     }
 
     @Override
-    protected void updateObject(TranslationContext context, IEntityPropertiesHolder input, Map<String, String> output, Relations relations,
+    protected void updateObject(TranslationContext context, IEntityPropertiesHolder input, Map<String, String> output, Object relations,
             PropertyFetchOptions fetchOptions)
     {
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/sql/PropertySqlTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/sql/PropertySqlTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..46428af3aa5e07c3fa84c47a53790243bd345a9c
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/property/sql/PropertySqlTranslator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.property.sql;
+
+import java.util.Collection;
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectHolder;
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.property.PropertyFetchOptions;
+
+/**
+ * @author pkupczyk
+ */
+public abstract class PropertySqlTranslator extends AbstractCachingTranslator<Long, ObjectHolder<Map<String, String>>, PropertyFetchOptions>
+{
+
+    @Override
+    protected ObjectHolder<Map<String, String>> createObject(TranslationContext context, Long objectId, PropertyFetchOptions fetchOptions)
+    {
+        return new ObjectHolder<Map<String, String>>();
+    }
+
+    @Override
+    protected Object getObjectsRelations(TranslationContext context, Collection<Long> objectIds, PropertyFetchOptions fetchOptions)
+    {
+        return loadProperties(objectIds);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void updateObject(TranslationContext context, Long objectId, ObjectHolder<Map<String, String>> result, Object relations,
+            PropertyFetchOptions fetchOptions)
+    {
+        Map<Long, Map<String, String>> properties = (Map<Long, Map<String, String>>) relations;
+        result.setObject(properties.get(objectId));
+    }
+
+    protected abstract Map<Long, Map<String, String>> loadProperties(Collection<Long> entityIds);
+
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTranslator.java
index 59fbbf208a6ab8bfb981b3b1700350f8a8f3bd59..c03f4a1ee00dfed26d5755b29a8070fbd5008041 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTranslator.java
@@ -2,9 +2,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -14,30 +11,26 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.ToManyRelation;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.ToOneRelation;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.attachment.IAttachmentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.dataset.IDataSetTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment.IExperimentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.material.IMaterialPropertyTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.property.IPropertyTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.sql.SampleHistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.sql.SampleExperimentTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.sql.SampleHistoryTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.sql.SampleParentTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.space.ISpaceTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.tag.ITagTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.dataset.DataSet;
-import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.Sample;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.tag.Tag;
-import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.experiment.ExperimentFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sample.SampleFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.sample.SampleIdentifier;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.sample.SamplePermId;
 import ch.systemsx.cisd.openbis.generic.server.authorization.validator.SampleByIdentiferValidator;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
 
@@ -49,7 +42,10 @@ public class SampleTranslator extends AbstractCachingTranslator<SamplePE, Sample
     private ISpaceTranslator spaceTranslator;
 
     @Autowired
-    private IExperimentTranslator experimentTranslator;
+    private SampleExperimentTranslator experimentTranslator;
+
+    @Autowired
+    private SampleParentTranslator parentTranslator;
 
     @Autowired
     private IAttachmentTranslator attachmentTranslator;
@@ -72,6 +68,9 @@ public class SampleTranslator extends AbstractCachingTranslator<SamplePE, Sample
     @Autowired
     private IDataSetTranslator dataSetTranslator;
 
+    @Autowired
+    private SampleHistoryTranslator historyTranslator;
+
     @Override
     protected boolean shouldTranslate(TranslationContext context, SamplePE input, SampleFetchOptions fetchOptions)
     {
@@ -93,39 +92,43 @@ public class SampleTranslator extends AbstractCachingTranslator<SamplePE, Sample
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, final Collection<SamplePE> samples, SampleFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, final Collection<SamplePE> samples, SampleFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
+
+        Set<Long> sampleIds = new HashSet<Long>();
+        for (SamplePE sample : samples)
+        {
+            sampleIds.add(sample.getId());
+        }
 
         if (fetchOptions.hasExperiment())
         {
-            relations.add(new SampleExperimentRelation(context, samples, fetchOptions.withExperiment()));
+            relations.put(SampleExperimentTranslator.class, experimentTranslator.translate(context, sampleIds, fetchOptions.withExperiment()));
         }
 
         if (fetchOptions.hasParents())
         {
-            relations.add(new SampleParentsRelation(context, samples, fetchOptions.withParents()));
+            relations.put(SampleParentTranslator.class, parentTranslator.translate(context, sampleIds, fetchOptions.withParents()));
         }
 
         if (fetchOptions.hasHistory())
         {
-            Set<Long> sampleIds = new HashSet<Long>();
-            for (SamplePE sample : samples)
-            {
-                sampleIds.add(sample.getId());
-            }
-            relations.add(createRelation(SampleHistoryRelation.class, context, sampleIds, fetchOptions.withHistory()));
+            relations.put(SampleHistoryTranslator.class, historyTranslator.translate(context, sampleIds, fetchOptions.withHistory()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, SamplePE samplePe, Sample result, Relations relations, SampleFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, SamplePE samplePe, Sample result, Object objectRelations,
+            SampleFetchOptions fetchOptions)
     {
+        TranslationResults relations = (TranslationResults) objectRelations;
+
         if (fetchOptions.hasExperiment())
         {
-            result.setExperiment(relations.get(SampleExperimentRelation.class).getTranslated(samplePe));
+            result.setExperiment(relations.get(SampleExperimentTranslator.class, samplePe.getId()));
             result.getFetchOptions().withExperimentUsing(fetchOptions.withExperiment());
         }
 
@@ -149,7 +152,7 @@ public class SampleTranslator extends AbstractCachingTranslator<SamplePE, Sample
 
         if (fetchOptions.hasParents())
         {
-            result.setParents(relations.get(SampleParentsRelation.class).getTranslatedList(samplePe));
+            result.setParents((List<Sample>) relations.get(SampleParentTranslator.class, samplePe.getId()));
             result.getFetchOptions().withParentsUsing(fetchOptions.withParents());
         }
 
@@ -213,83 +216,9 @@ public class SampleTranslator extends AbstractCachingTranslator<SamplePE, Sample
 
         if (fetchOptions.hasHistory())
         {
-            SampleHistoryRelation relation = relations.get(SampleHistoryRelation.class);
-            result.setHistory(relation.getRelated(samplePe.getId()));
+            result.setHistory(relations.get(SampleHistoryTranslator.class, samplePe.getId()));
             result.getFetchOptions().withHistoryUsing(fetchOptions.withHistory());
         }
     }
 
-    private class SampleExperimentRelation extends ToOneRelation<SamplePE, ExperimentPE, Experiment, ExperimentFetchOptions>
-    {
-
-        private Collection<SamplePE> samples;
-
-        public SampleExperimentRelation(TranslationContext context, Collection<SamplePE> samples, ExperimentFetchOptions fetchOptions)
-        {
-            super(context, fetchOptions);
-            this.samples = samples;
-        }
-
-        @Override
-        protected Map<SamplePE, ExperimentPE> getOriginalMap()
-        {
-            // TODO replace with one database call
-            Map<SamplePE, ExperimentPE> map = new HashMap<SamplePE, ExperimentPE>();
-            for (SamplePE sample : samples)
-            {
-                map.put(sample, sample.getExperiment());
-            }
-            return map;
-        }
-
-        @Override
-        protected Map<ExperimentPE, Experiment> getTranslatedMap(TranslationContext context, Collection<ExperimentPE> originalCollection,
-                ExperimentFetchOptions fetchOptions)
-        {
-            return experimentTranslator.translate(context, originalCollection, fetchOptions);
-        }
-    }
-
-    private class SampleParentsRelation extends ToManyRelation<SamplePE, SamplePE, Sample, SampleFetchOptions>
-    {
-
-        private Collection<SamplePE> samples;
-
-        public SampleParentsRelation(TranslationContext context, Collection<SamplePE> samples, SampleFetchOptions fetchOptions)
-        {
-            super(context, fetchOptions);
-            this.samples = samples;
-        }
-
-        @Override
-        protected Map<SamplePE, Collection<SamplePE>> getOriginalMap()
-        {
-            // TODO replace with one database call
-            Map<SamplePE, Collection<SamplePE>> map = new HashMap<SamplePE, Collection<SamplePE>>();
-            for (SamplePE sample : samples)
-            {
-                List<SamplePE> parents = new ArrayList<SamplePE>(sample.getParents());
-                // return parents in the same order as they were created
-                Collections.sort(parents, new Comparator<SamplePE>()
-                    {
-                        @Override
-                        public int compare(SamplePE s1, SamplePE s2)
-                        {
-                            return s1.getId().compareTo(s2.getId());
-                        }
-                    });
-                map.put(sample, parents);
-            }
-            return map;
-        }
-
-        @Override
-        protected Map<SamplePE, Sample> getTranslatedMap(TranslationContext context, Collection<SamplePE> originalCollection,
-                SampleFetchOptions fetchOptions)
-        {
-            return translate(context, originalCollection, fetchOptions);
-        }
-
-    }
-
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTypeTranslator.java
index 4b1ac2f9061626834b8c856e8bc62780515e113e..b231c916bf4f6212f2ed15c7465da0506240a417 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/SampleTypeTranslator.java
@@ -3,7 +3,6 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.SampleType;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sample.SampleTypeFetchOptions;
@@ -34,7 +33,7 @@ public class SampleTypeTranslator extends AbstractCachingTranslator<SampleTypePE
     }
 
     @Override
-    protected void updateObject(TranslationContext context, SampleTypePE input, SampleType output, Relations relations,
+    protected void updateObject(TranslationContext context, SampleTypePE input, SampleType output, Object relations,
             SampleTypeFetchOptions fetchOptions)
     {
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleExperimentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleExperimentTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d0eb3fb4f9f01757a91aec667cdcfed1c7e285e
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleExperimentTranslator.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.sql;
+
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.lemnik.eodsql.QueryTool;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelationTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.experiment.IExperimentTranslator;
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.experiment.Experiment;
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.experiment.ExperimentFetchOptions;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class SampleExperimentTranslator extends ObjectToOneRelationTranslator<Experiment, ExperimentFetchOptions>
+{
+
+    @Autowired
+    private IDAOFactory daoFactory;
+
+    @Autowired
+    private IExperimentTranslator experimentTranslator;
+
+    @Override
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
+    {
+        SampleQuery query = QueryTool.getManagedQuery(SampleQuery.class);
+        return query.getExperiments(new LongOpenHashSet(objectIds));
+    }
+
+    @Override
+    protected Map<Long, Experiment> translateRelated(TranslationContext context, Collection<Long> relatedIds,
+            ExperimentFetchOptions relatedFetchOptions)
+    {
+        List<ExperimentPE> related = daoFactory.getExperimentDAO().listByIDs(relatedIds);
+        Map<ExperimentPE, Experiment> translated = experimentTranslator.translate(context, related, relatedFetchOptions);
+        Map<Long, Experiment> result = new HashMap<Long, Experiment>();
+
+        for (Map.Entry<ExperimentPE, Experiment> entry : translated.entrySet())
+        {
+            result.put(entry.getKey().getId(), entry.getValue());
+        }
+        return result;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleHistoryRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleHistoryTranslator.java
similarity index 87%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleHistoryRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleHistoryTranslator.java
index feed528d77628e02e6e5e43ef5934c56cd8a4818..4cd3a40373e75ad79d479fa6b3b65b53db34f849 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleHistoryRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleHistoryTranslator.java
@@ -22,15 +22,12 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Component;
-
 import net.lemnik.eodsql.QueryTool;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import org.springframework.stereotype.Component;
+
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryPropertyRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistorySqlTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryRelationshipRecord;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.RelationHistoryEntry;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.history.SampleRelationType;
@@ -46,15 +43,9 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.RelationType;
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class SampleHistoryRelation extends HistoryRelation
+public class SampleHistoryTranslator extends HistorySqlTranslator
 {
 
-    public SampleHistoryRelation(TranslationContext context, Collection<Long> entityIds, HistoryEntryFetchOptions fetchOptions)
-    {
-        super(context, entityIds, fetchOptions);
-    }
-
     @Override
     protected List<HistoryPropertyRecord> loadPropertyHistory(Collection<Long> entityIds)
     {
@@ -70,9 +61,10 @@ public class SampleHistoryRelation extends HistoryRelation
     }
 
     @Override
-    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap)
+    protected RelationHistoryEntry createRelationshipEntry(HistoryRelationshipRecord record, Map<Long, Person> authorMap,
+            HistoryEntryFetchOptions fetchOptions)
     {
-        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap);
+        RelationHistoryEntry entry = super.createRelationshipEntry(record, authorMap, fetchOptions);
 
         SampleRelationshipRecord sampleRecord = (SampleRelationshipRecord) record;
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleParentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleParentTranslator.java
new file mode 100644
index 0000000000000000000000000000000000000000..05b89a8fa84c77a15697eaced519989fffcb31b7
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleParentTranslator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.sql;
+
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.lemnik.eodsql.QueryTool;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToManyRelationTranslator;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.sample.ISampleTranslator;
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.sample.Sample;
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.sample.SampleFetchOptions;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class SampleParentTranslator extends ObjectToManyRelationTranslator<Sample, SampleFetchOptions>
+{
+
+    @Autowired
+    private IDAOFactory daoFactory;
+
+    @Autowired
+    private ISampleTranslator sampleTranslator;
+
+    @Override
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
+    {
+        SampleQuery query = QueryTool.getManagedQuery(SampleQuery.class);
+        return query.getParents(new LongOpenHashSet(objectIds));
+    }
+
+    @Override
+    protected Map<Long, Sample> translateRelated(TranslationContext context, Collection<Long> relatedIds,
+            SampleFetchOptions relatedFetchOptions)
+    {
+        List<SamplePE> related = daoFactory.getSampleDAO().listByIDs(relatedIds);
+        Map<SamplePE, Sample> translated = sampleTranslator.translate(context, related, relatedFetchOptions);
+        Map<Long, Sample> result = new HashMap<Long, Sample>();
+
+        for (Map.Entry<SamplePE, Sample> entry : translated.entrySet())
+        {
+            result.put(entry.getKey().getId(), entry.getValue());
+        }
+        return result;
+    }
+
+    @Override
+    protected Collection<Sample> createCollection()
+    {
+        return new ArrayList<Sample>();
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleQuery.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleQuery.java
index a8c0027435a88f5172c7fd544292c0e5ff811f2a..e672676a36d8a545673a51d13bb49d6a18e8e450 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleQuery.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/sample/sql/SampleQuery.java
@@ -23,6 +23,7 @@ import java.util.List;
 import net.lemnik.eodsql.Select;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectQuery;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.history.sql.HistoryPropertyRecord;
 import ch.systemsx.cisd.common.db.mapper.LongSetMapper;
 
@@ -45,4 +46,12 @@ public interface SampleQuery extends ObjectQuery
             + "from sample_relationships_history srh where srh.valid_until_timestamp is not null and srh.main_samp_id = any(?{1})", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
     public List<SampleRelationshipRecord> getRelationshipsHistory(LongSet sampleIds);
 
+    @Select(sql = "select s.id as objectId, s.expe_id as relatedId from samples s where s.id = any(?{1})", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
+    public List<ObjectRelationRecord> getExperiments(LongSet sampleIds);
+
+    @Select(sql = "select sr.sample_id_child as objectId, sr.sample_id_parent as relatedId from "
+            + "sample_relationships sr, relationship_types rt "
+            + "where sr.relationship_id = rt.id and rt.code = 'PARENT_CHILD' and sr.sample_id_child = any(?{1}) order by sr.id", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
+    public List<ObjectRelationRecord> getParents(LongSet sampleIds);
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/space/SpaceTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/space/SpaceTranslator.java
index d4205d859595b11105965250240e015bb8d2caa3..06ca4ede3c62bfe59651c27b51ca7760ac9ff033 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/space/SpaceTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/space/SpaceTranslator.java
@@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.PersonTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.project.ProjectTranslator;
@@ -75,7 +74,7 @@ public class SpaceTranslator extends AbstractCachingTranslator<SpacePE, Space, S
     }
 
     @Override
-    protected void updateObject(TranslationContext context, SpacePE space, Space result, Relations relations, SpaceFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, SpacePE space, Space result, Object relations, SpaceFetchOptions fetchOptions)
     {
         if (fetchOptions.hasRegistrator())
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/TagTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/TagTranslator.java
index e5e22914494e4d0261f83a1d64d5e3e38002d1de..c6780a94441ae3f5753b6a46b11c41b69a4c321d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/TagTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/TagTranslator.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.tag.Tag;
@@ -60,7 +59,7 @@ public class TagTranslator extends AbstractCachingTranslator<MetaprojectPE, Tag,
     }
 
     @Override
-    protected void updateObject(TranslationContext context, MetaprojectPE tag, Tag result, Relations relations, TagFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, MetaprojectPE tag, Tag result, Object relations, TagFetchOptions fetchOptions)
     {
         if (fetchOptions.hasOwner())
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagBaseRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagBaseTranslator.java
similarity index 70%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagBaseRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagBaseTranslator.java
index efdfee16faa886adb03272b948807dbc85bedc7f..ba1a6db47c03268429d811cda8927c8ababb42a6 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagBaseRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagBaseTranslator.java
@@ -18,32 +18,23 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.tag.sql;
 
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 
-import java.util.Collection;
 import java.util.List;
 
 import net.lemnik.eodsql.QueryTool;
 
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectBaseTranslator;
 
 /**
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class TagBaseRelation extends ObjectBaseRelation<TagBaseRecord>
+public class TagBaseTranslator extends ObjectBaseTranslator<TagBaseRecord>
 {
 
-    public TagBaseRelation(Collection<Long> objectIds)
-    {
-        super(objectIds);
-    }
-
     @Override
-    protected List<TagBaseRecord> load(LongOpenHashSet objectIds)
+    protected List<TagBaseRecord> loadRecords(LongOpenHashSet objectIds)
     {
         TagQuery query = QueryTool.getManagedQuery(TagQuery.class);
         return query.getTags(new LongOpenHashSet(objectIds));
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagOwnerRelation.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagOwnerTranslator.java
similarity index 72%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagOwnerRelation.java
rename to openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagOwnerTranslator.java
index 1b6a4c516b5e63f1682d04213b94248f8b904840..6a1464b59b271a5b6e940beb0f2e3f9b5588a4a5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagOwnerRelation.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagOwnerTranslator.java
@@ -25,13 +25,11 @@ import java.util.Map;
 import net.lemnik.eodsql.QueryTool;
 
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectRelationRecord;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelation;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.common.sql.ObjectToOneRelationTranslator;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.sql.IPersonSqlTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonFetchOptions;
@@ -40,27 +38,21 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonF
  * @author pkupczyk
  */
 @Component
-@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class TagOwnerRelation extends ObjectToOneRelation<Person, PersonFetchOptions>
+public class TagOwnerTranslator extends ObjectToOneRelationTranslator<Person, PersonFetchOptions>
 {
 
     @Autowired
     private IPersonSqlTranslator personTranslator;
 
-    public TagOwnerRelation(TranslationContext context, Collection<Long> objectIds, PersonFetchOptions relatedFetchOptions)
-    {
-        super(context, objectIds, relatedFetchOptions);
-    }
-
     @Override
-    protected List<ObjectRelationRecord> load(LongOpenHashSet objectIds)
+    protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
     {
         TagQuery query = QueryTool.getManagedQuery(TagQuery.class);
         return query.getOwnerIds(objectIds);
     }
 
     @Override
-    protected Map<Long, Person> translate(TranslationContext context, Collection<Long> relatedIds, PersonFetchOptions relatedFetchOptions)
+    protected Map<Long, Person> translateRelated(TranslationContext context, Collection<Long> relatedIds, PersonFetchOptions relatedFetchOptions)
     {
         return personTranslator.translate(context, relatedIds, relatedFetchOptions);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagSqlTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagSqlTranslator.java
index a8a504e9509a6262c3b506afd4af496e8951258f..b22046cacc69ff70557fb69981d2928cc1d453de 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagSqlTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/tag/sql/TagSqlTranslator.java
@@ -19,16 +19,18 @@ package ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.tag.sql;
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
 
 import net.lemnik.eodsql.QueryTool;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationResults;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.tag.Tag;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.tag.TagFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.tag.TagPermId;
@@ -40,6 +42,12 @@ import ch.ethz.sis.openbis.generic.shared.api.v3.dto.id.tag.TagPermId;
 public class TagSqlTranslator extends AbstractCachingTranslator<Long, Tag, TagFetchOptions> implements ITagSqlTranslator
 {
 
+    @Autowired
+    private TagBaseTranslator baseTranslator;
+
+    @Autowired
+    private TagOwnerTranslator ownerTranslator;
+
     @Override
     protected Collection<Long> shouldTranslate(TranslationContext context, Collection<Long> tagIds, TagFetchOptions fetchOptions)
     {
@@ -67,25 +75,25 @@ public class TagSqlTranslator extends AbstractCachingTranslator<Long, Tag, TagFe
     }
 
     @Override
-    protected Relations getObjectsRelations(TranslationContext context, Collection<Long> tagIds, TagFetchOptions fetchOptions)
+    protected TranslationResults getObjectsRelations(TranslationContext context, Collection<Long> tagIds, TagFetchOptions fetchOptions)
     {
-        Relations relations = new Relations();
+        TranslationResults relations = new TranslationResults();
 
-        relations.add(createRelation(TagBaseRelation.class, tagIds));
+        relations.put(TagBaseTranslator.class, baseTranslator.translate(context, tagIds, null));
 
         if (fetchOptions.hasOwner())
         {
-            relations.add(createRelation(TagOwnerRelation.class, context, tagIds, fetchOptions.withOwner()));
+            relations.put(TagOwnerTranslator.class, ownerTranslator.translate(context, tagIds, fetchOptions.withOwner()));
         }
 
         return relations;
     }
 
     @Override
-    protected void updateObject(TranslationContext context, Long tagId, Tag result, Relations relations, TagFetchOptions fetchOptions)
+    protected void updateObject(TranslationContext context, Long tagId, Tag result, Object objectRelations, TagFetchOptions fetchOptions)
     {
-        TagBaseRelation baseRelation = relations.get(TagBaseRelation.class);
-        TagBaseRecord baseRecord = baseRelation.getRecord(tagId);
+        TranslationResults relations = (TranslationResults) objectRelations;
+        TagBaseRecord baseRecord = relations.get(TagBaseTranslator.class, tagId);
 
         result.setPermId(new TagPermId(baseRecord.owner, baseRecord.name));
         result.setCode(baseRecord.name);
@@ -95,10 +103,39 @@ public class TagSqlTranslator extends AbstractCachingTranslator<Long, Tag, TagFe
 
         if (fetchOptions.hasOwner())
         {
-            TagOwnerRelation relation = relations.get(TagOwnerRelation.class);
-            result.setOwner(relation.getRelated(tagId));
+            result.setOwner(relations.get(TagOwnerTranslator.class, tagId));
             result.getFetchOptions().withOwnerUsing(fetchOptions.withOwner());
         }
     }
 
+    @Override
+    protected Comparator<Tag> getObjectComparator(TranslationContext context, final TagFetchOptions fetchOptions)
+    {
+        if (fetchOptions.getSortBy() == null)
+        {
+            return null;
+        }
+
+        if (fetchOptions.getSortBy().isCode())
+        {
+            return new Comparator<Tag>()
+                {
+                    @Override
+                    public int compare(Tag o1, Tag o2)
+                    {
+                        if (fetchOptions.getSortBy().code().isAsc())
+                        {
+                            return o1.getCode().compareTo(o2.getCode());
+                        } else
+                        {
+                            return -o1.getCode().compareTo(o2.getCode());
+                        }
+                    }
+                };
+        }
+
+        return null;
+
+    }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTermTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTermTranslator.java
index 2bada69d85f93338d83fe356d49ca3df5d10c57a..a4ee313688022bec7ded6a88d5490106be754b9a 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTermTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTermTranslator.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
@@ -61,7 +60,7 @@ public class VocabularyTermTranslator extends AbstractCachingTranslator<Vocabula
     }
 
     @Override
-    protected void updateObject(TranslationContext context, VocabularyTermPE term, VocabularyTerm result, Relations relations,
+    protected void updateObject(TranslationContext context, VocabularyTermPE term, VocabularyTerm result, Object relations,
             VocabularyTermFetchOptions fetchOptions)
     {
         if (fetchOptions.hasRegistrator())
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTranslator.java
index fb84ff8bbd758fe71d50cb977dfdc835aa591a71..ee0970fa12a6cb11ccd91eaac1493456a4c788b0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/entity/vocabulary/VocabularyTranslator.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.AbstractCachingTranslator;
-import ch.ethz.sis.openbis.generic.server.api.v3.translator.Relations;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.api.v3.translator.entity.person.IPersonTranslator;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.entity.person.Person;
@@ -54,7 +53,7 @@ public class VocabularyTranslator extends AbstractCachingTranslator<VocabularyPE
     }
 
     @Override
-    protected void updateObject(TranslationContext context, VocabularyPE vocabulary, Vocabulary result, Relations relations,
+    protected void updateObject(TranslationContext context, VocabularyPE vocabulary, Vocabulary result, Object relations,
             VocabularyFetchOptions fetchOptions)
     {
         if (fetchOptions.hasRegistrator())
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
index 3341dcc7edc35899597575a9af3f1eb636dd524c..94bedb76ac7ce4750dcdd57a203d40201e6a54ea 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.SortedMap;
@@ -142,9 +143,9 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
 
     private DataStorePE dataStore;
 
-    private Set<DataSetRelationshipPE> parentRelationships = new HashSet<DataSetRelationshipPE>();
+    private Set<DataSetRelationshipPE> parentRelationships = new LinkedHashSet<DataSetRelationshipPE>();
 
-    private Set<DataSetRelationshipPE> childRelationships = new HashSet<DataSetRelationshipPE>();
+    private Set<DataSetRelationshipPE> childRelationships = new LinkedHashSet<DataSetRelationshipPE>();
 
     @OptimisticLock(excluded = true)
     @OneToMany(fetch = FetchType.LAZY, mappedBy = "parentDataSet")
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
index 2729f7e5e0073aae165afb20aecf9111e52527e6..e5bc6f1ba981e1b7abf9e76d06d8b5a12a9e4460 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
@@ -22,6 +22,7 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -111,9 +112,9 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
 
     private String permId;
 
-    private Set<SampleRelationshipPE> parentRelationships = new HashSet<SampleRelationshipPE>();
+    private Set<SampleRelationshipPE> parentRelationships = new LinkedHashSet<SampleRelationshipPE>();
 
-    private Set<SampleRelationshipPE> childRelationships = new HashSet<SampleRelationshipPE>();
+    private Set<SampleRelationshipPE> childRelationships = new LinkedHashSet<SampleRelationshipPE>();
 
     private Set<MetaprojectAssignmentPE> metaprojectAssignments =
             new HashSet<MetaprojectAssignmentPE>();
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/Session.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/Session.java
index 82e75bd099c05df131bab82dad78bf98a7ce2239..32a493af3383a18aecb3c31f49ddd42aca8c7f56 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/Session.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/Session.java
@@ -16,7 +16,9 @@
 
 package ch.systemsx.cisd.openbis.generic.shared.dto;
 
+import java.util.HashMap;
 import java.util.LinkedHashSet;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.commons.lang.time.DateFormatUtils;
@@ -61,6 +63,8 @@ public final class Session extends BasicSession implements IAuthSession
 
     private final Set<ISessionCleaner> cleanupListeners = new LinkedHashSet<ISessionCleaner>();
 
+    private Map<String, Object> attributes = new HashMap<String, Object>();
+
     @Deprecated
     public Session()
     {
@@ -180,6 +184,11 @@ public final class Session extends BasicSession implements IAuthSession
         cleanupListeners.remove(sessionCleaner);
     }
 
+    public Map<String, Object> getAttributes()
+    {
+        return attributes;
+    }
+
     //
     // Object
     //
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/FetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/FetchOptions.java
index c6f5f84e745cde669a5819ed62e52454ae6e5181..0e2e8bc91f0cfee86eed8ba12f12dad35e417703 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/FetchOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/FetchOptions.java
@@ -7,6 +7,46 @@ import ch.systemsx.cisd.base.annotation.JsonObject;
 @JsonObject("dto.fetchoptions.FetchOptions")
 public abstract class FetchOptions implements Serializable
 {
+
     private static final long serialVersionUID = 1L;
 
+    private Integer pageSize;
+
+    private Integer pageIndex;
+
+    private Integer cacheMode;
+
+    public FetchOptions count(Integer size)
+    {
+        this.pageSize = size;
+        return this;
+    }
+
+    public Integer getPageSize()
+    {
+        return pageSize;
+    }
+
+    public FetchOptions from(Integer index)
+    {
+        this.pageIndex = index;
+        return this;
+    }
+
+    public Integer getPageIndex()
+    {
+        return pageIndex;
+    }
+
+    public FetchOptions cacheMode(Integer mode)
+    {
+        this.cacheMode = mode;
+        return this;
+    }
+
+    public Integer getCacheMode()
+    {
+        return cacheMode;
+    }
+
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/SortOrder.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/SortOrder.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d5fadfc6de3549302605b71aa24382b17121d2f
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/SortOrder.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions;
+
+import java.io.Serializable;
+
+/**
+ * @author pkupczyk
+ */
+public class SortOrder implements Serializable
+{
+
+    private boolean asc = true;
+
+    private static final long serialVersionUID = 1L;
+
+    public void asc()
+    {
+        asc = true;
+    }
+
+    public void desc()
+    {
+        asc = false;
+    }
+
+    public boolean isAsc()
+    {
+        return asc;
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialFetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialFetchOptions.java
index dc6e64caea5612b65f3d8e04241ce1f7bc3bc015..6d9ed841626358119904764665ef32adc9c06e41 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialFetchOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialFetchOptions.java
@@ -15,21 +15,20 @@
  */
 package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.FetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.history.HistoryEntryFetchOptions;
-import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialFetchOptions;
-import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material.MaterialTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.property.PropertyFetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.tag.TagFetchOptions;
 import ch.systemsx.cisd.base.annotation.JsonObject;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.io.Serializable;
 
 /**
  * Class automatically generated with {@link ch.ethz.sis.openbis.generic.shared.api.v3.dto.generators.DtoGenerator}
  */
 @JsonObject("dto.fetchoptions.material.MaterialFetchOptions")
-public class MaterialFetchOptions implements Serializable
+public class MaterialFetchOptions extends FetchOptions
 {
     private static final long serialVersionUID = 1L;
 
@@ -51,6 +50,23 @@ public class MaterialFetchOptions implements Serializable
     @JsonProperty
     private TagFetchOptions tags;
 
+    @JsonProperty
+    private MaterialSortOptions sortBy;
+
+    public MaterialSortOptions sortBy()
+    {
+        if (sortBy == null)
+        {
+            sortBy = new MaterialSortOptions();
+        }
+        return sortBy;
+    }
+
+    public MaterialSortOptions getSortBy()
+    {
+        return sortBy;
+    }
+
     // Method automatically generated with {@link ch.ethz.sis.openbis.generic.shared.api.v3.dto.generators.DtoGenerator}
     public MaterialTypeFetchOptions withType()
     {
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialSortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialSortOptions.java
new file mode 100644
index 0000000000000000000000000000000000000000..7edb895c1c1f63c5a4f853c98a4a29e01dda0468
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/material/MaterialSortOptions.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.material;
+
+import java.io.Serializable;
+
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.SortOrder;
+
+/**
+ * @author pkupczyk
+ */
+public class MaterialSortOptions implements Serializable
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private SortOrder code;
+
+    private SortOrder registrationDate;
+
+    public SortOrder code()
+    {
+        if (code == null)
+        {
+            code = new SortOrder();
+        }
+        return code;
+    }
+
+    public boolean isCode()
+    {
+        return code != null;
+    }
+
+    public SortOrder registrationDate()
+    {
+        if (registrationDate == null)
+        {
+            registrationDate = new SortOrder();
+        }
+        return registrationDate;
+    }
+
+    public boolean isRegistrationDate()
+    {
+        return registrationDate != null;
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagFetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagFetchOptions.java
index 5cddf6abaf40688fd1d54e0966167e3f7c410934..8a327f5bfe0b9793076af7b953ae1b5be0add297 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagFetchOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagFetchOptions.java
@@ -15,22 +15,40 @@
  */
 package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.tag;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.FetchOptions;
 import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.person.PersonFetchOptions;
 import ch.systemsx.cisd.base.annotation.JsonObject;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.io.Serializable;
 
 /**
  * Class automatically generated with {@link ch.ethz.sis.openbis.generic.shared.api.v3.dto.generators.DtoGenerator}
  */
 @JsonObject("dto.fetchoptions.tag.TagFetchOptions")
-public class TagFetchOptions implements Serializable
+public class TagFetchOptions extends FetchOptions
 {
     private static final long serialVersionUID = 1L;
 
     @JsonProperty
     private PersonFetchOptions owner;
 
+    @JsonProperty
+    private TagSortOptions sortBy;
+
+    public TagSortOptions sortBy()
+    {
+        if (sortBy == null)
+        {
+            sortBy = new TagSortOptions();
+        }
+        return sortBy;
+    }
+
+    public TagSortOptions getSortBy()
+    {
+        return sortBy;
+    }
+
     // Method automatically generated with {@link ch.ethz.sis.openbis.generic.shared.api.v3.dto.generators.DtoGenerator}
     public PersonFetchOptions withOwner()
     {
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java
new file mode 100644
index 0000000000000000000000000000000000000000..4842d160b61f91055b6a3379362f18651aacfa29
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/fetchoptions/tag/TagSortOptions.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.tag;
+
+import java.io.Serializable;
+
+import ch.ethz.sis.openbis.generic.shared.api.v3.dto.fetchoptions.SortOrder;
+
+/**
+ * @author pkupczyk
+ */
+public class TagSortOptions implements Serializable
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private SortOrder code;
+
+    private SortOrder registrationDate;
+
+    public SortOrder code()
+    {
+        if (code == null)
+        {
+            code = new SortOrder();
+        }
+        return code;
+    }
+
+    public boolean isCode()
+    {
+        return code != null;
+    }
+
+    public SortOrder registrationDate()
+    {
+        if (registrationDate == null)
+        {
+            registrationDate = new SortOrder();
+        }
+        return registrationDate;
+    }
+
+    public boolean isRegistrationDate()
+    {
+        return registrationDate != null;
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/Relation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/search/SearchResult.java
similarity index 73%
rename from openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/Relation.java
rename to openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/search/SearchResult.java
index 47aa8b8ab7edafe442cc6eb23387492536fdc389..d1601f66cebc1379a6bfffd10a26f47e737aeac0 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/api/v3/translator/Relation.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/shared/api/v3/dto/search/SearchResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 ETH Zuerich, Scientific IT Services
+ * Copyright 2015 ETH Zuerich, CISD
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,18 @@
  * limitations under the License.
  */
 
-package ch.ethz.sis.openbis.generic.server.api.v3.translator;
+package ch.ethz.sis.openbis.generic.shared.api.v3.dto.search;
+
+import java.util.List;
 
 /**
  * @author pkupczyk
  */
-public interface Relation
+public class SearchResult<OBJECT>
 {
 
-    public void load();
+    private List<Object> objects;
+
+    private int totalCount;
 
 }