diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index cac7bb848dff08faec3354d02c386fe0d07b97fa..3dcf65ef7eceefb90f907cf00c2d67c0e163ae40 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -51,7 +51,6 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.IExternalDataTable;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IGridCustomFilterOrColumnBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IGroupBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialBO;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.IMaterialTable;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IProjectBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IPropertyTypeBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IPropertyTypeTable;
@@ -60,6 +59,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IVocabularyBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IVocabularyTermBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.datasetlister.IDatasetLister;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister.IMaterialLister;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityTypeDAO;
@@ -141,7 +141,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.FileFormatTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.GridCustomFilterPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityInformationHolderDTO;
-import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.NewRoleAssignment;
@@ -167,7 +166,6 @@ import ch.systemsx.cisd.openbis.generic.shared.translator.DtoConverters;
 import ch.systemsx.cisd.openbis.generic.shared.translator.ExperimentTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.ExternalDataTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.GroupTranslator;
-import ch.systemsx.cisd.openbis.generic.shared.translator.MaterialTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.MaterialTypeTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.PersonTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.ProjectTranslator;
@@ -852,11 +850,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
     public List<Material> listMaterials(String sessionToken, MaterialType materialType)
     {
         final Session session = getSession(sessionToken);
-        final IMaterialTable materialTable = businessObjectFactory.createMaterialTable(session);
-        materialTable.load(materialType.getCode());
-        final List<MaterialPE> materials = materialTable.getMaterials();
-        Collections.sort(materials);
-        return MaterialTranslator.translate(materials);
+        final IMaterialLister materialLister = businessObjectFactory.createMaterialLister(session);
+        return materialLister.list(materialType);
     }
 
     public void registerSampleType(String sessionToken, SampleType entityType)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java
index 437489966c92f341eb463e8b687780ffb5220204..52753e19928f89ea44b1c172ccf6867e94be2f32 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IMaterialTable.java
@@ -34,6 +34,7 @@ public interface IMaterialTable
      * 
      * @param materialTypeCodeOrNull the material type code or <code>null</code>.
      */
+    @Deprecated
     public void load(String materialTypeCodeOrNull);
 
     /** Returns the loaded {@link MaterialPE}. */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java
index 0418e32ed690243f66256e9d6e93dfd6bab2c0f6..243a56659a64ac84ef9b43814fe20dbf465c63f1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java
@@ -33,8 +33,6 @@ public interface IDatasetLister
     /** @return datasets connected to the experiment with the specified id */
     List<ExternalData> listByExperimentTechId(TechId experimentId);
 
-    // TODO 2009-09-10, Piotr Buczek: write tests
-
     /**
      * @return datasets connected to the sample with the specified id
      * @param showOnlyDirectlyConnected whether to return only directly connected datasets, or also
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialLister.java
index 0ec60bdefc3c3680cf303193d7daf12134ad4dca..3fda0e9baedbc9d25201d250729f80fa0375af2e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialLister.java
@@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister;
 import java.util.List;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
 
 /**
  * A class for fast material listing.
@@ -29,4 +30,9 @@ public interface IMaterialLister
 {
     /** fetches and sets all properties of specified materials */
     void enrichWithProperties(List<Material> materials);
+
+    /**
+     * Returns a sorted list of {@link Material}s of given {@link MaterialType}.
+     */
+    public List<Material> list(MaterialType materialType);
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialListingQuery.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialListingQuery.java
index c07f003cc3a96508b98f894d87f2096178a564b0..759d98ca45abe2519a9afc3e9dc3a9cf91192beb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialListingQuery.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/IMaterialListingQuery.java
@@ -42,6 +42,16 @@ public interface IMaterialListingQuery extends TransactionQuery, IPropertyListin
 {
     public static final int FETCH_SIZE = 1000;
 
+    /**
+     * Returns the materials for the given <var>materialTypeId</var>
+     */
+    @Select(sql = "select m.id, m.code, m.dbin_id, m.maty_id, "
+            + "           m.registration_timestamp, m.pers_id_registerer "
+            + "      from materials m where m.dbin_id=?{1} and m.maty_id=?{2} "
+            + "  order by m.code", fetchSize = FETCH_SIZE)
+    public DataIterator<MaterialRecord> getMaterialsForMaterialType(long dbInstanceId,
+            long materialTypeId);
+
     //
     // Entity Properties
     //
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialLister.java
index b652b1130b90b263b5176a0aaa4eaa4c580196fb..09af3162c3cdbc9d3f3096fff294a07c7a1ae3fa 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialLister.java
@@ -16,6 +16,7 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister;
 
+import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
 import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 
@@ -28,20 +29,49 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.common.IEntityPropert
 import ch.systemsx.cisd.openbis.generic.server.business.bo.common.IEntityPropertiesHolderResolver;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.common.entity.SecondaryEntityDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Person;
 
 /**
- * Fast bd operations on material table.
+ * Fast DB operations on material table.
  * 
  * @author Tomasz Pylak
+ * @author Piotr Buczek
  */
 @Friend(toClasses =
     { IMaterialListingQuery.class })
 public class MaterialLister implements IMaterialLister
 {
+
+    //
+    // Input
+    //
+
+    private final long databaseInstanceId;
+
+    private final DatabaseInstance databaseInstance;
+
+    //
+    // Working interfaces
+    //
+
+    private final IMaterialListingQuery query;
+
     private final IEntityPropertiesEnricher propertiesEnricher;
 
+    private SecondaryEntityDAO referencedEntityDAO;
+
+    //
+    // Working data structures
+    //
+
+    private final Long2ObjectMap<Person> persons = new Long2ObjectOpenHashMap<Person>();
+
+    //
+
     public static IMaterialLister create(IDAOFactory daoFactory, String baseIndexURL)
     {
         MaterialListerDAO dao = MaterialListerDAO.create(daoFactory);
@@ -56,13 +86,107 @@ public class MaterialLister implements IMaterialLister
         IMaterialListingQuery query = dao.getQuery();
         EntityPropertiesEnricher propertiesEnricher =
                 new EntityPropertiesEnricher(query, dao.getPropertySetQuery());
-        return new MaterialLister(propertiesEnricher);
+        return new MaterialLister(dao.getDatabaseInstanceId(), dao.getDatabaseInstance(), query,
+                propertiesEnricher, referencedEntityDAO);
     }
 
     // For unit tests
-    MaterialLister(IEntityPropertiesEnricher propertiesEnricher)
+    MaterialLister(final long databaseInstanceId, DatabaseInstance databaseInstance,
+            final IMaterialListingQuery query, IEntityPropertiesEnricher propertiesEnricher,
+            SecondaryEntityDAO referencedEntityDAO)
     {
+        assert query != null;
+
+        this.databaseInstanceId = databaseInstanceId;
+        this.databaseInstance = databaseInstance;
+        this.query = query;
         this.propertiesEnricher = propertiesEnricher;
+        this.referencedEntityDAO = referencedEntityDAO;
+    }
+
+    //
+    // Listing
+    //
+
+    public List<Material> list(MaterialType materialType)
+    {
+        return enrichMaterials(query.getMaterialsForMaterialType(databaseInstanceId, materialType
+                .getId()), materialType);
+    }
+
+    //
+    // Enriching
+    //
+
+    private List<Material> enrichMaterials(Iterable<MaterialRecord> materials,
+            MaterialType materialType)
+    {
+        List<MaterialRecord> materialRecords = asList(materials);
+        final Long2ObjectMap<Material> materialMap = createMaterials(materialRecords, materialType);
+        enrichWithProperties(materialMap);
+        return asList(materialMap);
+    }
+
+    private Long2ObjectMap<Material> createMaterials(Iterable<MaterialRecord> records,
+            MaterialType materialType)
+    {
+        Long2ObjectMap<Material> materials = new Long2ObjectOpenHashMap<Material>();
+        for (MaterialRecord record : records)
+        {
+            materials.put(record.id, createMaterial(record, materialType));
+        }
+        return materials;
+    }
+
+    private Material createMaterial(MaterialRecord record, MaterialType materialType)
+    {
+        Material material = new Material();
+        material.setId(record.id);
+        material.setCode(escapeHtml(record.code));
+
+        assert record.maty_id == materialType.getId();
+        material.setMaterialType(materialType);
+        assert record.dbin_id == databaseInstanceId;
+        material.setDatabaseInstance(databaseInstance);
+
+        material.setRegistrator(getOrCreateRegistrator(record));
+        material.setRegistrationDate(record.registration_timestamp);
+        material.setProperties(new ArrayList<IEntityProperty>());
+
+        return material;
+    }
+
+    private Person getOrCreateRegistrator(MaterialRecord row)
+    {
+        return getOrCreateRegistrator(row.pers_id_registerer);
+    }
+
+    private Person getOrCreateRegistrator(long personId)
+    {
+        Person registrator = persons.get(personId);
+        if (registrator == null)
+        {
+            registrator = referencedEntityDAO.getPerson(personId);
+            persons.put(personId, registrator);
+        }
+        return registrator;
+    }
+
+    private static <T> List<T> asList(Iterable<T> items)
+    {
+        List<T> result = new ArrayList<T>();
+        for (T item : items)
+        {
+            result.add(item);
+        }
+        return result;
+    }
+
+    private static <T> List<T> asList(Long2ObjectMap<T> items)
+    {
+        List<T> result = new ArrayList<T>();
+        org.apache.commons.collections.CollectionUtils.addAll(result, items.values().iterator());
+        return result;
     }
 
     private void enrichWithProperties(final Long2ObjectMap<Material> resultMap)
@@ -99,4 +223,5 @@ public class MaterialLister implements IMaterialLister
             material.setProperties(new ArrayList<IEntityProperty>());
         }
     }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialRecord.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialRecord.java
new file mode 100644
index 0000000000000000000000000000000000000000..150838de9b591de4bbf0221fc2563aed5cc541c7
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/materiallister/MaterialRecord.java
@@ -0,0 +1,28 @@
+package ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister;
+
+import java.util.Date;
+
+import ch.rinn.restrictions.Private;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.common.CodeRecord;
+
+/**
+ * A record object representing one row of the material table.
+ */
+// CREATE TABLE materials (
+// id tech_id NOT NULL,
+// code code NOT NULL,
+// maty_id tech_id NOT NULL,
+// pers_id_registerer tech_id NOT NULL,
+// registration_timestamp time_stamp_dfl NOT NULL DEFAULT now(),
+// dbin_id tech_id NOT NULL);
+@Private
+public class MaterialRecord extends CodeRecord
+{
+    public Long dbin_id;
+
+    public long maty_id;
+
+    public long pers_id_registerer;
+
+    public Date registration_timestamp;
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMaterialDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMaterialDAO.java
index 79c33db9d2d1f2b5ebf18b7a0f420f4d3d6593aa..969a472c0f3d65501763531361986179f42d801c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMaterialDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IMaterialDAO.java
@@ -33,6 +33,7 @@ public interface IMaterialDAO extends IGenericDAO<MaterialPE>
     /**
      * Lists materials of given type. Fetches also properties and inhibitor.
      */
+    @Deprecated
     public List<MaterialPE> listMaterialsWithPropertiesAndInhibitor(MaterialTypePE type);
 
     /** Inserts given {@link MaterialPE}s into the database. */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MaterialType.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MaterialType.java
index 67bf774740e0f04fe87a098cae63e6a24dee28fa..8b35fafe8d05242d1fae9f5e0c38f53c7d693a0b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MaterialType.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/MaterialType.java
@@ -27,8 +27,20 @@ public class MaterialType extends EntityType
 {
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
+    private Long id;
+
     private List<MaterialTypePropertyType> materialTypePropertyTypes;
 
+    public Long getId()
+    {
+        return id;
+    }
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
     @Override
     public List<MaterialTypePropertyType> getAssignedPropertyTypes()
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/MaterialTypeTranslator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/MaterialTypeTranslator.java
index 239a59c0b4f63a565aaf8c0fe971409592afa1ae..aa28e3b7f77df291b8e64c1366a9a2ccdcbd010f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/MaterialTypeTranslator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/MaterialTypeTranslator.java
@@ -57,6 +57,7 @@ public class MaterialTypeTranslator
             return null;
         }
         final MaterialType result = new MaterialType();
+        result.setId(HibernateUtils.getId(entityTypeOrNull));
         result.setCode(StringEscapeUtils.escapeHtml(entityTypeOrNull.getCode()));
         result.setDescription(StringEscapeUtils.escapeHtml(entityTypeOrNull.getDescription()));
         result.setDatabaseInstance(DatabaseInstanceTranslator.translate(entityTypeOrNull
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
index 74154eaadc436e14d5cfef4c5e9be92c6135aaac..e9f44ecc77b6ad3c6c78085374c2c7423ad59e23 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/server/GenericServer.java
@@ -348,7 +348,6 @@ public final class GenericServer extends AbstractServer<IGenericServer> implemen
                         new SampleIdentifier(new GroupIdentifier(experimentIdentifierOrNull
                                 .getDatabaseInstanceCode(), experimentIdentifierOrNull
                                 .getGroupCode()), oldSampleIdentifier.getSampleCode());
-                // TODO 2009-12-08, Piotr Buczek: does it work well with subcodes?
             } else
             {
                 // no experiment - leave sample identifier unchanged