diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java
index 2625f69ab15ef513ed5cf9f9de140d27d20e2662..0863f2b12a2acc7e71a8dc76c738a1dd3c65063a 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java
@@ -65,6 +65,7 @@ public class RelationshipService implements IRelationshipService
         RelationshipUtils.updateModificationDateAndModifier(previousProject, session);
         experiment.setProject(project);
         RelationshipUtils.updateModificationDateAndModifier(project, session);
+        RelationshipUtils.updateModificationDateAndModifier(experiment, session);
     }
 
     @Override
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
index b82887f6119a75af309da02fee3bf39a8e65a7ed..f0a40adb4a5cee950f0c478e07ebb74374848b1b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractBusinessObject.java
@@ -17,8 +17,11 @@
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.hibernate.SessionFactory;
@@ -72,7 +75,10 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Identifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityPropertyPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityPropertiesHolder;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityWithMetaprojects;
+import ch.systemsx.cisd.openbis.generic.shared.dto.IModifierAndModificationDateBean;
+import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.MetaprojectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
@@ -516,4 +522,58 @@ abstract class AbstractBusinessObject implements IDAOFactory
     {
         return daoFactory.getMetaprojectDAO();
     }
+
+    protected void updateProperties(EntityTypePE entityType, List<IEntityProperty> properties,
+            IEntityPropertiesHolder entityAsPropertiesHolder,
+            IModifierAndModificationDateBean entityAsModifiableBean)
+    {
+        Set<? extends EntityPropertyPE> existingProperties =
+                entityAsPropertiesHolder.getProperties();
+        Map<String, Object> existingPropertyValuesByCode = new HashMap<String, Object>();
+        for (EntityPropertyPE existingProperty : existingProperties)
+        {
+            String propertyCode =
+                    existingProperty.getEntityTypePropertyType().getPropertyType().getCode();
+            existingPropertyValuesByCode.put(propertyCode, getValue(existingProperty));
+        }
+        Set<? extends EntityPropertyPE> convertedProperties =
+                convertProperties(entityType, existingProperties, properties);
+        if (isEquals(existingPropertyValuesByCode, convertedProperties) == false)
+        {
+            entityAsPropertiesHolder.setProperties(convertedProperties);
+            entityAsModifiableBean.setModifier(findPerson());
+            entityAsModifiableBean.setModificationDate(new Date());
+        }
+    }
+
+    private boolean isEquals(Map<String, Object> existingPropertyValuesByCode,
+            Set<? extends EntityPropertyPE> properties)
+    {
+        for (EntityPropertyPE property : properties)
+        {
+            Object existingValue =
+                    existingPropertyValuesByCode.remove(property.getEntityTypePropertyType()
+                            .getPropertyType().getCode());
+            if (existingValue == null || existingValue.equals(getValue(property)) == false)
+            {
+                return false;
+            }
+        }
+        return existingPropertyValuesByCode.isEmpty();
+    }
+
+    private Object getValue(EntityPropertyPE property)
+    {
+        String value = property.getValue();
+        if (value != null)
+        {
+            return value;
+        }
+        MaterialPE materialValue = property.getMaterialValue();
+        if (materialValue != null)
+        {
+            return materialValue;
+        }
+        return property.getVocabularyTerm();
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
index 46c03fedb53e85a58fc7974530241bd04ac53708..710d07404c3929a5c5240b95e1497e75b7ce6f90 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBO.java
@@ -19,7 +19,6 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -30,6 +29,7 @@ import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.common.collection.CollectionUtils;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.business.IRelationshipService;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.util.RelationshipUtils;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.util.SampleUtils;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IAttachmentDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
@@ -203,6 +203,10 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
                     + identifier);
         }
         final ExperimentPE exp = tryGetExperiment(identifier, project);
+        if (exp == null)
+        {
+            throw new UserFailureException("Unkown experiment: " + identifier);
+        }
         return exp;
     }
 
@@ -341,6 +345,7 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
                 newExperiment.getProperties(), registrator);
         defineExperimentProject(newExperiment, experimentIdentifier);
         experiment.setPermId(getOrCreatePermID(newExperiment));
+        RelationshipUtils.updateModificationDateAndModifier(experiment, session);
         setMetaprojects(experiment, newExperiment.getMetaprojectsOrNull());
         dataChanged = true;
     }
@@ -370,9 +375,8 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         {
             throw UserFailureException.fromTemplate(ERR_PROJECT_NOT_FOUND, newExperiment);
         }
-        project.setModificationDate(new Date());
-        project.setModifier(findPerson());
         experiment.setProject(project);
+        RelationshipUtils.updateModificationDateAndModifier(project, session);
     }
 
     private void defineExperimentType(NewExperiment newExperiment)
@@ -457,7 +461,8 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         {
             throwModifiedEntityException("Experiment");
         }
-        updateProperties(updates.getProperties());
+        updateProperties(experiment.getEntityType(), updates.getProperties(), experiment,
+                experiment);
 
         ProjectPE project = findProject(updates.getProjectIdentifier());
         ProjectPE previousProject = experiment.getProject();
@@ -612,14 +617,6 @@ public final class ExperimentBO extends AbstractBusinessObject implements IExper
         return project;
     }
 
-    @Private
-    void updateProperties(List<IEntityProperty> properties)
-    {
-        final Set<ExperimentPropertyPE> existingProperties = experiment.getProperties();
-        final ExperimentTypePE type = experiment.getExperimentType();
-        experiment.setProperties(convertProperties(type, existingProperties, properties));
-    }
-
     @Override
     public void updateManagedProperty(IManagedProperty managedProperty)
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
index 35e4f5d9819b69e857bf1148d77f8eb9e7339fe3..be444b4859fc14a29f71042a6319f32449de8260 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/SampleBO.java
@@ -36,7 +36,6 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.sample.SampleIdenti
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.sample.SamplePermIdId;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.id.sample.SampleTechIdId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IManagedProperty;
@@ -316,7 +315,7 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
         {
             throwModifiedEntityException("Sample");
         }
-        updateProperties(updates.getProperties());
+        updateProperties(sample.getSampleType(), updates.getProperties(), sample, sample);
         spaceUpdated = updateSpace(sample, updates.getSampleIdentifier(), null);
         if (updates.isUpdateExperimentLink())
         {
@@ -334,13 +333,6 @@ public final class SampleBO extends AbstractSampleBusinessObject implements ISam
         dataChanged = true;
     }
 
-    private void updateProperties(List<IEntityProperty> properties)
-    {
-        final Set<SamplePropertyPE> existingProperties = sample.getProperties();
-        final SampleTypePE type = sample.getSampleType();
-        sample.setProperties(convertProperties(type, existingProperties, properties));
-    }
-
     private void updateParents(SampleUpdatesDTO updates)
     {
         String[] parentCodes = updates.getModifiedParentCodesOrNull();
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/ExperimentDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/ExperimentDAO.java
index d495a6a4d22de86a4188ff635058ec530fd8b0ab..16626f5983c5540d6ff96ba92b3ad7b5799d6711 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/ExperimentDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/ExperimentDAO.java
@@ -19,7 +19,6 @@ package ch.systemsx.cisd.openbis.generic.server.dataaccess.db;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 import java.util.Set;
 
@@ -422,23 +421,9 @@ public class ExperimentDAO extends AbstractGenericEntityWithPropertiesDAO<Experi
     {
         assert experiment != null : "Missing experiment.";
         experiment.setCode(CodeConverter.tryToDatabase(experiment.getCode()));
-        if (experiment.getModificationDate() == null)
-        {
-            experiment.setModificationDate(new Date());
-        }
         validatePE(experiment);
         final HibernateTemplate template = getHibernateTemplate();
         template.saveOrUpdate(experiment);
-        // Hibernate behaves as follows: If a PE bean property annotated with
-        // @OptimisticLock(excluded = true) (as it is the case for modifier and modification date)
-        // has changed the version will only be increased if a direct
-        // bean property (like project, but not properties or meta-projects) has also changed. This
-        // sounds like a bug. Thus, modifier and modification date is changed after the following
-        // flush in order to increase the version in case of changed properties or meta-projects.
-        template.flush();
-        experiment.setModifier(modifier);
-        experiment.setModificationDate(new Date());
-        template.saveOrUpdate(experiment);
         if (operationLog.isDebugEnabled())
         {
             operationLog.debug(String.format("ADD: experiment '%s'.", experiment));
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java
index 49c44a317d9a82b6c6ad96f4edcfe311e18aa93a..01c9cfcaca94532c84ce604fe85425ec984d67d0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java
@@ -86,16 +86,8 @@ public class SampleDAO extends AbstractGenericEntityWithPropertiesDAO<SamplePE>
         {
             sample.setModificationDate(new Date());
         }
-        hibernateTemplate.saveOrUpdate(sample);
-        // Hibernate behaves as follows: If a PE bean property annotated with
-        // @OptimisticLock(excluded = true) (as it is the case for modifier and modification date)
-        // has changed the version will only be increased if a direct
-        // bean property (like space, but not properties or meta-projects) has also changed. This
-        // sounds like a bug. Thus, modifier and modification date is changed after the following
-        // flush in order to increase the version in case of changed properties or meta-projects.
-        hibernateTemplate.flush();
-        sample.setModifier(modifier);
-        sample.setModificationDate(new Date());
+        // sample.setModifier(modifier);
+        // sample.setModificationDate(new Date());
         hibernateTemplate.saveOrUpdate(sample);
         if (doLog && operationLog.isInfoEnabled())
         {
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java
index 70a950cf9f313e02a9a0e26f92e995a24a6a09b9..8ddb532349aca86efe496353f9a0ec7b13263947 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExperimentBOTest.java
@@ -22,6 +22,7 @@ import static ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool.E
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -29,12 +30,14 @@ import java.util.Set;
 import org.jmock.Expectations;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.test.AssertionUtil;
 import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool;
+import ch.systemsx.cisd.openbis.generic.server.util.TimeIntervalChecker;
 import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
@@ -50,6 +53,8 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentUpdatesDTO;
 import ch.systemsx.cisd.openbis.generic.shared.dto.IAuthSession;
+import ch.systemsx.cisd.openbis.generic.shared.dto.IEntityPropertiesHolder;
+import ch.systemsx.cisd.openbis.generic.shared.dto.IModifierAndModificationDateBean;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -82,11 +87,22 @@ public final class ExperimentBOTest extends AbstractBOTest
 
     private static final String EXP_CODE = "EXP-CODE";
 
+    private IEntityPropertiesHolder entityAsPropertiesHolder;
+
+    private IModifierAndModificationDateBean entityAsModifiableBean;
+
     private final ExperimentBO createExperimentBO()
     {
         return new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, relationshipService);
     }
 
+    @BeforeMethod
+    public void setUp()
+    {
+        entityAsPropertiesHolder = context.mock(IEntityPropertiesHolder.class);
+        entityAsModifiableBean = context.mock(IModifierAndModificationDateBean.class);
+    }
+
     @Test
     public void testLoadByExperimentIdentifier() throws Exception
     {
@@ -100,13 +116,21 @@ public final class ExperimentBOTest extends AbstractBOTest
     private void prepareUpdateProperties(final Set<ExperimentPropertyPE> oldProperties,
             final List<IEntityProperty> newProperties, final EntityTypePE entityType,
             final PersonPE registrator, final List<ExperimentPropertyPE> updated)
+    {
+        final Set<ExperimentPropertyPE> newProps = new HashSet<ExperimentPropertyPE>(updated);
+        prepareUpdateProperties(oldProperties, newProperties, entityType, registrator, newProps);
+    }
+
+    private void prepareUpdateProperties(final Set<ExperimentPropertyPE> oldProperties,
+            final List<IEntityProperty> newProperties, final EntityTypePE entityType,
+            final PersonPE registrator, final Set<ExperimentPropertyPE> newProps)
     {
         context.checking(new Expectations()
             {
                 {
                     one(propertiesConverter).updateProperties(oldProperties, entityType,
                             newProperties, registrator, Collections.<String> emptySet());
-                    will(returnValue(new HashSet<ExperimentPropertyPE>(updated)));
+                    will(returnValue(newProps));
 
                 }
             });
@@ -205,6 +229,7 @@ public final class ExperimentBOTest extends AbstractBOTest
         final ProjectPE project = createProject(dbCode, groupCode, projectCode);
         final ExperimentTypePE type = createExperimentType(expTypeCode);
         final ExperimentPE experiment = createExperiment(project, expCode, type);
+        TimeIntervalChecker timeIntervalChecker = new TimeIntervalChecker();
 
         prepareAnyDaoCreation();
         context.checking(new Expectations()
@@ -233,6 +258,12 @@ public final class ExperimentBOTest extends AbstractBOTest
         experimentBO.define(newExperiment);
         experimentBO.save();
 
+        assertEquals(EXAMPLE_PERSON, experimentBO.getExperiment().getModifier());
+        assertEquals(EXAMPLE_PERSON, experimentBO.getExperiment().getProject().getModifier());
+        timeIntervalChecker
+                .assertDateInInterval(experimentBO.getExperiment().getModificationDate());
+        timeIntervalChecker.assertDateInInterval(experimentBO.getExperiment().getProject()
+                .getModificationDate());
         context.assertIsSatisfied();
     }
 
@@ -423,23 +454,223 @@ public final class ExperimentBOTest extends AbstractBOTest
                 new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
         bo.loadByExperimentIdentifier(identifier);
 
-        assertTrue(bo.getExperiment().getProperties().contains(changedProperty));
-        assertTrue(bo.getExperiment().getProperties().contains(deletedProperty));
-        assertFalse(bo.getExperiment().getProperties().contains(addedProperty));
+        ExperimentPE experiment = bo.getExperiment();
+        assertTrue(experiment.getProperties().contains(changedProperty));
+        assertTrue(experiment.getProperties().contains(deletedProperty));
+        assertFalse(experiment.getProperties().contains(addedProperty));
 
         final List<IEntityProperty> newProperties = createDummyProperties();
         prepareUpdateProperties(exp.getProperties(), newProperties, experimentType,
                 ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(),
                 Arrays.asList(changedProperty, addedProperty));
-        bo.updateProperties(newProperties);
+        bo.updateProperties(experiment.getEntityType(), newProperties, experiment, experiment);
+
+        assertTrue(experiment.getProperties().contains(changedProperty));
+        assertFalse(experiment.getProperties().contains(deletedProperty));
+        assertTrue(experiment.getProperties().contains(addedProperty));
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testUpdatePropertiesWithSameProperties()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE stringProp = CommonTestUtils.createNotesProperty(entityType);
+        ExperimentPropertyPE termProp = CommonTestUtils.createOrganProperty(entityType);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(),
+                Arrays.asList(materialProp, stringProp, termProp));
+        prepareEntity(existingProperties, null);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
 
-        assertTrue(bo.getExperiment().getProperties().contains(changedProperty));
-        assertFalse(bo.getExperiment().getProperties().contains(deletedProperty));
-        assertTrue(bo.getExperiment().getProperties().contains(addedProperty));
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testUpdatePropertiesOfUnchangedProperties()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE materialProp2 = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE stringProp = CommonTestUtils.createNotesProperty(entityType);
+        ExperimentPropertyPE stringProp2 = CommonTestUtils.createNotesProperty(entityType);
+        ExperimentPropertyPE termProp = CommonTestUtils.createOrganProperty(entityType);
+        ExperimentPropertyPE termProp2 = CommonTestUtils.createOrganProperty(entityType);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(),
+                Arrays.asList(stringProp2, materialProp2, termProp2));
+        prepareEntity(existingProperties, null);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
 
         context.assertIsSatisfied();
     }
 
+    @Test
+    public void testUpdatePropertiesOfChangedStringProperty()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE stringProp = CommonTestUtils.createStringProperty(entityType, "alpha");
+        ExperimentPropertyPE changedStringProp =
+                CommonTestUtils.createStringProperty(entityType, "beta");
+        ExperimentPropertyPE termProp = CommonTestUtils.createOrganProperty(entityType);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        final Set<ExperimentPropertyPE> newConvertedProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, changedStringProp,
+                        termProp));
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(), newConvertedProperties);
+        prepareEntity(existingProperties, newConvertedProperties);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testUpdatePropertiesOfChangedMaterialProperty()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE changedMaterialProp =
+                CommonTestUtils.createMaterialProperty(entityType, "BETA");
+        ExperimentPropertyPE stringProp = CommonTestUtils.createStringProperty(entityType, "alpha");
+        ExperimentPropertyPE termProp = CommonTestUtils.createOrganProperty(entityType);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        final Set<ExperimentPropertyPE> newConvertedProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(changedMaterialProp, stringProp,
+                        termProp));
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(), newConvertedProperties);
+        prepareEntity(existingProperties, newConvertedProperties);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testUpdatePropertiesOfChangedTermProperty()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE stringProp = CommonTestUtils.createStringProperty(entityType, "alpha");
+        ExperimentPropertyPE termProp =
+                CommonTestUtils.createTermProperty(entityType, CommonTestUtils.BRAIN);
+        ExperimentPropertyPE changedTermProp =
+                CommonTestUtils.createTermProperty(entityType, CommonTestUtils.LEG);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        final Set<ExperimentPropertyPE> newConvertedProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp,
+                        changedTermProp));
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(), newConvertedProperties);
+        prepareEntity(existingProperties, newConvertedProperties);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testUpdatePropertiesOfWithAddedTermProperty()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE stringProp = CommonTestUtils.createStringProperty(entityType, "alpha");
+        ExperimentPropertyPE termProp = CommonTestUtils.createOrganProperty(entityType);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        final Set<ExperimentPropertyPE> newConvertedProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(), newConvertedProperties);
+        prepareEntity(existingProperties, newConvertedProperties);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
+
+        context.assertIsSatisfied();
+    }
+
+    @Test
+    public void testUpdatePropertiesOfWithRemovedMaterialProperty()
+    {
+        ExperimentTypePE entityType = CommonTestUtils.createExperimentType();
+        ExperimentPropertyPE materialProp = CommonTestUtils.createMaterialProperty(entityType);
+        ExperimentPropertyPE stringProp = CommonTestUtils.createStringProperty(entityType, "alpha");
+        ExperimentPropertyPE termProp = CommonTestUtils.createOrganProperty(entityType);
+        final Set<ExperimentPropertyPE> existingProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(materialProp, stringProp, termProp));
+        List<IEntityProperty> newProperties = createDummyProperties();
+        final Set<ExperimentPropertyPE> newConvertedProperties =
+                new HashSet<ExperimentPropertyPE>(Arrays.asList(stringProp, termProp));
+        prepareUpdateProperties(existingProperties, newProperties, entityType,
+                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson(), newConvertedProperties);
+        prepareEntity(existingProperties, newConvertedProperties);
+        ExperimentBO bo =
+                new ExperimentBO(daoFactory, ManagerTestTool.EXAMPLE_SESSION, propertiesConverter);
+
+        bo.updateProperties(entityType, newProperties, entityAsPropertiesHolder,
+                entityAsModifiableBean);
+
+        context.assertIsSatisfied();
+    }
+
+    private void prepareEntity(final Set<ExperimentPropertyPE> existingProperties,
+            final Set<ExperimentPropertyPE> newConvertedPropertiesOrNull)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(entityAsPropertiesHolder).getProperties();
+                    will(returnValue(existingProperties));
+
+                    if (newConvertedPropertiesOrNull != null)
+                    {
+                        one(entityAsPropertiesHolder).setProperties(newConvertedPropertiesOrNull);
+                        one(entityAsModifiableBean).setModifier(
+                                ManagerTestTool.EXAMPLE_SESSION.tryGetPerson());
+                        one(entityAsModifiableBean).setModificationDate(with(any(Date.class)));
+                    }
+                }
+            });
+    }
+
     private List<IEntityProperty> createDummyProperties()
     {
         return new ArrayList<IEntityProperty>();
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/CommonTestUtils.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/CommonTestUtils.java
index dca8068f337b4ec050e76ac951b770a66bd68d10..1178c05c2fc5afbe6d6e0add0d7376864de4947c 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/CommonTestUtils.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/CommonTestUtils.java
@@ -16,6 +16,8 @@
 
 package ch.systemsx.cisd.openbis.generic.shared;
 
+import static ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool.EXAMPLE_SESSION;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
@@ -68,9 +70,9 @@ public class CommonTestUtils
 {
     public static final VocabularyTermPE BRAIN = createVocabularyTerm("BRAIN");
 
-    private static final VocabularyTermPE LEG = createVocabularyTerm("LEG");
+    public static final VocabularyTermPE LEG = createVocabularyTerm("LEG");
 
-    private static final VocabularyTermPE HEAD = createVocabularyTerm("HEAD");
+    public static final VocabularyTermPE HEAD = createVocabularyTerm("HEAD");
 
     public static final TechId TECH_ID = new TechId(1L);
 
@@ -137,26 +139,52 @@ public class CommonTestUtils
     }
 
     public static ExperimentPropertyPE createNotesProperty(ExperimentTypePE experimentType)
+    {
+        return createStringProperty(experimentType, "Check the impact on the hand on 03/04/2008.");
+    }
+
+    public static ExperimentPropertyPE createStringProperty(ExperimentTypePE experimentType,
+            String value)
     {
         ExperimentTypePropertyTypePE notesAssignment =
                 createAssignment(ExamplePropertyTypes.NOTES, experimentType);
         ExperimentPropertyPE deleted = new ExperimentPropertyPE();
         deleted.setEntityTypePropertyType(notesAssignment);
-        deleted.setValue("Check the impact on the hand on 03/04/2008.");
+        deleted.setValue(value);
         return deleted;
     }
 
     public static ExperimentPropertyPE createOrganProperty(ExperimentTypePE experimentType)
     {
+        return createTermProperty(experimentType, BRAIN);
+    }
 
+    public static ExperimentPropertyPE createTermProperty(ExperimentTypePE experimentType,
+            VocabularyTermPE term)
+    {
         ExperimentTypePropertyTypePE organAssignment =
                 createAssignment(ExamplePropertyTypes.INFECTED_ORGAN, experimentType);
         ExperimentPropertyPE changed = new ExperimentPropertyPE();
         changed.setEntityTypePropertyType(organAssignment);
-        changed.setVocabularyTerm(CommonTestUtils.BRAIN);
+        changed.setVocabularyTerm(term);
         return changed;
     }
 
+    public static ExperimentPropertyPE createMaterialProperty(ExperimentTypePE experimentType)
+    {
+        return createMaterialProperty(experimentType, "ABCD");
+    }
+
+    public static ExperimentPropertyPE createMaterialProperty(ExperimentTypePE experimentType,
+            String materialCode)
+    {
+        ExperimentPropertyPE property = new ExperimentPropertyPE();
+        property.setEntityTypePropertyType(createAssignment(ExamplePropertyTypes.INFECTING_VIRUS,
+                experimentType));
+        property.setMaterialValue(createMaterial(VIRUS, materialCode));
+        return property;
+    }
+
     public static ExperimentTypePropertyTypePE createAssignment(PropertyTypePE propertyType,
             ExperimentTypePE type)
     {
@@ -371,6 +399,16 @@ public class CommonTestUtils
         return attachmentContentPE;
     }
 
+    public static MaterialPE createMaterial(MaterialTypePE materialType, String code)
+    {
+        final MaterialPE material = new MaterialPE();
+        material.setCode(code);
+        material.setMaterialType(materialType);
+        material.setDatabaseInstance(CommonTestUtils.createHomeDatabaseInstance());
+        material.setRegistrator(EXAMPLE_SESSION.tryGetPerson());
+        return material;
+    }
+
     public static MaterialTypePE createMaterialType()
     {
         return createMaterialType(MATERIAL_TYPE_VIRUS);
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/optimistic_locking/ExperimentOptimisticLockingTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/optimistic_locking/ExperimentOptimisticLockingTest.java
index 6e259f5358857806302da62a7ae307fa28ab2296..3f027e6a971e1581f1cabd702f85bfb0aba64f8d 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/optimistic_locking/ExperimentOptimisticLockingTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/optimistic_locking/ExperimentOptimisticLockingTest.java
@@ -214,6 +214,7 @@ public class ExperimentOptimisticLockingTest extends OptimisticLockingTestCase
         Experiment experiment = toolBox.createAndLoadExperiment(1);
         toolBox.createAndLoadSample(1, experiment);
         ExperimentUpdatesDTO update = new ExperimentUpdatesDTO();
+        update.setVersion(toolBox.loadExperiment(experiment).getVersion());
         update.setExperimentId(new TechId(experiment));
         update.setSampleCodes(new String[0]);
         update.setProperties(ToolBox.NO_PROPERTIES);