diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bebc70ca8ade4d56695dca9e9c47379dcd90758..f0143f3d049d95f9f66034772e847d6ecdc34155 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,7 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   [README.md](misc/move_files/README.md).
 - LDAP server may now be given and may be different from LDAP domain. See
   `misc/pam_authentication/ldap.conf`
-- #47 - Sub-properties can now be queried, such as in `SELECT window.width FROM house`.
+- #47 - Sub-properties can now be queried, such as in
+  `SELECT window.width FROM house`.
+- Added support for versioning, if it is enabled on the backend.
 
 
 ### Changed
diff --git a/conf/core/cache.ccf b/conf/core/cache.ccf
index 821e5d7862efb21e0aa13f8410886c6c14b10a7c..b4e1f93596a6170ef04ec373dc7a8d70e51fedcc 100644
--- a/conf/core/cache.ccf
+++ b/conf/core/cache.ccf
@@ -28,6 +28,8 @@ jcs.region.BACKEND_JobRules.cacheattributes.MaxObjects=103
 jcs.region.BACKEND_SparseEntities
 jcs.region.BACKEND_SparseEntities.cacheattributes.MaxObjects=1002
 
+jcs.region.BACKEND_RetrieveFullVersionInfo
+jcs.region.BACKEND_RetrieveFullVersionInfo.cacheattributes.MaxObjects=1006
 
 # PAM UserSource Caching: Cached Items expire after 60 seconds if they are not requested (idle) and after 600 seconds max.
 # PAM_UnixUserGroups
diff --git a/conf/core/server.conf b/conf/core/server.conf
index d30d82b681a185a820783c6c2d8d81a62981eb64..3507886b0475341b06a8d676845e9280c67b991b 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -67,7 +67,7 @@ MYSQL_USER_NAME=caosdb
 # Password for the user
 MYSQL_USER_PASSWORD=caosdb
 # Schema of mysql procedures and tables which is required by this CaosDB instance
-MYSQL_SCHEMA_VERSION=v3.0.0-rc1
+MYSQL_SCHEMA_VERSION=v3.0.0-rc2
 
 
 # --------------------------------------------------
@@ -181,3 +181,4 @@ CHECK_ENTITY_ACL_ROLES_MODE=MUST
 # part of any Entity ACL.
 GLOBAL_ENTITY_PERMISSIONS_FILE=./conf/core/global_entity_permissions.xml
 
+ENTITY_VERSIONING_ENABLED=true
diff --git a/pom.xml b/pom.xml
index 0594c0548331f0e834fc0bc10a61fec69ffa9a69..65174bd706023526d6aba948cf890dd2e73dc3bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -100,7 +100,7 @@
     <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
-      <version>6.0.6</version>
+       <version>8.0.19</version>
     </dependency>
     <dependency>
       <groupId>org.xerial</groupId>
@@ -150,7 +150,7 @@
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-jcs-core</artifactId>
-      <version>2.1</version>
+      <version>2.2.1</version>
     </dependency>
     <dependency>
       <groupId>org.kohsuke</groupId>
diff --git a/src/main/java/caosdb/datetime/UTCDateTime.java b/src/main/java/caosdb/datetime/UTCDateTime.java
index 215de67befe403aa6a202ed178244b3de898d23e..db66ef0ad08abf0d52a8f3b1b97c6a20cc613c8a 100644
--- a/src/main/java/caosdb/datetime/UTCDateTime.java
+++ b/src/main/java/caosdb/datetime/UTCDateTime.java
@@ -296,7 +296,7 @@ public class UTCDateTime implements Interval {
     throw new NullPointerException("toString method!!!");
   }
 
-  public static UTCDateTime UTCSeconds(final Long utcseconds, final Integer nanosecond) {
+  public static UTCDateTime UTCSeconds(final Long utcseconds, final Integer nanoseconds) {
     if (LEAP_SECONDS.isEmpty()) {
       initLeapSeconds();
     }
@@ -310,10 +310,10 @@ public class UTCDateTime implements Interval {
     if (leapSeconds2 != leapSeconds && LEAP_SECONDS.contains(systemSeconds)) {
       gc.add(Calendar.SECOND, -1);
       return new UTCDateTime(
-          systemSeconds, leapSeconds, nanosecond, new LeapSecondDateTimeStringStrategy(gc, 1));
+          systemSeconds, leapSeconds, nanoseconds, new LeapSecondDateTimeStringStrategy(gc, 1));
     } else {
       return new UTCDateTime(
-          systemSeconds, leapSeconds, nanosecond, new GregorianCalendarDateTimeStringStrategy(gc));
+          systemSeconds, leapSeconds, nanoseconds, new GregorianCalendarDateTimeStringStrategy(gc));
     }
   }
 
diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java
index 45da4eb0e655f011e06deede49eec2035745fac1..cdee262f08836071d19e5a75038f4291a917b1ff 100644
--- a/src/main/java/caosdb/server/CaosDBServer.java
+++ b/src/main/java/caosdb/server/CaosDBServer.java
@@ -64,7 +64,6 @@ import caosdb.server.transaction.ChecksumUpdater;
 import caosdb.server.utils.FileUtils;
 import caosdb.server.utils.Initialization;
 import caosdb.server.utils.NullPrintStream;
-import caosdb.server.utils.Utils;
 import java.io.BufferedReader;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -75,6 +74,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Properties;
 import java.util.TimeZone;
+import java.util.UUID;
 import java.util.logging.Handler;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
@@ -875,7 +875,7 @@ class CaosDBComponent extends Component {
   public void handle(final Request request, final Response response) {
     long t1 = System.currentTimeMillis();
     // The server request ID is just a long random number
-    request.getAttributes().put("SRID", Utils.getUID());
+    request.getAttributes().put("SRID", UUID.randomUUID().toString());
     response.setServerInfo(CaosDBServer.getServerInfo());
     super.handle(request, response);
     log(request, response, t1);
diff --git a/src/main/java/caosdb/server/caching/JCSCacheHelper.java b/src/main/java/caosdb/server/caching/JCSCacheHelper.java
index 1ce455a949bcfb76a36815b3b14bcea89e676a4a..c471ed7ef860d902b56940da52a876a44fd97218 100644
--- a/src/main/java/caosdb/server/caching/JCSCacheHelper.java
+++ b/src/main/java/caosdb/server/caching/JCSCacheHelper.java
@@ -5,7 +5,7 @@
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
  * Copyright (C) 2019 IndiScale GmbH
- * Copyright (C) 2019 Timm Fitschen (t.fitschen@indiscale.com)
+ * Copyright (C) 2019,2020 Timm Fitschen (t.fitschen@indiscale.com)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -88,6 +88,9 @@ public class JCSCacheHelper implements CacheHelper {
       }
       logger.info("Configuring JCS Caching with {}", config);
     }
+
+    // If the JCS config is updated/reset, it has to be shut down before.
+    JCS.shutdown();
     JCS.setConfigProperties(config);
   }
 
diff --git a/src/main/java/caosdb/server/database/BackendTransaction.java b/src/main/java/caosdb/server/database/BackendTransaction.java
index 14e65e86bd9fe05b074c731e27b601fa4ed51f68..55987005d2c8c76f432cb12065f45b598075ec9c 100644
--- a/src/main/java/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/caosdb/server/database/BackendTransaction.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH
+ * Copyright (C) 2020 Timm Fitschen (t.fitschen@indiscale.com)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -58,6 +60,7 @@ import caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveRole;
 import caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveSparseEntity;
 import caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveTransactionHistory;
 import caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveUser;
+import caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveVersionHistory;
 import caosdb.server.database.backend.implementation.MySQL.MySQLRuleLoader;
 import caosdb.server.database.backend.implementation.MySQL.MySQLSetFileCheckedTimestampImpl;
 import caosdb.server.database.backend.implementation.MySQL.MySQLSetPassword;
@@ -113,6 +116,7 @@ import caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
 import caosdb.server.database.backend.interfaces.RetrieveSparseEntityImpl;
 import caosdb.server.database.backend.interfaces.RetrieveTransactionHistoryImpl;
 import caosdb.server.database.backend.interfaces.RetrieveUserImpl;
+import caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
 import caosdb.server.database.backend.interfaces.RuleLoaderImpl;
 import caosdb.server.database.backend.interfaces.SetFileCheckedTimestampImpl;
 import caosdb.server.database.backend.interfaces.SetPasswordImpl;
@@ -204,6 +208,7 @@ public abstract class BackendTransaction implements Undoable {
       setImpl(
           RetrieveQueryTemplateDefinitionImpl.class, MySQLRetrieveQueryTemplateDefinition.class);
       setImpl(InsertEntityDatatypeImpl.class, MySQLInsertEntityDatatype.class);
+      setImpl(RetrieveVersionHistoryImpl.class, MySQLRetrieveVersionHistory.class);
     }
   }
 
diff --git a/src/main/java/caosdb/server/database/CacheableBackendTransaction.java b/src/main/java/caosdb/server/database/CacheableBackendTransaction.java
index fc4c9659d9a8ea80393bebad945bd6badf9580c4..0131b740d81cbd41d1b9263c5f42029e1af4539c 100644
--- a/src/main/java/caosdb/server/database/CacheableBackendTransaction.java
+++ b/src/main/java/caosdb/server/database/CacheableBackendTransaction.java
@@ -4,8 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2019 IndiScale GmbH
- * Copyright (C) 2019 Timm Fitschen (t.fitschen@indiscale.com)
+ * Copyright (C) 2019,2020 IndiScale GmbH
+ * Copyright (C) 2019,2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -51,7 +51,7 @@ public abstract class CacheableBackendTransaction<K, V extends Serializable>
   private final V execute(final K key) throws TransactionException {
     // get from cache if possible...
     if (cacheIsEnabled() && key != null) {
-      final V cached = getCache().get(getKey());
+      final V cached = getCache().get(key);
       if (cached != null) {
         this.cached = true;
         return cached;
@@ -64,7 +64,7 @@ public abstract class CacheableBackendTransaction<K, V extends Serializable>
     if (notCached != null) {
       if (cacheIsEnabled() && key != null) {
         // now cache if possible
-        getCache().put(getKey(), notCached);
+        getCache().put(key, notCached);
       }
     }
     return notCached;
diff --git a/src/main/java/caosdb/server/database/DatabaseUtils.java b/src/main/java/caosdb/server/database/DatabaseUtils.java
index 84c38fa448b7d8a1035aa28ded7bbfcecc5a88b3..9edc0347abf8b63de0ce76ead06fb1f6883332e1 100644
--- a/src/main/java/caosdb/server/database/DatabaseUtils.java
+++ b/src/main/java/caosdb/server/database/DatabaseUtils.java
@@ -173,12 +173,7 @@ public class DatabaseUtils {
     while (rs.next()) {
       final FlatProperty fp = new FlatProperty();
       fp.id = rs.getInt("PropertyID");
-
-      final String v = bytes2UTF8(rs.getBytes("PropertyValue"));
-      if (v != null) {
-        fp.value = v;
-      }
-
+      fp.value = bytes2UTF8(rs.getBytes("PropertyValue"));
       fp.status = bytes2UTF8(rs.getBytes("PropertyStatus"));
       fp.idx = rs.getInt("PropertyIndex");
       ret.add(fp);
@@ -217,12 +212,14 @@ public class DatabaseUtils {
     ret.datatype = bytes2UTF8(rs.getBytes("Datatype"));
     ret.collection = bytes2UTF8(rs.getBytes("Collection"));
 
-    final String path = bytes2UTF8(rs.getBytes("FilePath"));
-    if (!rs.wasNull()) {
-      ret.filePath = path;
-      ret.fileSize = rs.getLong("FileSize");
-      ret.fileHash = bytes2UTF8(rs.getBytes("FileHash"));
-    }
+    ret.filePath = bytes2UTF8(rs.getBytes("FilePath"));
+    ret.fileSize = rs.getLong("FileSize");
+    ret.fileHash = bytes2UTF8(rs.getBytes("FileHash"));
+
+    ret.version = bytes2UTF8(rs.getBytes("Version"));
+    ret.versionSeconds = rs.getLong("VersionSeconds");
+    ret.versionNanos = rs.getInt("VersionNanos");
+
     return ret;
   }
 
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java
index 9700daf3a99b30e42baf8580f863998d7efe67a1..fc17dd1066cfa57660d44dbb6a521d3177e5fc72 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java
@@ -85,8 +85,7 @@ class DatabaseConnectionPool {
             + CaosDBServer.getServerProperty(ServerProperties.KEY_MYSQL_PORT)
             + "/"
             + CaosDBServer.getServerProperty(ServerProperties.KEY_MYSQL_DATABASE_NAME)
-            + "?noAccessToProcedureBodies=true&cacheCallableStmts=true&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8_unicode_ci&characterSetResults=utf8&serverTimezone=CET";
-    // + "?profileSQL=true&characterSetResults=utf8";
+            + "?noAccessToProcedureBodies=true&autoReconnect=true&serverTimezone=UTC&characterEncoding=UTF-8";
     final String user = CaosDBServer.getServerProperty(ServerProperties.KEY_MYSQL_USER_NAME);
     final String pwd = CaosDBServer.getServerProperty(ServerProperties.KEY_MYSQL_USER_PASSWORD);
     final ConnectionPool pool = new ConnectionPool("MySQL Pool", 2, 5, 0, 0, url, user, pwd);
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java
index 458823167423893b6defb166f013618be3a796d7..b443c54507e415ed4171fcf0ac99c3da762fb506 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -22,15 +24,20 @@
  */
 package caosdb.server.database.backend.implementation.MySQL;
 
+import caosdb.server.accessControl.Principal;
 import caosdb.server.database.misc.DBHelper;
+import caosdb.server.transaction.ChecksumUpdater;
 import caosdb.server.transaction.TransactionInterface;
 import caosdb.server.transaction.WriteTransaction;
-import caosdb.server.utils.Info;
-import caosdb.server.utils.Initialization;
+import java.io.UnsupportedEncodingException;
+import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.HashMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Provides cached statements for a MySQL back-end.
@@ -41,23 +48,71 @@ public class MySQLHelper implements DBHelper {
 
   private Connection connection = null;
 
+  private Logger logger = LoggerFactory.getLogger(getClass());
+
+  /**
+   * Initialize a transaction by calling the corresponding SQL procedure.
+   *
+   * <p>In the database, this adds a row to the transaction table with SRID, user and timestamp.
+   */
+  public void initTransaction(Connection connection, WriteTransaction<?> transaction)
+      throws SQLException {
+    try (CallableStatement call = connection.prepareCall("CALL set_transaction(?,?,?,?,?)")) {
+
+      String username = ((Principal) transaction.getTransactor().getPrincipal()).getUsername();
+      String realm = ((Principal) transaction.getTransactor().getPrincipal()).getRealm();
+      long seconds = transaction.getTimestamp().getUTCSeconds();
+      int nanos = transaction.getTimestamp().getNanoseconds();
+      byte[] srid = transaction.getSRID().getBytes("UTF-8");
+
+      call.setBytes(1, srid);
+      call.setString(2, username);
+      call.setString(3, realm);
+      call.setLong(4, seconds);
+      call.setInt(5, nanos);
+      call.execute();
+
+    } catch (UnsupportedEncodingException e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+  }
+
+  public Connection initConnection(TransactionInterface transaction)
+      throws ConnectionException, SQLException {
+    Connection connection;
+    connection = DatabaseConnectionPool.getConnection();
+
+    if (transaction instanceof ChecksumUpdater) {
+      connection.setReadOnly(false);
+      connection.setAutoCommit(false);
+    } else if (transaction instanceof WriteTransaction) {
+      connection.setReadOnly(false);
+      connection.setAutoCommit(false);
+      initTransaction(connection, (WriteTransaction<?>) transaction);
+    } else {
+      connection.setReadOnly(false);
+      connection.setAutoCommit(true);
+    }
+
+    return connection;
+  }
+
   public Connection getConnection() throws SQLException, ConnectionException {
     if (this.connection == null) {
-      this.connection = DatabaseConnectionPool.getConnection();
-      if (this.transaction instanceof WriteTransaction) {
-        this.connection.setReadOnly(false);
-        this.connection.setAutoCommit(false);
-      } else if (this.transaction instanceof Initialization || this.transaction instanceof Info) {
-        this.connection.setReadOnly(false);
-        this.connection.setAutoCommit(true);
-      } else {
-        this.connection.setReadOnly(false);
-        this.connection.setAutoCommit(true);
-      }
+      this.connection = initConnection(this.transaction);
     }
     return this.connection;
   }
 
+  /**
+   * Prepare a statement from a string. Reuse prepared statements from the cache if available.
+   *
+   * @param statement
+   * @return
+   * @throws SQLException
+   * @throws ConnectionException
+   */
   public PreparedStatement prepareStatement(final String statement)
       throws SQLException, ConnectionException {
     if (this.stmtCache.containsKey(statement)) {
@@ -81,6 +136,7 @@ public class MySQLHelper implements DBHelper {
 
   private TransactionInterface transaction = null;
 
+  /** Make all changes permanent. */
   @Override
   public void commit() throws SQLException {
     if (this.connection != null
@@ -90,13 +146,20 @@ public class MySQLHelper implements DBHelper {
     }
   }
 
+  /**
+   * Reset SRID variable, close all statements, roll back to last save point and close connection.
+   */
   @Override
   public void cleanUp() {
 
-    // close all statements (if necessary), roll back to last save point (if
-    // possible) and close connection (if necessary).
     try {
       if (this.connection != null && !this.connection.isClosed()) {
+        try (Statement s = connection.createStatement()) {
+          s.execute("SET @SRID = NULL");
+        } catch (SQLException e) {
+          logger.error("Exception while resetting the @SRID variable.", e);
+        }
+
         // close all cached statements (if possible)
         for (final PreparedStatement stmt : this.stmtCache.values()) {
           try {
@@ -104,7 +167,7 @@ public class MySQLHelper implements DBHelper {
               stmt.close();
             }
           } catch (final SQLException e) {
-            e.printStackTrace();
+            logger.warn("Exception while closing a prepared statement.", e);
           }
         }
 
@@ -113,12 +176,12 @@ public class MySQLHelper implements DBHelper {
             this.connection.rollback();
           }
         } catch (final SQLException r) {
-          r.printStackTrace();
+          logger.warn("Exception during roll-back attempt.", r);
         }
         this.connection.close();
       }
     } catch (final SQLException e) {
-      e.printStackTrace();
+      logger.warn("Exception during clean-up.", e);
     }
 
     // clear everything
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
index 4ad9f5b6a3cc3e51c05bc83bbc9332807b79484d..97cfde68b1b9097152aca3ea68705e6cdfddfaad 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
@@ -22,6 +22,7 @@
  */
 package caosdb.server.database.backend.implementation.MySQL;
 
+import caosdb.server.database.DatabaseUtils;
 import caosdb.server.database.access.Access;
 import caosdb.server.database.backend.interfaces.InsertSparseEntityImpl;
 import caosdb.server.database.exceptions.IntegrityException;
@@ -56,6 +57,7 @@ public class MySQLInsertSparseEntity extends MySQLTransaction implements InsertS
       try (final ResultSet rs = insertEntityStmt.executeQuery()) {
         if (rs.next()) {
           entity.id = rs.getInt("EntityID");
+          entity.version = DatabaseUtils.bytes2UTF8(rs.getBytes("Version"));
         } else {
           throw new TransactionException("Didn't get new EntityID back.");
         }
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java
index c1c430cfee6e7a6b88391aa02df072e918f8d692..721ba566ed389732456342aab78691c171b33922 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java
@@ -30,6 +30,7 @@ import caosdb.server.database.proto.VerySparseEntity;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Types;
 import java.util.ArrayList;
 
 public class MySQLRetrieveParents extends MySQLTransaction implements RetrieveParentsImpl {
@@ -38,16 +39,22 @@ public class MySQLRetrieveParents extends MySQLTransaction implements RetrievePa
     super(access);
   }
 
-  private static final String stmtStr = "call retrieveEntityParents(?)";
+  private static final String stmtStr = "call retrieveEntityParents(?, ?)";
 
   @Override
-  public ArrayList<VerySparseEntity> execute(final Integer id) throws TransactionException {
+  public ArrayList<VerySparseEntity> execute(final Integer id, final String version)
+      throws TransactionException {
     try {
       ResultSet rs = null;
       try {
         final PreparedStatement prepareStatement = prepareStatement(stmtStr);
 
         prepareStatement.setInt(1, id);
+        if (version == null) {
+          prepareStatement.setNull(2, Types.VARBINARY);
+        } else {
+          prepareStatement.setString(2, version);
+        }
         rs = prepareStatement.executeQuery();
         return DatabaseUtils.parseParentResultSet(rs);
       } finally {
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
index 8b9a547bac69cf221dbf7c204a93e95326bb484c..9b6704c4cb3e6395c5b8239a3aefc34bb818e937 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java
@@ -31,6 +31,7 @@ import caosdb.server.database.proto.ProtoProperty;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Types;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -40,15 +41,17 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
     super(access);
   }
 
-  private static final String stmtStr = "call retrieveEntityProperties(?,?)";
-  private static final String stmtStr2 = "call retrieveOverrides(?,?)";
+  private static final String stmtStr = "call retrieveEntityProperties(?,?,?)";
+  private static final String stmtStr2 = "call retrieveOverrides(?,?,?)";
 
   @Override
-  public ArrayList<ProtoProperty> execute(final Integer entity) throws TransactionException {
+  public ArrayList<ProtoProperty> execute(final Integer entity, final String version)
+      throws TransactionException {
     try {
       final PreparedStatement prepareStatement = prepareStatement(stmtStr);
 
-      final List<FlatProperty> props = retrieveFlatPropertiesStage1(0, entity, prepareStatement);
+      final List<FlatProperty> props =
+          retrieveFlatPropertiesStage1(0, entity, version, prepareStatement);
 
       final ArrayList<ProtoProperty> protos = new ArrayList<ProtoProperty>();
       for (final FlatProperty p : props) {
@@ -56,7 +59,7 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
         proto.property = p;
 
         final List<FlatProperty> subProps =
-            retrieveFlatPropertiesStage1(entity, p.id, prepareStatement);
+            retrieveFlatPropertiesStage1(entity, p.id, version, prepareStatement);
 
         proto.subProperties = subProps;
 
@@ -71,7 +74,10 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
   }
 
   private List<FlatProperty> retrieveFlatPropertiesStage1(
-      final Integer domain, final Integer entity, final PreparedStatement stmt)
+      final Integer domain,
+      final Integer entity,
+      final String version,
+      final PreparedStatement stmt)
       throws SQLException, ConnectionException {
     ResultSet rs = null;
     try {
@@ -82,6 +88,12 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
       }
 
       stmt.setInt(2, entity);
+      if (version == null) {
+        stmt.setNull(3, Types.VARBINARY);
+      } else {
+        stmt.setString(3, version);
+      }
+
       long t1 = System.currentTimeMillis();
       rs = stmt.executeQuery();
       long t2 = System.currentTimeMillis();
@@ -91,7 +103,7 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
 
       final PreparedStatement stmt2 = prepareStatement(stmtStr2);
 
-      retrieveOverrides(domain, entity, stmt2, props);
+      retrieveOverrides(domain, entity, version, stmt2, props);
 
       return props;
     } finally {
@@ -104,6 +116,7 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
   private void retrieveOverrides(
       final Integer domain,
       final Integer entity,
+      final String version,
       final PreparedStatement stmt2,
       final List<FlatProperty> props)
       throws SQLException {
@@ -112,6 +125,11 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev
     try {
       stmt2.setInt(1, domain);
       stmt2.setInt(2, entity);
+      if (version == null) {
+        stmt2.setNull(3, Types.VARBINARY);
+      } else {
+        stmt2.setString(3, version);
+      }
       long t1 = System.currentTimeMillis();
       rs = stmt2.executeQuery();
       long t2 = System.currentTimeMillis();
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java
index 92f2cd5fe4c26d796112baec5d908b9431187ae7..c6d88ec9f8fca1651318456161ed2c0347a47f21 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java
@@ -28,6 +28,7 @@ import caosdb.server.database.exceptions.TransactionException;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Types;
 
 public class MySQLRetrieveQueryTemplateDefinition extends MySQLTransaction
     implements RetrieveQueryTemplateDefinitionImpl {
@@ -37,14 +38,19 @@ public class MySQLRetrieveQueryTemplateDefinition extends MySQLTransaction
   }
 
   public static final String STMT_RETRIEVE_QUERY_TEMPLATE_DEF =
-      "SELECT definition FROM query_template_def WHERE id=?";
+      "call retrieveQueryTemplateDef(?,?)";
 
   @Override
-  public String retrieve(final Integer id) {
+  public String retrieve(final Integer id, final String version) {
     try {
 
       final PreparedStatement stmt = prepareStatement(STMT_RETRIEVE_QUERY_TEMPLATE_DEF);
       stmt.setInt(1, id);
+      if (version == null) {
+        stmt.setNull(2, Types.VARBINARY);
+      } else {
+        stmt.setString(2, version);
+      }
       final ResultSet rs = stmt.executeQuery();
       if (rs.next()) {
         return rs.getString("definition");
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java
index 2aff2aaedda70672eb676597a2e3ba7a1db14352..097961974b0c599480e8186f170665838e7d046e 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java
@@ -30,6 +30,7 @@ import caosdb.server.database.proto.SparseEntity;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Types;
 
 /**
  * Retrieve a single SparseEntity by id.
@@ -43,14 +44,19 @@ public class MySQLRetrieveSparseEntity extends MySQLTransaction
     super(access);
   }
 
-  private static final String stmtStr = "call retrieveEntity(?)";
+  private static final String stmtStr = "call retrieveEntity(?,?)";
 
   @Override
-  public SparseEntity execute(final int id) throws TransactionException {
+  public SparseEntity execute(final int id, final String version) throws TransactionException {
     try {
       final PreparedStatement preparedStatement = prepareStatement(stmtStr);
 
       preparedStatement.setInt(1, id);
+      if (version == null) {
+        preparedStatement.setNull(2, Types.VARBINARY);
+      } else {
+        preparedStatement.setString(2, version);
+      }
       try (final ResultSet rs = preparedStatement.executeQuery()) {
         if (rs.next()) {
           return DatabaseUtils.parseEntityResultSet(rs);
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
new file mode 100644
index 0000000000000000000000000000000000000000..22ac0c2e30c0f48fbc884f40593bac2dc45d7b7d
--- /dev/null
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
@@ -0,0 +1,85 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+package caosdb.server.database.backend.implementation.MySQL;
+
+import caosdb.server.database.DatabaseUtils;
+import caosdb.server.database.access.Access;
+import caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
+import caosdb.server.database.exceptions.TransactionException;
+import caosdb.server.database.proto.VersionHistoryItem;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+/**
+ * Transaction to retrieve all versions of an entity.
+ *
+ * <p>Creates a mapping ID :: (VersionHistoryItem)
+ */
+public class MySQLRetrieveVersionHistory extends MySQLTransaction
+    implements RetrieveVersionHistoryImpl {
+
+  public static final String VERSION_HISTORY_STMT = "CALL get_version_history(?)";
+
+  public MySQLRetrieveVersionHistory(Access access) {
+    super(access);
+  }
+
+  @Override
+  public HashMap<String, VersionHistoryItem> execute(Integer entityId) {
+
+    HashMap<String, VersionHistoryItem> result = new HashMap<>();
+    try {
+      PreparedStatement s = prepareStatement(VERSION_HISTORY_STMT);
+      s.setInt(1, entityId);
+      ResultSet rs = s.executeQuery();
+
+      while (rs.next()) {
+        String childId = DatabaseUtils.bytes2UTF8(rs.getBytes("child"));
+        String parentId = DatabaseUtils.bytes2UTF8(rs.getBytes("parent"));
+        Long childSeconds = rs.getLong("child_seconds");
+        Integer childNanos = rs.getInt("child_nanos");
+        VersionHistoryItem v = result.get(childId);
+        if (v == null) {
+          v = new VersionHistoryItem();
+          v.id = childId;
+          v.seconds = childSeconds;
+          v.nanos = childNanos;
+          result.put(childId, v);
+        }
+
+        if (parentId != null) {
+          if (v.parents == null) {
+            v.parents = new LinkedList<>();
+          }
+          v.parents.add(parentId);
+        }
+      }
+    } catch (SQLException | ConnectionException e) {
+      throw new TransactionException(e);
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
index 4cc59f9d0f0a813bb92294f6fabb1cd51f43c151..73c589fade317586cc1b5bfb4b796578f9e819ba 100644
--- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
@@ -22,12 +22,14 @@
  */
 package caosdb.server.database.backend.implementation.MySQL;
 
+import caosdb.server.database.DatabaseUtils;
 import caosdb.server.database.access.Access;
 import caosdb.server.database.backend.interfaces.UpdateSparseEntityImpl;
 import caosdb.server.database.exceptions.IntegrityException;
 import caosdb.server.database.exceptions.TransactionException;
 import caosdb.server.database.proto.SparseEntity;
 import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLIntegrityConstraintViolationException;
 import java.sql.Types;
@@ -39,15 +41,31 @@ public class MySQLUpdateSparseEntity extends MySQLTransaction implements UpdateS
   }
 
   public static final String STMT_UPDATE_ENTITY = "call updateEntity(?,?,?,?,?,?,?)";
-  public static final String STMT_UPDATE_FILE_PROPS =
-      "INSERT INTO files (hash, size, path, file_id) VALUES (unhex(?),?,?,?) ON DUPLICATE KEY UPDATE hash=unhex(?), size=?, path=?;";
+  public static final String STMT_UPDATE_FILE_PROPS = "call setFileProperties(?,?,?,?)";
 
   @Override
   public void execute(final SparseEntity spe) throws TransactionException {
     try {
-      final PreparedStatement updateEntityStmt = prepareStatement(STMT_UPDATE_ENTITY);
+      // file properties;
+      final PreparedStatement updateFilePropsStmt = prepareStatement(STMT_UPDATE_FILE_PROPS);
+      updateFilePropsStmt.setInt(1, spe.id);
+      if (spe.filePath != null) {
+        updateFilePropsStmt.setString(2, spe.filePath);
+        updateFilePropsStmt.setLong(3, spe.fileSize);
+        if (spe.fileHash != null) {
+          updateFilePropsStmt.setString(4, spe.fileHash);
+        } else {
+          updateFilePropsStmt.setNull(4, Types.VARCHAR);
+        }
+      } else {
+        updateFilePropsStmt.setNull(2, Types.VARCHAR);
+        updateFilePropsStmt.setNull(3, Types.BIGINT);
+        updateFilePropsStmt.setNull(4, Types.VARCHAR);
+      }
+      updateFilePropsStmt.execute();
 
       // very sparse entity
+      final PreparedStatement updateEntityStmt = prepareStatement(STMT_UPDATE_ENTITY);
       updateEntityStmt.setInt(1, spe.id);
       updateEntityStmt.setString(2, spe.name);
       updateEntityStmt.setString(3, spe.description);
@@ -59,28 +77,12 @@ public class MySQLUpdateSparseEntity extends MySQLTransaction implements UpdateS
       }
       updateEntityStmt.setString(6, spe.collection);
       updateEntityStmt.setString(7, spe.acl);
-      updateEntityStmt.execute();
+      ResultSet rs = updateEntityStmt.executeQuery();
 
-      // file properties;
-      if (spe.filePath != null) {
-        final PreparedStatement updateFilePropsStmt = prepareStatement(STMT_UPDATE_FILE_PROPS);
-        if (spe.fileHash != null) {
-          updateFilePropsStmt.setString(1, spe.fileHash);
-          updateFilePropsStmt.setString(5, spe.fileHash);
-        } else {
-          updateFilePropsStmt.setNull(1, Types.VARCHAR);
-          updateFilePropsStmt.setNull(5, Types.VARCHAR);
-        }
-        updateFilePropsStmt.setLong(2, spe.fileSize);
-        updateFilePropsStmt.setLong(6, spe.fileSize);
-
-        updateFilePropsStmt.setString(3, spe.filePath);
-        updateFilePropsStmt.setString(7, spe.filePath);
-
-        updateFilePropsStmt.setInt(4, spe.id);
-
-        updateFilePropsStmt.execute();
+      if (rs.next()) {
+        spe.version = DatabaseUtils.bytes2UTF8(rs.getBytes("Version"));
       }
+
     } catch (final SQLIntegrityConstraintViolationException e) {
       throw new IntegrityException(e);
     } catch (final SQLException e) {
diff --git a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java
index 551ee962d8768aa90b9f5aaed313c050f0873079..9dfb8cf57c260c7fe154c25f095886ca6a3c2698 100644
--- a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java
+++ b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveParentsImpl.java
@@ -28,5 +28,6 @@ import java.util.ArrayList;
 
 public interface RetrieveParentsImpl extends BackendTransactionImpl {
 
-  public ArrayList<VerySparseEntity> execute(Integer id) throws TransactionException;
+  public ArrayList<VerySparseEntity> execute(Integer id, String version)
+      throws TransactionException;
 }
diff --git a/src/main/java/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
index 3c8449f7fc62581457091ea5ab0ccc5921b89a92..b9a15a526b857b9674432a96733c6f2fd8e5d4ea 100644
--- a/src/main/java/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
+++ b/src/main/java/caosdb/server/database/backend/interfaces/RetrievePropertiesImpl.java
@@ -28,5 +28,5 @@ import java.util.ArrayList;
 
 public interface RetrievePropertiesImpl extends BackendTransactionImpl {
 
-  public ArrayList<ProtoProperty> execute(Integer id) throws TransactionException;
+  public ArrayList<ProtoProperty> execute(Integer id, String version) throws TransactionException;
 }
diff --git a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveQueryTemplateDefinitionImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveQueryTemplateDefinitionImpl.java
index fee860ca2c0ee4341ae91a4a1736d518cd71a5e8..cda336044de4bd0d37a3d67da140c744f2699f32 100644
--- a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveQueryTemplateDefinitionImpl.java
+++ b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveQueryTemplateDefinitionImpl.java
@@ -24,5 +24,5 @@ package caosdb.server.database.backend.interfaces;
 
 public interface RetrieveQueryTemplateDefinitionImpl extends BackendTransactionImpl {
 
-  public String retrieve(final Integer id);
+  public String retrieve(Integer id, String version);
 }
diff --git a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveSparseEntityImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveSparseEntityImpl.java
index 82a534c03182d577e7b27eca9b2ff9def1c60677..5b6978f8f456bf978c3ca60ae0da479eaf54ed0a 100644
--- a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveSparseEntityImpl.java
+++ b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveSparseEntityImpl.java
@@ -27,5 +27,5 @@ import caosdb.server.database.proto.SparseEntity;
 
 public interface RetrieveSparseEntityImpl extends BackendTransactionImpl {
 
-  public SparseEntity execute(int id) throws TransactionException;
+  public SparseEntity execute(int id, String version) throws TransactionException;
 }
diff --git a/src/main/java/caosdb/server/database/backend/interfaces/RetrieveVersionHistoryImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveVersionHistoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..118e783d816ce45d1a43738bf8959e4a94740d3c
--- /dev/null
+++ b/src/main/java/caosdb/server/database/backend/interfaces/RetrieveVersionHistoryImpl.java
@@ -0,0 +1,9 @@
+package caosdb.server.database.backend.interfaces;
+
+import caosdb.server.database.proto.VersionHistoryItem;
+import java.util.HashMap;
+
+public interface RetrieveVersionHistoryImpl extends BackendTransactionImpl {
+
+  public HashMap<String, VersionHistoryItem> execute(Integer entityId);
+}
diff --git a/src/main/java/caosdb/server/database/backend/transaction/DeleteEntityProperties.java b/src/main/java/caosdb/server/database/backend/transaction/DeleteEntityProperties.java
index 15ace2b9820398cb270a6a1e098e6f642b66ab9d..984ca5eb853e356814aafb69ebd9860bee4fe8a1 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/DeleteEntityProperties.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/DeleteEntityProperties.java
@@ -40,8 +40,8 @@ public class DeleteEntityProperties extends BackendTransaction {
 
   @Override
   public void execute() {
-    RetrieveProperties.removeCached(this.entity.getId());
-    RetrieveParents.removeCached(this.entity.getId());
+    RetrieveProperties.removeCached(this.entity.getIdVersion());
+    RetrieveParents.removeCached(this.entity.getIdVersion());
 
     final DeleteEntityPropertiesImpl ret = getImplementation(DeleteEntityPropertiesImpl.class);
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java
index 39deb1416c743f838bfc1b96c29fd845dbcef742..a2e4de6482d78053ab01c70ea3634c3f8af232a3 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java
@@ -42,7 +42,7 @@ public class DeleteSparseEntity extends BackendTransaction {
 
   @Override
   protected void execute() {
-    RetrieveSparseEntity.removeCached(this.entity.getId());
+    RetrieveSparseEntity.removeCached(this.entity);
     if (entity.hasFileProperties()) {
       GetFileRecordByPath.removeCached(this.entity.getFileProperties().getPath());
     }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/InsertSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/InsertSparseEntity.java
index f8711197064a88dd92c408f329d1a36df55c5ed2..c8e20895711e16c40c75c89c9776e8ad55851117 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/InsertSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/InsertSparseEntity.java
@@ -30,6 +30,7 @@ import caosdb.server.database.exceptions.IntegrityException;
 import caosdb.server.database.exceptions.TransactionException;
 import caosdb.server.database.proto.SparseEntity;
 import caosdb.server.entity.EntityInterface;
+import caosdb.server.entity.Version;
 import caosdb.server.utils.Undoable;
 
 public class InsertSparseEntity extends BackendTransaction {
@@ -68,5 +69,6 @@ public class InsertSparseEntity extends BackendTransaction {
               public void cleanUp() {}
             });
     this.entity.setId(e.id);
+    this.entity.setVersion(new Version(e.version));
   }
 }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveFullEntity.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveFullEntity.java
index a1817767650175f604286a29ea82b5387a4dc2d6..ad0d39e94b56904def08a04a8a42a89627a7b4d6 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveFullEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveFullEntity.java
@@ -107,6 +107,7 @@ public class RetrieveFullEntity extends BackendTransaction {
       }
       execute(new RetrieveParents(e));
       execute(new RetrieveProperties(e));
+      execute(new RetrieveVersionInfo(e));
 
       // recursion! retrieveSubEntities calls retrieveFull sometimes, but with reduced selectors.
       if (selections != null && !selections.isEmpty()) {
@@ -143,7 +144,7 @@ public class RetrieveFullEntity extends BackendTransaction {
               RetrieveEntity ref = new RetrieveEntity(value.getId());
               // recursion!  (Only for the matching selections)
               retrieveFullEntity(ref, getSubSelects(selections, propertyName));
-              value.setEntity(ref);
+              value.setEntity(ref, true);
             }
           }
         }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java
index 5c1b0178f51496f83a4fac6a4ce3b2447abdccd5..d33d5e96d978fe86d6eb965113726eb79bb60d40 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java
@@ -34,10 +34,22 @@ import caosdb.server.entity.EntityInterface;
 import java.util.ArrayList;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
+// TODO Problem with the caching.
+// When an old entity version has a parent which is deleted, the name is
+// still in the cached VerySparseEntity. This can be resolved by using a
+// similar strategy as in RetrieveProperties.java where the name etc. are
+// retrieved in a second step. Thus the deletion doesn't slip through
+// unnoticed.
+//
+// Changes are necessary in the backend-api, i.e. mysqlbackend and the
+// interfaces as well.
+//
+// See also a failing test in caosdb-pyinttest:
+// tests/test_version.py::test_bug_cached_parent_name_in_old_version
 public class RetrieveParents
-    extends CacheableBackendTransaction<Integer, ArrayList<VerySparseEntity>> {
+    extends CacheableBackendTransaction<String, ArrayList<VerySparseEntity>> {
 
-  private static final ICacheAccess<Integer, ArrayList<VerySparseEntity>> cache =
+  private static final ICacheAccess<String, ArrayList<VerySparseEntity>> cache =
       Cache.getCache("BACKEND_EntityParents");
 
   /**
@@ -45,9 +57,9 @@ public class RetrieveParents
    *
    * @param id
    */
-  public static void removeCached(final Integer id) {
-    if (id != null && cache != null) {
-      cache.remove(id);
+  public static void removeCached(final String idVersion) {
+    if (idVersion != null && cache != null) {
+      cache.remove(idVersion);
     }
   }
 
@@ -61,18 +73,18 @@ public class RetrieveParents
   @Override
   public ArrayList<VerySparseEntity> executeNoCache() throws TransactionException {
     final RetrieveParentsImpl t = getImplementation(RetrieveParentsImpl.class);
-    final Integer key = getKey();
-    return t.execute(key);
+    return t.execute(this.entity.getId(), this.entity.getVersion().getId());
   }
 
   @Override
   protected void process(final ArrayList<VerySparseEntity> t) throws TransactionException {
     this.entity.getParents().clear();
+
     DatabaseUtils.parseParentsFromVerySparseEntity(this.entity, t);
   }
 
   @Override
-  protected Integer getKey() {
-    return this.entity.getId();
+  protected String getKey() {
+    return this.entity.getIdVersion();
   }
 }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java
index 487feb56cf848e948fcacb8f33f49e4a0bc2267d..822f57fd49e54d7517578592acf09100924fe81b 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java
@@ -37,11 +37,11 @@ import java.util.ArrayList;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrieveProperties
-    extends CacheableBackendTransaction<Integer, ArrayList<ProtoProperty>> {
+    extends CacheableBackendTransaction<String, ArrayList<ProtoProperty>> {
 
   private final EntityInterface entity;
   public static final String CACHE_REGION = "BACKEND_EntityProperties";
-  private static final ICacheAccess<Integer, ArrayList<ProtoProperty>> cache =
+  private static final ICacheAccess<String, ArrayList<ProtoProperty>> cache =
       Cache.getCache(CACHE_REGION);
 
   /**
@@ -49,9 +49,9 @@ public class RetrieveProperties
    *
    * @param id
    */
-  protected static void removeCached(final Integer id) {
-    if (id != null && cache != null) {
-      cache.remove(id);
+  protected static void removeCached(final String idVersion) {
+    if (idVersion != null && cache != null) {
+      cache.remove(idVersion);
     }
   }
 
@@ -63,7 +63,7 @@ public class RetrieveProperties
   @Override
   public ArrayList<ProtoProperty> executeNoCache() throws TransactionException {
     final RetrievePropertiesImpl t = getImplementation(RetrievePropertiesImpl.class);
-    return t.execute(getKey());
+    return t.execute(this.entity.getId(), this.entity.getVersion().getId());
   }
 
   @Override
@@ -98,7 +98,7 @@ public class RetrieveProperties
   }
 
   @Override
-  protected Integer getKey() {
-    return this.entity.getId();
+  protected String getKey() {
+    return this.entity.getIdVersion();
   }
 }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveQueryTemplateDefinition.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveQueryTemplateDefinition.java
index 208c7439aa6cf9ff8f32abc8f5cd59ea9a3cba95..0c96c72cb0be41aaf368c22009526914994bc8ec 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveQueryTemplateDefinition.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveQueryTemplateDefinition.java
@@ -39,6 +39,7 @@ public class RetrieveQueryTemplateDefinition extends BackendTransaction {
   protected void execute() throws TransactionException {
     final RetrieveQueryTemplateDefinitionImpl t =
         getImplementation(RetrieveQueryTemplateDefinitionImpl.class);
-    this.entity.setQueryTemplateDefinition(t.retrieve(this.entity.getId()));
+    this.entity.setQueryTemplateDefinition(
+        t.retrieve(this.entity.getId(), this.entity.getVersion().getId()));
   }
 }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
index a91b5021f1fa722a8e8831234fc8ebc2032ba115..7843f57957d392c4bbf65f01a60d559cff11c6c8 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
@@ -3,9 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2019 IndiScale GmbH
- * Copyright (C) 2019 Timm Fitschen (t.fitschen@indiscale.com)
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019,2020 IndiScale GmbH
+ * Copyright (C) 2019,2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -35,20 +35,21 @@ import caosdb.server.entity.EntityInterface;
 import caosdb.server.utils.EntityStatus;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
-public class RetrieveSparseEntity extends CacheableBackendTransaction<Integer, SparseEntity> {
+public class RetrieveSparseEntity extends CacheableBackendTransaction<String, SparseEntity> {
 
   private final EntityInterface entity;
-  private static final ICacheAccess<Integer, SparseEntity> cache =
+  private static final ICacheAccess<String, SparseEntity> cache =
       Cache.getCache("BACKEND_SparseEntities");
 
   /**
-   * To be called by {@link UpdateSparseEntity} and {@link DeleteEntity} on execution.
+   * To be called by {@link UpdateSparseEntity} and {@link DeleteSparseEntity} on execution.
    *
-   * @param id
+   * @param entity
    */
-  public static void removeCached(final Integer id) {
-    if (id != null && cache != null) {
-      cache.remove(id);
+  public static void removeCached(final EntityInterface entity) {
+    if (entity != null && cache != null) {
+      cache.remove(entity.getId().toString());
+      cache.remove(entity.getIdVersion());
     }
   }
 
@@ -57,14 +58,15 @@ public class RetrieveSparseEntity extends CacheableBackendTransaction<Integer, S
     this.entity = entity;
   }
 
-  public RetrieveSparseEntity(final int id) {
+  public RetrieveSparseEntity(final int id, final String version) {
     this(new Entity(id));
+    this.entity.getVersion().setId(version);
   }
 
   @Override
   public SparseEntity executeNoCache() throws TransactionException {
     final RetrieveSparseEntityImpl t = getImplementation(RetrieveSparseEntityImpl.class);
-    final SparseEntity ret = t.execute(getKey());
+    final SparseEntity ret = t.execute(getEntity().getId(), getEntity().getVersion().getId());
     if (ret == null) {
       this.entity.setEntityStatus(EntityStatus.NONEXISTENT);
     }
@@ -78,8 +80,14 @@ public class RetrieveSparseEntity extends CacheableBackendTransaction<Integer, S
   }
 
   @Override
-  protected Integer getKey() {
-    return this.entity.getId();
+  protected String getKey() {
+    if ("HEAD".equalsIgnoreCase(entity.getVersion().getId())) {
+      return this.entity.getId().toString();
+    } else if (entity.hasVersion()
+        && entity.getVersion().getId().toUpperCase().startsWith("HEAD")) {
+      return null;
+    }
+    return this.entity.getIdVersion();
   }
 
   public EntityInterface getEntity() {
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java
new file mode 100644
index 0000000000000000000000000000000000000000..20be2f8fb60ce54877fdb2fd85d27a6944a71d86
--- /dev/null
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java
@@ -0,0 +1,79 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+package caosdb.server.database.backend.transaction;
+
+import caosdb.server.database.CacheableBackendTransaction;
+import caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
+import caosdb.server.database.exceptions.TransactionException;
+import caosdb.server.database.proto.VersionHistoryItem;
+import caosdb.server.entity.EntityInterface;
+import java.util.Collection;
+import java.util.HashMap;
+
+public abstract class RetrieveVersionHistory
+    extends CacheableBackendTransaction<Integer, HashMap<String, VersionHistoryItem>> {
+
+  // TODO
+  // private static final ICacheAccess<String, Version> cache =
+  // Cache.getCache("BACKEND_RetrieveVersionHistory");
+  private EntityInterface entity;
+  private HashMap<String, VersionHistoryItem> map;
+
+  public static void removeCached(Integer entityId) {
+    // TODO
+  }
+
+  public RetrieveVersionHistory(EntityInterface e) {
+    super(null); // TODO caching
+    this.entity = e;
+  }
+
+  @Override
+  public HashMap<String, VersionHistoryItem> executeNoCache() throws TransactionException {
+    RetrieveVersionHistoryImpl impl = getImplementation(RetrieveVersionHistoryImpl.class);
+    return impl.execute(getKey());
+  }
+
+  /** After this method call, the version map is available to the object. */
+  @Override
+  protected void process(HashMap<String, VersionHistoryItem> map) throws TransactionException {
+    this.map = map;
+  }
+
+  @Override
+  protected Integer getKey() {
+    return entity.getId();
+  }
+
+  public HashMap<String, VersionHistoryItem> getMap() {
+    return this.map;
+  }
+
+  public EntityInterface getEntity() {
+    return this.entity;
+  }
+
+  public Collection<VersionHistoryItem> getList() {
+    return this.map.values();
+  }
+}
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..1daab0504cafbd05e9e03dff78b2c230a0438ccd
--- /dev/null
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java
@@ -0,0 +1,84 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+package caosdb.server.database.backend.transaction;
+
+import caosdb.datetime.UTCDateTime;
+import caosdb.server.database.exceptions.TransactionException;
+import caosdb.server.database.proto.VersionHistoryItem;
+import caosdb.server.entity.EntityInterface;
+import caosdb.server.entity.Version;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+public class RetrieveVersionInfo extends RetrieveVersionHistory {
+
+  public RetrieveVersionInfo(EntityInterface e) {
+    super(e);
+  }
+
+  @Override
+  protected void process(HashMap<String, VersionHistoryItem> map) throws TransactionException {
+    super.process(map); // Make the map available to the object.
+    if (!map.isEmpty()) getVersion();
+  }
+
+  public Version getVersion() {
+    Version v = getEntity().getVersion();
+    VersionHistoryItem i = getMap().get(v.getId());
+    if (i != null) v.setDate(UTCDateTime.UTCSeconds(i.seconds, i.nanos));
+
+    v.setPredecessors(getPredecessors(v.getId()));
+    v.setSuccessors(getSuccessors(v.getId()));
+    return v;
+  }
+
+  /** Return a list of direct children. */
+  private LinkedList<Version> getSuccessors(String id) {
+    LinkedList<Version> result = new LinkedList<>();
+
+    outer:
+    for (VersionHistoryItem i : getList()) {
+      if (i.parents != null)
+        for (String p : i.parents) {
+          if (id.equals(p)) {
+            Version successor = new Version(i.id, i.seconds, i.nanos);
+            result.add(successor);
+            continue outer;
+          }
+        }
+    }
+    return result;
+  }
+
+  /** Return a list of direct parents. */
+  private LinkedList<Version> getPredecessors(String id) {
+    LinkedList<Version> result = new LinkedList<>();
+    if (getMap().containsKey(id) && getMap().get(id).parents != null)
+      for (String p : getMap().get(id).parents) {
+        VersionHistoryItem i = getMap().get(p);
+        Version predecessor = new Version(i.id, i.seconds, i.nanos);
+        result.add(predecessor);
+      }
+    return result;
+  }
+}
diff --git a/src/main/java/caosdb/server/database/backend/transaction/UpdateEntity.java b/src/main/java/caosdb/server/database/backend/transaction/UpdateEntity.java
index 6378e8bbbbd8aee87382d226df0691d004e641aa..a7c9ba43f32b1ef97a6a09544041b39a2cd5c71a 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/UpdateEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/UpdateEntity.java
@@ -53,6 +53,8 @@ public class UpdateEntity extends BackendTransaction {
         execute(new InsertEntityValue(e));
 
         execute(new InsertEntityProperties(e));
+
+        execute(new RetrieveVersionInfo(e));
       }
     }
   }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
index c6a1c7683409e0c875484e14553e4aeac2757a71..309ff9cbde0131f4b90cc89301a2338a8b92971f 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
@@ -29,6 +29,7 @@ import caosdb.server.database.backend.interfaces.UpdateSparseEntityImpl;
 import caosdb.server.database.exceptions.TransactionException;
 import caosdb.server.database.proto.SparseEntity;
 import caosdb.server.entity.EntityInterface;
+import caosdb.server.entity.Version;
 
 public class UpdateSparseEntity extends BackendTransaction {
 
@@ -40,7 +41,7 @@ public class UpdateSparseEntity extends BackendTransaction {
 
   @Override
   public void execute() throws TransactionException {
-    RetrieveSparseEntity.removeCached(this.entity.getId());
+    RetrieveSparseEntity.removeCached(this.entity);
     if (entity.hasFileProperties()) {
       GetFileRecordByPath.removeCached(this.entity.getFileProperties().getPath());
     }
@@ -50,5 +51,7 @@ public class UpdateSparseEntity extends BackendTransaction {
     final SparseEntity spe = this.entity.getSparseEntity();
 
     t.execute(spe);
+
+    this.entity.setVersion(new Version(spe.version));
   }
 }
diff --git a/src/main/java/caosdb/server/database/proto/SparseEntity.java b/src/main/java/caosdb/server/database/proto/SparseEntity.java
index 4f5dcc69ce5ed7c6afa636b97237b53abb56ab51..d70d4ae026eb72d8c782b763ae3fc21d03a73064 100644
--- a/src/main/java/caosdb/server/database/proto/SparseEntity.java
+++ b/src/main/java/caosdb/server/database/proto/SparseEntity.java
@@ -38,6 +38,9 @@ public class SparseEntity extends VerySparseEntity {
   public String filePath = null;
   public Long fileSize = null;
   public Long fileChecked = null;
+  public String version = null;
+  public Long versionSeconds = null;
+  public Integer versionNanos = null;
 
   @Override
   public String toString() {
@@ -51,6 +54,9 @@ public class SparseEntity extends VerySparseEntity {
         .append(this.fileHash)
         .append(this.filePath)
         .append(this.fileSize)
+        .append(this.version)
+        .append(this.versionSeconds)
+        .append(this.versionNanos)
         .toString();
   }
 }
diff --git a/src/main/java/caosdb/server/database/proto/VersionHistoryItem.java b/src/main/java/caosdb/server/database/proto/VersionHistoryItem.java
new file mode 100644
index 0000000000000000000000000000000000000000..e671950f39f40c0b93069d031f636ad964560508
--- /dev/null
+++ b/src/main/java/caosdb/server/database/proto/VersionHistoryItem.java
@@ -0,0 +1,13 @@
+package caosdb.server.database.proto;
+
+import java.io.Serializable;
+import java.util.LinkedList;
+
+public class VersionHistoryItem implements Serializable {
+
+  private static final long serialVersionUID = 7855704308135158698L;
+  public String id = null;
+  public LinkedList<String> parents = null;
+  public Long seconds = null;
+  public Integer nanos = null;
+}
diff --git a/src/main/java/caosdb/server/datatype/ReferenceDatatype2.java b/src/main/java/caosdb/server/datatype/ReferenceDatatype2.java
index ce92aa022cfe85573049eaaeed4e26653b400679..87c17079b0facdc193ea79dd6005598f7fe151b6 100644
--- a/src/main/java/caosdb/server/datatype/ReferenceDatatype2.java
+++ b/src/main/java/caosdb/server/datatype/ReferenceDatatype2.java
@@ -47,7 +47,7 @@ public class ReferenceDatatype2 extends ReferenceDatatype {
   }
 
   public void setEntity(final EntityInterface datatypeEntity) {
-    this.refid.setEntity(datatypeEntity);
+    this.refid.setEntity(datatypeEntity, false);
   }
 
   @Override
diff --git a/src/main/java/caosdb/server/datatype/ReferenceValue.java b/src/main/java/caosdb/server/datatype/ReferenceValue.java
index 25fa0b381cb436236dd6e3676970cf7e54c17776..77f7aa8c56d97b8dc5abf914c0b6c88749683d69 100644
--- a/src/main/java/caosdb/server/datatype/ReferenceValue.java
+++ b/src/main/java/caosdb/server/datatype/ReferenceValue.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -26,27 +28,36 @@ import caosdb.server.datatype.AbstractDatatype.Table;
 import caosdb.server.entity.EntityInterface;
 import caosdb.server.entity.Message;
 import caosdb.server.utils.ServerMessages;
+import java.util.Objects;
 import org.jdom2.Element;
 
+/**
+ * A ReferenceValue represents the value of a reference property to another entity.
+ *
+ * <p>Differently from other properties, they may be versioned, i.e. they may reference to a
+ * specific version of an entity.
+ *
+ * <p>TODO: Ways to specify a reference value, what are the consequences of versioned references?
+ */
 public class ReferenceValue implements SingleValue {
   private EntityInterface entity = null;
   private String name = null;
   private Integer id = null;
+  private String version = null;
+  private boolean versioned = false;
 
   public static ReferenceValue parseReference(final Object reference) throws Message {
     if (reference == null) {
       return null;
     }
     if (reference instanceof EntityInterface) {
-      return new ReferenceValue((EntityInterface) reference);
+      return new ReferenceValue(
+          (EntityInterface) reference, ((EntityInterface) reference).hasVersion());
     } else if (reference instanceof ReferenceValue) {
       return (ReferenceValue) reference;
     } else if (reference instanceof GenericValue) {
-      try {
-        return new ReferenceValue(Integer.parseInt(((GenericValue) reference).toDatabaseString()));
-      } catch (final NumberFormatException e) {
-        return new ReferenceValue(((GenericValue) reference).toDatabaseString());
-      }
+      String str = ((GenericValue) reference).toDatabaseString();
+      return parseFromString(str);
     } else if (reference instanceof CollectionValue) {
       throw ServerMessages.DATA_TYPE_DOES_NOT_ACCEPT_COLLECTION_VALUES;
     } else {
@@ -58,24 +69,81 @@ public class ReferenceValue implements SingleValue {
     }
   }
 
+  /**
+   * Split a reference string into an entity part and a version part, if there is a version part.
+   *
+   * <p>If parsing the entity ID part to an integer fails, a NumberFormatException may be thrown.
+   */
+  public static ReferenceValue parseIdVersion(String str) {
+    String[] split = str.split("@", 2);
+    if (split.length == 2) {
+      return new ReferenceValue(Integer.parseInt(split[0]), split[1]);
+    } else {
+      return new ReferenceValue(Integer.parseInt(str));
+    }
+  }
+
+  /**
+   * Create a ReferenceValue from a string.
+   *
+   * <p>If the string looks like a valid "entityID@version" string, the result will have the
+   * corresponding entity and version parts.
+   */
+  public static ReferenceValue parseFromString(String str) {
+    try {
+      return parseIdVersion(str);
+    } catch (final NumberFormatException e) {
+      return new ReferenceValue(str);
+    }
+  }
+
+  /**
+   * Produce a nice but short string:
+   *
+   * <p>Case 1 "versioned" (reference to an entity without specifying that entity's version):
+   * Produces a string like "1234" or "Experiment". Note that referencing via name is never
+   * versioned.
+   *
+   * <p>Case 2 "unversioned" (reference to an entity with a specified version): Produces a string
+   * like "1234@ab987f".
+   */
   @Override
   public String toString() {
-    if (this.entity != null) {
+    if (this.entity != null && versioned) { // Was specified as "versioned", with resolved entity
+      return this.entity.getIdVersion();
+    } else if (this.entity != null) { // resolved, but unversioned
       return this.entity.getId().toString();
-    } else if (this.id == null && this.name != null) {
+    } else if (this.id == null
+        && this.name != null) { // Only name is available, no id (and thus no resolved entity)
       return this.name;
     }
+    // Specification via id is the only remaining possibility
+    return getIdVersion(); // if version is null, returns ID only
+  }
+
+  public String getIdVersion() {
+    if (this.version != null) {
+      return new StringBuilder().append(this.id).append("@").append(this.version).toString();
+    }
     return this.id.toString();
   }
 
-  public ReferenceValue(final EntityInterface entity) {
+  public ReferenceValue(final EntityInterface entity, boolean versioned) {
+    this.versioned = versioned;
     this.entity = entity;
   }
 
   public ReferenceValue(final Integer id) {
+    this(id, null);
+  }
+
+  public ReferenceValue(final Integer id, final String version) {
     this.id = id;
+    this.version = version;
+    this.versioned = version != null;
   }
 
+  /** If the reference is given by name, versioning is not possible (at the moment). */
   public ReferenceValue(final String name) {
     this.name = name;
   }
@@ -84,7 +152,8 @@ public class ReferenceValue implements SingleValue {
     return this.entity;
   }
 
-  public final void setEntity(final EntityInterface entity) {
+  public final void setEntity(final EntityInterface entity, boolean versioned) {
+    this.versioned = versioned;
     this.entity = entity;
   }
 
@@ -102,6 +171,13 @@ public class ReferenceValue implements SingleValue {
     return this.id;
   }
 
+  public final String getVersion() {
+    if (this.entity != null && versioned && this.entity.hasVersion()) {
+      return this.entity.getVersion().getId();
+    }
+    return this.version;
+  }
+
   public final void setId(final Integer id) {
     this.id = id;
   }
@@ -126,7 +202,8 @@ public class ReferenceValue implements SingleValue {
     if (obj instanceof ReferenceValue) {
       final ReferenceValue that = (ReferenceValue) obj;
       if (that.getId() != null && getId() != null) {
-        return that.getId().equals(getId());
+        return that.getId().equals(getId())
+            && Objects.deepEquals(that.getVersion(), this.getVersion());
       } else if (that.getName() != null && getName() != null) {
         return that.getName().equals(getName());
       }
diff --git a/src/main/java/caosdb/server/entity/BaseEntity.java b/src/main/java/caosdb/server/entity/BaseEntity.java
deleted file mode 100644
index 62d4873b54c262eb75920f3f5c990e9988f07e50..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/entity/BaseEntity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package caosdb.server.entity;
-
-class BaseEntity {
-
-  EntityID id = null;
-}
diff --git a/src/main/java/caosdb/server/entity/DeleteEntity.java b/src/main/java/caosdb/server/entity/DeleteEntity.java
index 29b6a3a075b60e811cfb0e66d066278a47d2edcb..b27ab3a7745a9b4909dac642a9fccbaf035f52f7 100644
--- a/src/main/java/caosdb/server/entity/DeleteEntity.java
+++ b/src/main/java/caosdb/server/entity/DeleteEntity.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -27,4 +29,9 @@ public class DeleteEntity extends Entity {
   public DeleteEntity(final int id) {
     super(id);
   }
+
+  public DeleteEntity(int id, String version) {
+    super(id);
+    setVersion(new Version(version));
+  }
 }
diff --git a/src/main/java/caosdb/server/entity/Entity.java b/src/main/java/caosdb/server/entity/Entity.java
index 7fed351c51373e0cdb6aca9fe531c9699c2f9703..16f4f508e2d97a4c0f58c1775e95db9efbf4ffba 100644
--- a/src/main/java/caosdb/server/entity/Entity.java
+++ b/src/main/java/caosdb/server/entity/Entity.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -22,6 +24,7 @@
  */
 package caosdb.server.entity;
 
+import caosdb.datetime.UTCDateTime;
 import caosdb.server.CaosDBException;
 import caosdb.server.database.proto.SparseEntity;
 import caosdb.server.database.proto.VerySparseEntity;
@@ -800,7 +803,11 @@ public class Entity extends AbstractObservable implements EntityInterface {
     final CollectionValue vals = new CollectionValue();
     int pidx = 0;
     for (final Element pe : element.getChildren()) {
-      if (pe.getName().equalsIgnoreCase("EmptyString")) {
+      if (pe.getName().equalsIgnoreCase("Version")) {
+        // IGNORE: Once it becomes allowed for clients to set a version id, parsing
+        // the Version element would be done here. Until this is the case, the
+        // Version tag is ignored.
+      } else if (pe.getName().equalsIgnoreCase("EmptyString")) {
         // special case: empty string which cannot be distinguished from null
         // values otherwise.
         setValue(new GenericValue(""));
@@ -1026,6 +1033,7 @@ public class Entity extends AbstractObservable implements EntityInterface {
   }
 
   private boolean datatypeOverride = false;
+  private Version version = new Version();
 
   @Override
   public EntityInterface setDatatypeOverride(final boolean b) {
@@ -1066,10 +1074,15 @@ public class Entity extends AbstractObservable implements EntityInterface {
   }
 
   @Override
-  public EntityInterface parseSparseEntity(final SparseEntity spe) {
+  public final EntityInterface parseSparseEntity(final SparseEntity spe) {
     setId(spe.id);
     this.setRole(spe.role);
     setEntityACL(spe.acl);
+    UTCDateTime versionDate = null;
+    if (spe.versionSeconds != null) {
+      versionDate = UTCDateTime.UTCSeconds(spe.versionSeconds, spe.versionNanos);
+    }
+    this.version = new Version(spe.version, versionDate);
 
     if (!isNameOverride()) {
       setName(spe.name);
@@ -1118,4 +1131,34 @@ public class Entity extends AbstractObservable implements EntityInterface {
   public boolean skipJob() {
     return false;
   }
+
+  @Override
+  public Version getVersion() {
+    return this.version;
+  }
+
+  @Override
+  public boolean hasVersion() {
+    return this.version.getId() != null;
+  }
+
+  @Override
+  public void setVersion(Version version) {
+    this.version = version;
+  }
+
+  /** Return "id@version" if there is versioning information, else only "id". */
+  @Override
+  public String getIdVersion() {
+    if (!this.hasId()) {
+      return null;
+    } else if (this.hasVersion()) {
+      return new StringBuilder()
+          .append(getId())
+          .append("@")
+          .append(getVersion().getId())
+          .toString();
+    }
+    return getId().toString();
+  }
 }
diff --git a/src/main/java/caosdb/server/entity/EntityID.java b/src/main/java/caosdb/server/entity/EntityID.java
deleted file mode 100644
index fbfb83c8028f5f2e0d8376c6dc480c19046c0d5b..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/entity/EntityID.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package caosdb.server.entity;
-
-public class EntityID {}
diff --git a/src/main/java/caosdb/server/entity/EntityInterface.java b/src/main/java/caosdb/server/entity/EntityInterface.java
index 20aa57a5b3f64ae5794d01235049c12740a0419b..4205111a58c30185d916c1d921acb7470d93f7a9 100644
--- a/src/main/java/caosdb/server/entity/EntityInterface.java
+++ b/src/main/java/caosdb/server/entity/EntityInterface.java
@@ -52,6 +52,8 @@ public interface EntityInterface
 
   public abstract Integer getId();
 
+  public abstract String getIdVersion();
+
   public abstract void setId(Integer id);
 
   public abstract boolean hasId();
@@ -185,5 +187,11 @@ public interface EntityInterface
 
   public abstract void setQueryTemplateDefinition(String query);
 
+  public abstract Version getVersion();
+
+  public abstract boolean hasVersion();
+
+  public abstract void setVersion(Version version);
+
   public abstract void addToElement(Element element, SetFieldStrategy strategy);
 }
diff --git a/src/main/java/caosdb/server/entity/NamedEntity.java b/src/main/java/caosdb/server/entity/NamedEntity.java
deleted file mode 100644
index 76ba79a317e146ca4b86793545f95d9e05e42480..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/entity/NamedEntity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package caosdb.server.entity;
-
-class NamedEntity extends BaseEntity {
-
-  String name = null;
-}
diff --git a/src/main/java/caosdb/server/entity/RetrieveEntity.java b/src/main/java/caosdb/server/entity/RetrieveEntity.java
index 5b54c2bcd4ce401bc469728f774ad68198ba0986..7035232b5a66df77518c8c1b586b7bcb43b7a22b 100644
--- a/src/main/java/caosdb/server/entity/RetrieveEntity.java
+++ b/src/main/java/caosdb/server/entity/RetrieveEntity.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -22,9 +24,6 @@
  */
 package caosdb.server.entity;
 
-import caosdb.server.database.proto.SparseEntity;
-import caosdb.server.datatype.AbstractCollectionDatatype;
-
 public class RetrieveEntity extends Entity {
 
   public RetrieveEntity(final int id) {
@@ -35,38 +34,13 @@ public class RetrieveEntity extends Entity {
     super(name);
   }
 
-  public RetrieveEntity parseSparseEntity(final SparseEntity spe) {
-    setId(spe.id);
-    this.setRole(spe.role);
-    setEntityACL(spe.acl);
-
-    if (!isNameOverride()) {
-      setName(spe.name);
-    }
-    if (!isDescOverride()) {
-      setDescription(spe.description);
-    }
-    if (!isDatatypeOverride()) {
-      final String dt = spe.datatype;
-      final String col = spe.collection;
-
-      if (dt != null
-          && !dt.equalsIgnoreCase("null")
-          && (!hasDatatype() || !dt.equalsIgnoreCase(getDatatype().toString()))) {
-        if (col != null && !col.equalsIgnoreCase("null")) {
-          this.setDatatype(AbstractCollectionDatatype.collectionDatatypeFactory(col, dt));
-        } else {
-          this.setDatatype(dt);
-        }
-      }
-    }
-
-    if (spe.filePath != null) {
-      setFileProperties(new FileProperties(spe.fileHash, spe.filePath, spe.fileSize));
-    } else {
-      setFileProperties(null);
-    }
+  public RetrieveEntity(int id, String version) {
+    super(id);
+    this.setVersion(new Version(version));
+  }
 
-    return this;
+  public RetrieveEntity(String name, String version) {
+    super(name);
+    this.setVersion(new Version(version));
   }
 }
diff --git a/src/main/java/caosdb/server/entity/ValidEntity.java b/src/main/java/caosdb/server/entity/ValidEntity.java
index 3e99d4ead10152f3ce434a91c8c3d3643cc61273..d2c8b519734f3732f6fc7c43047a99e59ccd71c4 100644
--- a/src/main/java/caosdb/server/entity/ValidEntity.java
+++ b/src/main/java/caosdb/server/entity/ValidEntity.java
@@ -22,47 +22,9 @@
  */
 package caosdb.server.entity;
 
-import caosdb.server.database.proto.SparseEntity;
-import caosdb.server.datatype.AbstractCollectionDatatype;
-
 public class ValidEntity extends Entity {
 
   public ValidEntity(final int id) {
     super(id);
   }
-
-  public ValidEntity parseSparseEntity(final SparseEntity spe) {
-    setId(spe.id);
-    this.setRole(spe.role);
-    setEntityACL(spe.acl);
-
-    if (!isNameOverride()) {
-      setName(spe.name);
-    }
-    if (!isDescOverride()) {
-      setDescription(spe.description);
-    }
-    if (!isDatatypeOverride()) {
-      final String dt = spe.datatype;
-      final String col = spe.collection;
-
-      if (dt != null
-          && !dt.equalsIgnoreCase("null")
-          && (!hasDatatype() || !dt.equalsIgnoreCase(getDatatype().toString()))) {
-        if (col != null && !col.equalsIgnoreCase("null")) {
-          this.setDatatype(AbstractCollectionDatatype.collectionDatatypeFactory(col, dt));
-        } else {
-          this.setDatatype(dt);
-        }
-      }
-    }
-
-    if (spe.filePath != null) {
-      setFileProperties(new FileProperties(spe.fileHash, spe.filePath, spe.fileSize));
-    } else {
-      setFileProperties(null);
-    }
-
-    return this;
-  }
 }
diff --git a/src/main/java/caosdb/server/entity/Version.java b/src/main/java/caosdb/server/entity/Version.java
new file mode 100644
index 0000000000000000000000000000000000000000..07c7387e8c419f962e1174504a28ceceda5c30e3
--- /dev/null
+++ b/src/main/java/caosdb/server/entity/Version.java
@@ -0,0 +1,84 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package caosdb.server.entity;
+
+import caosdb.datetime.UTCDateTime;
+import java.util.LinkedList;
+
+/**
+ * Plain old java object (POJO) for an entity's version.
+ *
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
+ */
+public class Version {
+
+  private String id = null;
+  private LinkedList<Version> predecessors = null;
+  private LinkedList<Version> successors = null;
+  private UTCDateTime date = null;
+
+  public Version(String id, long seconds, int nanos) {
+    this(id, UTCDateTime.UTCSeconds(seconds, nanos));
+  }
+
+  public Version(String id, UTCDateTime date) {
+    this.id = id;
+    this.date = date;
+  }
+
+  public Version(String id) {
+    this(id, null);
+  }
+
+  public Version() {}
+
+  public UTCDateTime getDate() {
+    return date;
+  }
+
+  public void setDate(UTCDateTime date) {
+    this.date = date;
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public LinkedList<Version> getSuccessors() {
+    return successors;
+  }
+
+  public void setSuccessors(LinkedList<Version> successors) {
+    this.successors = successors;
+  }
+
+  public LinkedList<Version> getPredecessors() {
+    return predecessors;
+  }
+
+  public void setPredecessors(LinkedList<Version> predecessors) {
+    this.predecessors = predecessors;
+  }
+}
diff --git a/src/main/java/caosdb/server/entity/container/DeleteContainer.java b/src/main/java/caosdb/server/entity/container/DeleteContainer.java
index c219ee5e4399caeee857006f995748435d023963..c3d57bc656a7c9609dd8fb6ca4d031e0e82f63fa 100644
--- a/src/main/java/caosdb/server/entity/container/DeleteContainer.java
+++ b/src/main/java/caosdb/server/entity/container/DeleteContainer.java
@@ -42,4 +42,9 @@ public class DeleteContainer extends EntityByIdContainer {
   public void add(final int id) {
     add(new DeleteEntity(id));
   }
+
+  @Override
+  public void add(int id, String version) {
+    add(new DeleteEntity(id, version));
+  }
 }
diff --git a/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java b/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java
index 1bfda2024c4ff11375ad619d64946e1036c714b0..29a0978b8acdc809b75c52de643ee131cd62b129 100644
--- a/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java
+++ b/src/main/java/caosdb/server/entity/container/EntityByIdContainer.java
@@ -37,4 +37,6 @@ public abstract class EntityByIdContainer extends TransactionContainer {
   }
 
   public abstract void add(int id);
+
+  public abstract void add(int id, String version);
 }
diff --git a/src/main/java/caosdb/server/entity/container/RetrieveContainer.java b/src/main/java/caosdb/server/entity/container/RetrieveContainer.java
index 9b4f7364d4ecdc63da1be8f1ead292545801a078..5b205bc27f38e3d1a04b2ed3c5a7e874af229489 100644
--- a/src/main/java/caosdb/server/entity/container/RetrieveContainer.java
+++ b/src/main/java/caosdb/server/entity/container/RetrieveContainer.java
@@ -46,4 +46,13 @@ public class RetrieveContainer extends EntityByIdContainer {
   public void add(final String name) {
     add(new RetrieveEntity(name));
   }
+
+  public void add(final String name, String version) {
+    add(new RetrieveEntity(name, version));
+  }
+
+  @Override
+  public void add(int id, String version) {
+    add(new RetrieveEntity(id, version));
+  }
 }
diff --git a/src/main/java/caosdb/server/entity/wrapper/EntityWrapper.java b/src/main/java/caosdb/server/entity/wrapper/EntityWrapper.java
index 056f342c6b09b8f07130e6c89013777809c567b8..f8d563de1776d155b2f66b913de454c7e5b952fd 100644
--- a/src/main/java/caosdb/server/entity/wrapper/EntityWrapper.java
+++ b/src/main/java/caosdb/server/entity/wrapper/EntityWrapper.java
@@ -33,6 +33,7 @@ import caosdb.server.entity.FileProperties;
 import caosdb.server.entity.Message;
 import caosdb.server.entity.Role;
 import caosdb.server.entity.StatementStatus;
+import caosdb.server.entity.Version;
 import caosdb.server.entity.container.ParentContainer;
 import caosdb.server.entity.container.PropertyContainer;
 import caosdb.server.entity.xml.SetFieldStrategy;
@@ -555,6 +556,26 @@ public class EntityWrapper implements EntityInterface {
     return this.entity.hasPermission(subject, permission);
   }
 
+  @Override
+  public Version getVersion() {
+    return this.entity.getVersion();
+  }
+
+  @Override
+  public boolean hasVersion() {
+    return this.entity.hasVersion();
+  }
+
+  @Override
+  public void setVersion(Version version) {
+    this.entity.setVersion(version);
+  }
+
+  @Override
+  public String getIdVersion() {
+    return this.entity.getIdVersion();
+  }
+
   @Override
   public void addToElement(Element element, SetFieldStrategy strategy) {
     this.entity.addToElement(element, strategy);
diff --git a/src/main/java/caosdb/server/entity/wrapper/Parent.java b/src/main/java/caosdb/server/entity/wrapper/Parent.java
index c36ffa9c2c1a16062add2eb912ca01132af9872c..f3686b5b802e004e55d572e58076e5e08bb5988d 100644
--- a/src/main/java/caosdb/server/entity/wrapper/Parent.java
+++ b/src/main/java/caosdb/server/entity/wrapper/Parent.java
@@ -57,4 +57,10 @@ public class Parent extends EntityWrapper {
   public Affiliation getAffiliation() {
     return this.affiliation;
   }
+
+  @Override
+  public boolean hasVersion() {
+    // parents are not versioned (yet).
+    return false;
+  }
 }
diff --git a/src/main/java/caosdb/server/entity/wrapper/Property.java b/src/main/java/caosdb/server/entity/wrapper/Property.java
index f6bb840a75d9216ee3fd56f3d2bf95f701280492..1d1cd2a8ae11489479b9e9191365843182d136ca 100644
--- a/src/main/java/caosdb/server/entity/wrapper/Property.java
+++ b/src/main/java/caosdb/server/entity/wrapper/Property.java
@@ -134,4 +134,10 @@ public class Property extends EntityWrapper {
   public EntityInterface getDomainEntity() {
     return this.domain;
   }
+
+  @Override
+  public boolean hasVersion() {
+    // properties are not versioned (yet).
+    return false;
+  }
 }
diff --git a/src/main/java/caosdb/server/entity/xml/EntityToElementStrategy.java b/src/main/java/caosdb/server/entity/xml/EntityToElementStrategy.java
index ae69b94024eac0fdd9667233b507545e2b0f09af..6cfe8fa9f3628cc0670ccb3e6f30a263a0bc5cf3 100644
--- a/src/main/java/caosdb/server/entity/xml/EntityToElementStrategy.java
+++ b/src/main/java/caosdb/server/entity/xml/EntityToElementStrategy.java
@@ -87,6 +87,10 @@ public class EntityToElementStrategy implements ToElementStrategy {
     if (setFieldStrategy.isToBeSet("id") && entity.hasId()) {
       element.setAttribute("id", Integer.toString(entity.getId()));
     }
+    if (setFieldStrategy.isToBeSet("version") && entity.hasVersion()) {
+      Element v = new VersionXMLSerializer().toElement(entity.getVersion());
+      element.addContent(v);
+    }
     if (setFieldStrategy.isToBeSet("cuid") && entity.hasCuid()) {
       element.setAttribute("cuid", entity.getCuid());
     }
diff --git a/src/main/java/caosdb/server/entity/xml/SetFieldStrategy.java b/src/main/java/caosdb/server/entity/xml/SetFieldStrategy.java
index 3e7e735e6d863721a7591b8a2fc18f6fc9dfa15c..7a4a29a16cc545ca5316c7b086a8b1ec46cfcfc3 100644
--- a/src/main/java/caosdb/server/entity/xml/SetFieldStrategy.java
+++ b/src/main/java/caosdb/server/entity/xml/SetFieldStrategy.java
@@ -112,10 +112,10 @@ public class SetFieldStrategy {
        * are actually used, e.g ["a.b.c.d1", "a.b.c.d2"].
        */
       return new SetFieldStrategy() {
-        // Always-true-strategy
+        // Return true for everything except version fields.
         @Override
         public boolean isToBeSet(String field) {
-          return true;
+          return field == null || !field.equalsIgnoreCase("version");
         }
       };
     }
diff --git a/src/main/java/caosdb/server/entity/xml/VersionXMLSerializer.java b/src/main/java/caosdb/server/entity/xml/VersionXMLSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..badd1a95f2a2338bde43a1de23774097ab2c45ed
--- /dev/null
+++ b/src/main/java/caosdb/server/entity/xml/VersionXMLSerializer.java
@@ -0,0 +1,57 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package caosdb.server.entity.xml;
+
+import caosdb.server.entity.Version;
+import java.util.TimeZone;
+import org.jdom2.Element;
+
+/**
+ * Creates a JDOM Element for a Version instance.
+ *
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
+ */
+class VersionXMLSerializer {
+  public Element toElement(Version version) {
+    Element result = new Element("Version");
+    result.setAttribute("id", version.getId());
+    if (version.getDate() != null) {
+      result.setAttribute("date", version.getDate().toDateTimeString(TimeZone.getDefault()));
+    }
+    if (version.getPredecessors() != null) {
+      for (Version p : version.getPredecessors()) {
+        Element predecessor = new Element("Predecessor");
+        predecessor.setAttribute("id", p.getId());
+        predecessor.setAttribute("date", p.getDate().toDateTimeString(TimeZone.getDefault()));
+        result.addContent(predecessor);
+      }
+    }
+    if (version.getSuccessors() != null) {
+      for (Version s : version.getSuccessors()) {
+        Element successor = new Element("Successor");
+        successor.setAttribute("id", s.getId());
+        successor.setAttribute("date", s.getDate().toDateTimeString(TimeZone.getDefault()));
+        result.addContent(successor);
+      }
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/caosdb/server/jobs/Job.java b/src/main/java/caosdb/server/jobs/Job.java
index 110809e77ae0d40076642d953c15b0d9566eddd6..ff214b977dfd870a9aa56b3e8efa4f12967b2083 100644
--- a/src/main/java/caosdb/server/jobs/Job.java
+++ b/src/main/java/caosdb/server/jobs/Job.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -171,11 +173,39 @@ public abstract class Job extends AbstractObservable implements Observer {
 
   protected final EntityInterface retrieveValidSparseEntityByName(final String name)
       throws Message {
-    return retrieveValidSparseEntityById(retrieveValidIDByName(name));
+    return retrieveValidSparseEntityById(retrieveValidIDByName(name), null);
   }
 
-  protected final EntityInterface retrieveValidSparseEntityById(final Integer id) throws Message {
-    final EntityInterface ret = execute(new RetrieveSparseEntity(id)).getEntity();
+  protected final EntityInterface retrieveValidSparseEntityById(
+      final Integer id, final String version) throws Message {
+
+    String resulting_version = version;
+    if (version == null || version.equals("HEAD")) {
+      // the targeted entity version is the entity after the transaction or the
+      // entity without a specific version. Thus we have to fetch the entity
+      // from the container if possible.
+      EntityInterface ret = getEntityById(id);
+      if (ret != null) {
+        return ret;
+      }
+    } else if (version.startsWith("HEAD~")) {
+      EntityInterface entById = getEntityById(id);
+      if (entById != null && entById.getEntityStatus() != EntityStatus.VALID) {
+        // if version is HEAD~{OFFSET} with {OFFSET} > 0 and the targeted entity is is to be
+        // updated, the actual offset has to be reduced by 1. HEAD always denotes the entity@HEAD
+        // *after* the successful transaction, so that it is consistent with subsequent retrieves.
+        int offset = Integer.parseInt(version.substring(5)) - 1;
+        if (offset == 0) {
+          // special case HEAD~1
+          resulting_version = "HEAD";
+        } else {
+          resulting_version = new StringBuilder().append("HEAD~").append(offset).toString();
+        }
+      }
+    }
+
+    final EntityInterface ret =
+        execute(new RetrieveSparseEntity(id, resulting_version)).getEntity();
     if (ret.getEntityStatus() == EntityStatus.NONEXISTENT) {
       throw ServerMessages.ENTITY_DOES_NOT_EXIST;
     }
diff --git a/src/main/java/caosdb/server/jobs/core/CheckDatatypePresent.java b/src/main/java/caosdb/server/jobs/core/CheckDatatypePresent.java
index 69d36f42fa2f457b05dcc394659b5c088976bff4..faf481dba5ac16836b4495d6112fc25c887a6f1f 100644
--- a/src/main/java/caosdb/server/jobs/core/CheckDatatypePresent.java
+++ b/src/main/java/caosdb/server/jobs/core/CheckDatatypePresent.java
@@ -138,7 +138,8 @@ public final class CheckDatatypePresent extends EntityJob {
       }
     } else {
 
-      final EntityInterface validDatatypeEntity = retrieveValidSparseEntityById(datatype.getId());
+      final EntityInterface validDatatypeEntity =
+          retrieveValidSparseEntityById(datatype.getId(), null);
       assertAllowedToUse(validDatatypeEntity);
       datatype.setEntity(validDatatypeEntity);
     }
@@ -151,7 +152,7 @@ public final class CheckDatatypePresent extends EntityJob {
   private void checkIfOverride() throws Message {
     if (getEntity().hasId() && getEntity().getId() > 0) {
       // get data type from database
-      final EntityInterface foreign = retrieveValidSparseEntityById(getEntity().getId());
+      final EntityInterface foreign = retrieveValidSparseEntityById(getEntity().getId(), null);
 
       if (foreign.hasDatatype() && !foreign.getDatatype().equals(getEntity().getDatatype())) {
         // is override!
@@ -179,7 +180,7 @@ public final class CheckDatatypePresent extends EntityJob {
     // the data type of the corresponding abstract property.
     if (getEntity().hasId() && getEntity().getId() > 0) {
       // get from data base
-      final EntityInterface foreign = retrieveValidSparseEntityById(getEntity().getId());
+      final EntityInterface foreign = retrieveValidSparseEntityById(getEntity().getId(), null);
       inheritDatatypeFromForeignEntity(foreign);
     } else if (getEntity().hasId() && getEntity().getId() < 0) {
       // get from container
@@ -218,7 +219,7 @@ public final class CheckDatatypePresent extends EntityJob {
     for (final EntityInterface parent : getEntity().getParents()) {
       EntityInterface parentEntity = null;
       if (parent.getId() > 0) {
-        parentEntity = retrieveValidSparseEntityById(parent.getId());
+        parentEntity = retrieveValidSparseEntityById(parent.getId(), null);
       } else {
         parentEntity = getEntityById(parent.getId());
         runJobFromSchedule(parentEntity, CheckDatatypePresent.class);
diff --git a/src/main/java/caosdb/server/jobs/core/CheckParValid.java b/src/main/java/caosdb/server/jobs/core/CheckParValid.java
index 005053a4a7d59c6c9f1a05171085701c606970b5..560558dce6e494fa4857fefadfa7d22ef79ee5aa 100644
--- a/src/main/java/caosdb/server/jobs/core/CheckParValid.java
+++ b/src/main/java/caosdb/server/jobs/core/CheckParValid.java
@@ -65,7 +65,7 @@ public class CheckParValid extends EntityJob {
             if (parent.getId() >= 0) {
               // id >= 0 (parent is yet in the database)
               // retrieve parent by id
-              final EntityInterface foreign = retrieveValidSparseEntityById(parent.getId());
+              final EntityInterface foreign = retrieveValidSparseEntityById(parent.getId(), null);
               // check permissions for this
               // parentforeign.acceptObserver(o)
               assertAllowedToUse(foreign);
diff --git a/src/main/java/caosdb/server/jobs/core/CheckPropValid.java b/src/main/java/caosdb/server/jobs/core/CheckPropValid.java
index 6a7cbb51556dd0b9c634da4c8a56d20204fc9f50..5e199e848984a4d042c3a755d83a8d57ba91987f 100644
--- a/src/main/java/caosdb/server/jobs/core/CheckPropValid.java
+++ b/src/main/java/caosdb/server/jobs/core/CheckPropValid.java
@@ -55,7 +55,7 @@ public class CheckPropValid extends EntityJob {
             if (property.getId() >= 0) {
 
               final EntityInterface abstractProperty =
-                  retrieveValidSparseEntityById(property.getId());
+                  retrieveValidSparseEntityById(property.getId(), null);
 
               assertAllowedToUse(abstractProperty);
 
diff --git a/src/main/java/caosdb/server/jobs/core/CheckRefidIsaParRefid.java b/src/main/java/caosdb/server/jobs/core/CheckRefidIsaParRefid.java
index fa2b21ccaea7ba3c8bd2b733f8420c213e4f03f7..04a1ac8c5bf6aa42bf2dcec803e6bc1b68a3a7f0 100644
--- a/src/main/java/caosdb/server/jobs/core/CheckRefidIsaParRefid.java
+++ b/src/main/java/caosdb/server/jobs/core/CheckRefidIsaParRefid.java
@@ -94,7 +94,8 @@ public class CheckRefidIsaParRefid extends EntityJob {
                   && getEntityByName(rv.getName()).getRole() == Role.File) {
               } else if (rv.getId() != null
                   && rv.getId() > 0
-                  && retrieveValidSparseEntityById(rv.getId()).getRole() == Role.File) {
+                  && retrieveValidSparseEntityById(rv.getId(), rv.getVersion()).getRole()
+                      == Role.File) {
               } else if (rv.getName() != null
                   && retrieveValidSparseEntityByName(rv.getName()).getRole() == Role.File) {
               } else {
diff --git a/src/main/java/caosdb/server/jobs/core/CheckRefidValid.java b/src/main/java/caosdb/server/jobs/core/CheckRefidValid.java
index 14d98962f48c300ae77fe0811922bf3b1d7eb4ae..36591070cd64558114bb56e22860b95f42fc6463 100644
--- a/src/main/java/caosdb/server/jobs/core/CheckRefidValid.java
+++ b/src/main/java/caosdb/server/jobs/core/CheckRefidValid.java
@@ -3,7 +3,9 @@
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -85,9 +87,12 @@ public class CheckRefidValid extends EntityJob {
   private void checkRefValue(final ReferenceValue ref) throws Message {
     if (ref.getId() != null) {
       if (ref.getId() >= 0) {
-        final EntityInterface referencedValidEntity = retrieveValidSparseEntityById(ref.getId());
+        final EntityInterface referencedValidEntity =
+            retrieveValidSparseEntityById(ref.getId(), ref.getVersion());
         assertAllowedToUse(referencedValidEntity);
-        ref.setEntity(referencedValidEntity);
+
+        // link the entity as versioned entity iff the reference specified a version
+        ref.setEntity(referencedValidEntity, ref.getVersion() != null);
 
       } else {
 
@@ -100,7 +105,9 @@ public class CheckRefidValid extends EntityJob {
           final EntityInterface referencedEntity = getEntityById(ref.getId());
           if (referencedEntity != null) {
             assertAllowedToUse(referencedEntity);
-            ref.setEntity(referencedEntity);
+
+            // link the entity as versioned entity iff the reference specified a version
+            ref.setEntity(referencedEntity, ref.getVersion() != null);
           } else {
             throw ServerMessages.REFERENCED_ENTITY_DOES_NOT_EXIST;
           }
@@ -117,7 +124,9 @@ public class CheckRefidValid extends EntityJob {
 
         if (referencedEntity != null) {
           assertAllowedToUse(referencedEntity);
-          ref.setEntity(referencedEntity);
+
+          // link the entity as versioned entity iff the reference specified a version
+          ref.setEntity(referencedEntity, ref.getVersion() != null);
           if (checkRefEntity(ref)) {
             ref.getEntity().acceptObserver(this);
           }
@@ -125,7 +134,9 @@ public class CheckRefidValid extends EntityJob {
           final EntityInterface referencedValidEntity =
               retrieveValidSparseEntityByName(ref.getName());
           assertAllowedToUse(referencedValidEntity);
-          ref.setEntity(referencedValidEntity);
+
+          // link the entity as versioned entity iff the reference specified a version
+          ref.setEntity(referencedValidEntity, ref.getVersion() != null);
         }
       }
     }
diff --git a/src/main/java/caosdb/server/jobs/core/RemoveDuplicates.java b/src/main/java/caosdb/server/jobs/core/RemoveDuplicates.java
index a104c124293cf07ecc3b871c4bc6b31470ec2a15..a09f66533cb3bc3c7cc01b2d6931bc120f87f7a7 100644
--- a/src/main/java/caosdb/server/jobs/core/RemoveDuplicates.java
+++ b/src/main/java/caosdb/server/jobs/core/RemoveDuplicates.java
@@ -30,17 +30,20 @@ public class RemoveDuplicates extends ContainerJob {
 
   @Override
   protected void run() {
-    final HashSet<EntityInterface> rm = new HashSet<EntityInterface>();
+    // collect duplicates
+    final HashSet<EntityInterface> duplicates = new HashSet<EntityInterface>();
     for (final EntityInterface e : getContainer()) {
-      if (e.hasId() && !rm.contains(e)) {
+      if (e.hasId() && !duplicates.contains(e)) {
         for (final EntityInterface e2 : getContainer()) {
-          if (e2 != e && e.getId().equals(e2.getId())) {
-            rm.add(e2);
+          if (e2 != e && e.getIdVersion().equals(e2.getIdVersion())) {
+            // this is a duplicate of another entity in this container
+            duplicates.add(e2);
           }
         }
       }
     }
-    for (final EntityInterface e : rm) {
+    // remove duplicates.
+    for (final EntityInterface e : duplicates) {
       getContainer().remove(e);
     }
   }
diff --git a/src/main/java/caosdb/server/query/Query.java b/src/main/java/caosdb/server/query/Query.java
index 4eeaafc03a8f94b0df845aff40a923d1f52dc60a..a2d6e48767f5835bd9bf40ce65717bf9e8363f98 100644
--- a/src/main/java/caosdb/server/query/Query.java
+++ b/src/main/java/caosdb/server/query/Query.java
@@ -301,7 +301,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
 
         // ... check for RETRIEVE:ENTITY permission...
         final EntityInterface e =
-            execute(new RetrieveSparseEntity(q.getKey()), query.getAccess()).getEntity();
+            execute(new RetrieveSparseEntity(q.getKey(), null), query.getAccess()).getEntity();
         final EntityACL entityACL = e.getEntityACL();
         if (!entityACL.isPermitted(query.getUser(), EntityPermission.RETRIEVE_ENTITY)) {
           // ... and ignore if not.
@@ -555,7 +555,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
       while (rs.next()) {
         final long t1 = System.currentTimeMillis();
         final Integer id = rs.getInt("id");
-        if (!execute(new RetrieveSparseEntity(id), query.getAccess())
+        if (!execute(new RetrieveSparseEntity(id, null), query.getAccess())
             .getEntity()
             .getEntityACL()
             .isPermitted(query.getUser(), EntityPermission.RETRIEVE_ENTITY)) {
@@ -588,7 +588,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     while (iterator.hasNext()) {
       final long t1 = System.currentTimeMillis();
       final Integer id = iterator.next();
-      if (!execute(new RetrieveSparseEntity(id), getAccess())
+      if (!execute(new RetrieveSparseEntity(id, null), getAccess())
           .getEntity()
           .getEntityACL()
           .isPermitted(getUser(), EntityPermission.RETRIEVE_ENTITY)) {
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index d7897450b2caaa7a70f20ead02ed1bb0c67e192d..97488a6ba2a7984d66bb94332b4d7df0f9478cfb 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -4,7 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019,2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -23,7 +24,6 @@
  */
 package caosdb.server.resource;
 
-import static caosdb.server.utils.Utils.isNonNullInteger;
 import static java.net.URLDecoder.decode;
 
 import caosdb.server.CaosDBException;
@@ -38,7 +38,6 @@ import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
 import java.sql.SQLException;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -77,9 +76,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   private static final XMLParser xmlparser = new XMLParser();
   protected String sRID = null; // Server side request ID
   private String cRID = null; // Client side request ID
-  private String[] requestedItems = null;
-  private ArrayList<Integer> requestedIDs = new ArrayList<Integer>();
-  private ArrayList<String> requestedNames = new ArrayList<String>();
+  private String[] requestedItems = {};
   private WebinterfaceUtils utils;
 
   /** Return the {@link WebinterfaceUtils} instance for this resource. */
@@ -141,21 +138,6 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
       }
 
       this.requestedItems = specifier.split("&");
-      for (final String requestedItem : this.requestedItems) {
-        if (isNonNullInteger(requestedItem)) {
-          final int id = Integer.parseInt(requestedItem);
-          if (id > 0) {
-            getRequestedIDs().add(id);
-          }
-        } else if (requestedItem.equalsIgnoreCase("all")) {
-          getRequestedNames().clear();
-          getRequestedIDs().clear();
-          getRequestedNames().add("all");
-          break;
-        } else {
-          getRequestedNames().add(requestedItem);
-        }
-      }
     }
 
     // flags
@@ -427,16 +409,8 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     }
   }
 
-  public ArrayList<Integer> getRequestedIDs() {
-    return this.requestedIDs;
-  }
-
-  public ArrayList<String> getRequestedNames() {
-    return this.requestedNames;
-  }
-
-  public void setRequestedNames(final ArrayList<String> requestedNames) {
-    this.requestedNames = requestedNames;
+  public String[] getRequestedItems() {
+    return this.requestedItems;
   }
 
   public HashMap<String, String> getFlags() {
diff --git a/src/main/java/caosdb/server/resource/PermissionRulesResource.java b/src/main/java/caosdb/server/resource/PermissionRulesResource.java
index 1610ba372def0b758b76d15d6d839b825a9d96c6..dba1797a03287c503e5898c7e9a7dde6c389630c 100644
--- a/src/main/java/caosdb/server/resource/PermissionRulesResource.java
+++ b/src/main/java/caosdb/server/resource/PermissionRulesResource.java
@@ -46,7 +46,7 @@ public class PermissionRulesResource extends AbstractCaosDBServerResource {
   protected Representation httpGetInChildClass()
       throws ConnectionException, IOException, SQLException, CaosDBException,
           NoSuchAlgorithmException, Exception {
-    final String role = getRequestedNames().get(0);
+    final String role = getRequestedItems()[0];
 
     getUser().checkPermission(ACMPermissions.PERMISSION_RETRIEVE_ROLE_PERMISSIONS(role));
 
@@ -73,7 +73,7 @@ public class PermissionRulesResource extends AbstractCaosDBServerResource {
   public Representation httpPutInChildClass(final Representation entity) throws Exception {
     final Element root = parseEntity(entity).getRootElement();
 
-    final String role = getRequestedNames().get(0);
+    final String role = getRequestedItems()[0];
     final HashSet<PermissionRule> rules = new HashSet<PermissionRule>();
 
     for (final Element e : root.getChildren()) {
diff --git a/src/main/java/caosdb/server/resource/RolesResource.java b/src/main/java/caosdb/server/resource/RolesResource.java
index ef9b30f5b831454fac392317e2c57833657c62d6..0f1b01e3230cf3e73f1c20949f38e180bd8f7e3b 100644
--- a/src/main/java/caosdb/server/resource/RolesResource.java
+++ b/src/main/java/caosdb/server/resource/RolesResource.java
@@ -52,8 +52,8 @@ public class RolesResource extends AbstractCaosDBServerResource {
     final Element root = generateRootElement();
     final Document document = new Document();
 
-    if (!getRequestedNames().isEmpty()) {
-      final String name = getRequestedNames().get(0);
+    if (getRequestedItems().length > 0) {
+      final String name = getRequestedItems()[0];
       if (name != null) {
         getUser().checkPermission(ACMPermissions.PERMISSION_RETRIEVE_ROLE_DESCRIPTION(name));
         final RetrieveRoleTransaction t = new RetrieveRoleTransaction(name);
@@ -78,8 +78,8 @@ public class RolesResource extends AbstractCaosDBServerResource {
   protected Representation httpDeleteInChildClass()
       throws ConnectionException, SQLException, CaosDBException, IOException,
           NoSuchAlgorithmException, Exception {
-    if (!getRequestedNames().isEmpty()) {
-      final String name = getRequestedNames().get(0);
+    if (getRequestedItems().length > 0) {
+      final String name = getRequestedItems()[0];
       if (name != null) {
         final DeleteRoleTransaction t = new DeleteRoleTransaction(name);
         try {
@@ -133,26 +133,28 @@ public class RolesResource extends AbstractCaosDBServerResource {
   protected Representation httpPutInChildClass(final Representation entity)
       throws ConnectionException, JDOMException, Exception, xmlNotWellFormedException {
 
-    final String name = getRequestedNames().get(0);
-    String description = null;
+    if (getRequestedItems().length > 0) {
+      final String name = getRequestedItems()[0];
+      String description = null;
 
-    final Form f = new Form(entity);
-    if (!f.isEmpty()) {
-      description = f.getFirstValue("role_description");
-    }
+      final Form f = new Form(entity);
+      if (!f.isEmpty()) {
+        description = f.getFirstValue("role_description");
+      }
 
-    if (name != null && description != null) {
-      final Role role = new Role();
-      role.name = name;
-      role.description = description;
-      final UpdateRoleTransaction t = new UpdateRoleTransaction(role);
-      try {
-        t.execute();
-      } catch (final Message m) {
-        if (m == ServerMessages.ROLE_DOES_NOT_EXIST) {
-          return error(m, Status.CLIENT_ERROR_NOT_FOUND);
-        } else {
-          throw m;
+      if (name != null && description != null) {
+        final Role role = new Role();
+        role.name = name;
+        role.description = description;
+        final UpdateRoleTransaction t = new UpdateRoleTransaction(role);
+        try {
+          t.execute();
+        } catch (final Message m) {
+          if (m == ServerMessages.ROLE_DOES_NOT_EXIST) {
+            return error(m, Status.CLIENT_ERROR_NOT_FOUND);
+          } else {
+            throw m;
+          }
         }
       }
     }
diff --git a/src/main/java/caosdb/server/resource/UserResource.java b/src/main/java/caosdb/server/resource/UserResource.java
index b77ff8a49035d2be930a3f957e743ed2f0dae067..7dc9111ee332421fd27d1c32632cc458cbf6c800 100644
--- a/src/main/java/caosdb/server/resource/UserResource.java
+++ b/src/main/java/caosdb/server/resource/UserResource.java
@@ -60,9 +60,9 @@ public class UserResource extends AbstractCaosDBServerResource {
     final Document doc = new Document();
     final Element rootElem = generateRootElement();
 
-    if (!getRequestedNames().isEmpty()) {
+    if (getRequestedItems().length > 0) {
       try {
-        final String username = getRequestedNames().get(0);
+        final String username = getRequestedItems()[0];
         final String realm =
             (getRequestAttributes().containsKey("realm")
                 ? (String) getRequestAttributes().get("realm")
@@ -92,7 +92,7 @@ public class UserResource extends AbstractCaosDBServerResource {
 
     try {
       final Form form = new Form(entity);
-      final String username = getRequestedNames().get(0);
+      final String username = getRequestedItems()[0];
       final String realm =
           (getRequestAttributes().containsKey("realm")
               ? (String) getRequestAttributes().get("realm")
@@ -187,7 +187,8 @@ public class UserResource extends AbstractCaosDBServerResource {
     final Document doc = new Document();
     final Element rootElem = generateRootElement();
 
-    final DeleteUserTransaction t = new DeleteUserTransaction(getRequestedNames().get(0));
+    final String username = getRequestedItems()[0];
+    final DeleteUserTransaction t = new DeleteUserTransaction(username);
     try {
       t.execute();
     } catch (final Message m) {
diff --git a/src/main/java/caosdb/server/resource/UserRolesResource.java b/src/main/java/caosdb/server/resource/UserRolesResource.java
index 27e147157675982f036b88981b1dce221abae754..46d51ca4d60bec89b584cefc362e54ab37063503 100644
--- a/src/main/java/caosdb/server/resource/UserRolesResource.java
+++ b/src/main/java/caosdb/server/resource/UserRolesResource.java
@@ -47,7 +47,7 @@ public class UserRolesResource extends AbstractCaosDBServerResource {
   protected Representation httpGetInChildClass()
       throws ConnectionException, IOException, SQLException, CaosDBException,
           NoSuchAlgorithmException, Exception {
-    final String user = getRequestedNames().get(0);
+    final String user = getRequestedItems()[0];
     final String realm =
         (getRequestAttributes().get("realm") != null
             ? (String) getRequestAttributes().get("realm")
@@ -73,7 +73,7 @@ public class UserRolesResource extends AbstractCaosDBServerResource {
   @Override
   protected Representation httpPutInChildClass(final Representation entity)
       throws ConnectionException, JDOMException, Exception, xmlNotWellFormedException {
-    final String user = getRequestedNames().get(0);
+    final String user = getRequestedItems()[0];
     final String realm =
         (getRequestAttributes().get("realm") != null
             ? (String) getRequestAttributes().get("realm")
diff --git a/src/main/java/caosdb/server/resource/transaction/EntityResource.java b/src/main/java/caosdb/server/resource/transaction/EntityResource.java
index dc25bcff3418b3f46f90e965a6b80cdf3111c07f..70e7bdf73adbf5e950fe0a51e119a8dbbe2e054f 100644
--- a/src/main/java/caosdb/server/resource/transaction/EntityResource.java
+++ b/src/main/java/caosdb/server/resource/transaction/EntityResource.java
@@ -30,8 +30,8 @@ import caosdb.server.entity.container.RetrieveContainer;
 import caosdb.server.entity.container.UpdateContainer;
 import caosdb.server.resource.AbstractCaosDBServerResource;
 import caosdb.server.resource.transaction.handlers.FileUploadHandler;
-import caosdb.server.resource.transaction.handlers.IDHandler;
 import caosdb.server.resource.transaction.handlers.RequestHandler;
+import caosdb.server.resource.transaction.handlers.SimpleDeleteRequestHandler;
 import caosdb.server.resource.transaction.handlers.SimpleGetRequestHandler;
 import caosdb.server.resource.transaction.handlers.SimpleWriteHandler;
 import caosdb.server.transaction.Delete;
@@ -72,7 +72,7 @@ public class EntityResource extends AbstractCaosDBServerResource {
   }
 
   protected RequestHandler<DeleteContainer> getDeleteRequestHandler() {
-    return new IDHandler<DeleteContainer>();
+    return new SimpleDeleteRequestHandler();
   }
 
   protected RequestHandler<InsertContainer> getPostRequestHandler() {
diff --git a/src/main/java/caosdb/server/resource/transaction/handlers/IDHandler.java b/src/main/java/caosdb/server/resource/transaction/handlers/IDHandler.java
deleted file mode 100644
index e641ed416c86075d23a37d70ad791c26a820a966..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/resource/transaction/handlers/IDHandler.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package caosdb.server.resource.transaction.handlers;
-
-import caosdb.server.entity.container.EntityByIdContainer;
-import caosdb.server.resource.transaction.EntityResource;
-
-public class IDHandler<T extends EntityByIdContainer> extends RequestHandler<T> {
-
-  @Override
-  public void handle(final EntityResource t, final T container) throws Exception {
-    for (final int id : t.getRequestedIDs()) {
-      container.add(id);
-    }
-  }
-}
diff --git a/src/main/java/caosdb/server/resource/transaction/handlers/SimpleDeleteRequestHandler.java b/src/main/java/caosdb/server/resource/transaction/handlers/SimpleDeleteRequestHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..7fb416451bd8f053df5231eb1002db0c03544f99
--- /dev/null
+++ b/src/main/java/caosdb/server/resource/transaction/handlers/SimpleDeleteRequestHandler.java
@@ -0,0 +1,55 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2018 Research Group Biomedical Physics,
+ *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+package caosdb.server.resource.transaction.handlers;
+
+import caosdb.server.entity.container.DeleteContainer;
+import caosdb.server.resource.transaction.EntityResource;
+
+public class SimpleDeleteRequestHandler extends RequestHandler<DeleteContainer> {
+
+  @Override
+  public void handle(final EntityResource t, final DeleteContainer container) throws Exception {
+    // TODO a lot of code duplication, see SimpleGetRequestHandle#handle.
+    // However, this is about to be changed again when string ids are
+    // introduced, anyways. So we just leave it.
+    for (final String item : t.getRequestedItems()) {
+      String[] elem = item.split("@", 1);
+      Integer id = null;
+      String version = null;
+      try {
+        id = Integer.parseInt(elem[0]);
+      } catch (NumberFormatException e) {
+        // pass
+      }
+      if (elem.length > 1) {
+        version = elem[1];
+      }
+
+      if (id != null) {
+        container.add(id, version);
+      }
+    }
+  }
+}
diff --git a/src/main/java/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java b/src/main/java/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java
index 8fbb9d25cb33d2d0a75ce8405a164c1e8c9ce0fd..18c89d46966a3e22b1a9066b84dcf2d05efd122c 100644
--- a/src/main/java/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java
+++ b/src/main/java/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java
@@ -25,13 +25,29 @@ package caosdb.server.resource.transaction.handlers;
 import caosdb.server.entity.container.RetrieveContainer;
 import caosdb.server.resource.transaction.EntityResource;
 
-public class SimpleGetRequestHandler extends IDHandler<RetrieveContainer> {
+public class SimpleGetRequestHandler extends RequestHandler<RetrieveContainer> {
 
   @Override
   public void handle(final EntityResource t, final RetrieveContainer container) throws Exception {
-    super.handle(t, container);
-    for (final String name : t.getRequestedNames()) {
-      container.add(name);
+    for (final String item : t.getRequestedItems()) {
+      String[] elem = item.split("@", 2);
+      Integer id = null;
+      String name = null;
+      String version = null;
+      try {
+        id = Integer.parseInt(elem[0]);
+      } catch (NumberFormatException e) {
+        name = elem[0];
+      }
+      if (elem.length > 1) {
+        version = elem[1];
+      }
+
+      if (id != null) {
+        container.add(id, version);
+      } else {
+        container.add(name);
+      }
     }
   }
 }
diff --git a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
index fa51d5ff49c9693d2216fd5dc168873f850e6e25..faeb96e02738395647abb75fca3eaafe18040ad3 100644
--- a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
+++ b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
@@ -166,8 +166,11 @@ public class ServerSideScriptingCaller {
       if (f.getPath() == null || f.getPath().isEmpty()) {
         throw new CaosDBException("The path must not be null or empty!");
       }
-      caosdb.server.utils.FileUtils.createSymlink(
-          getUploadFilesDir().toPath().resolve(f.getPath()).toFile(), f.getFile());
+      File link = getUploadFilesDir().toPath().resolve(f.getPath()).toFile();
+      if (!link.getParentFile().exists()) {
+        link.getParentFile().mkdirs();
+      }
+      caosdb.server.utils.FileUtils.createSymlink(link, f.getFile());
     }
   }
 
diff --git a/src/main/java/caosdb/server/transaction/ChecksumUpdater.java b/src/main/java/caosdb/server/transaction/ChecksumUpdater.java
index 30313478c263a131658005daa39da3b495c85eb2..29603fc5833837dfef4a16e8886cdca995c41b94 100644
--- a/src/main/java/caosdb/server/transaction/ChecksumUpdater.java
+++ b/src/main/java/caosdb/server/transaction/ChecksumUpdater.java
@@ -141,7 +141,7 @@ public class ChecksumUpdater extends WriteTransaction<TransactionContainer> impl
           instance.running = false;
           return null;
         }
-        return execute(new RetrieveSparseEntity(id), weakAccess).getEntity();
+        return execute(new RetrieveSparseEntity(id, null), weakAccess).getEntity();
       }
     } catch (final Exception e) {
       e.printStackTrace();
diff --git a/src/main/java/caosdb/server/transaction/Insert.java b/src/main/java/caosdb/server/transaction/Insert.java
index 627ada52655c5e7802e98172d1c32f8280a95fb4..c1d53f3051eb65efd9d658179e514e13863c313b 100644
--- a/src/main/java/caosdb/server/transaction/Insert.java
+++ b/src/main/java/caosdb/server/transaction/Insert.java
@@ -26,6 +26,7 @@ import caosdb.server.database.access.Access;
 import caosdb.server.database.backend.transaction.InsertEntity;
 import caosdb.server.entity.EntityInterface;
 import caosdb.server.entity.FileProperties;
+import caosdb.server.entity.Version;
 import caosdb.server.entity.container.InsertContainer;
 import caosdb.server.entity.container.TransactionContainer;
 import caosdb.server.permissions.EntityACL;
@@ -103,6 +104,10 @@ public class Insert extends WriteTransaction<InsertContainer> {
   public void insert(final TransactionContainer container, final Access access) throws Exception {
     if (container.getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) {
       execute(new InsertEntity(container), access);
+      for (EntityInterface e : container) {
+        // TODO move to InsertEntity transaction
+        e.setVersion(new Version(e.getVersion().getId(), this.getTimestamp()));
+      }
     }
   }
 
diff --git a/src/main/java/caosdb/server/transaction/WriteTransaction.java b/src/main/java/caosdb/server/transaction/WriteTransaction.java
index e67ae0600bc573efbdf67b9b341299fe1fe3ca00..ec37d19d6f5a4663c0af0adda186c282215a302b 100644
--- a/src/main/java/caosdb/server/transaction/WriteTransaction.java
+++ b/src/main/java/caosdb/server/transaction/WriteTransaction.java
@@ -64,4 +64,8 @@ public abstract class WriteTransaction<C extends TransactionContainer> extends T
       }
     }
   }
+
+  public String getSRID() {
+    return getContainer().getRequestId();
+  }
 }
diff --git a/src/main/java/caosdb/server/utils/Utils.java b/src/main/java/caosdb/server/utils/Utils.java
index 085be51d0929ce76d10ed5e4e8c32696e0b48476..752d828d0e89e790aa7d42e1358b91d3fb5f3c1b 100644
--- a/src/main/java/caosdb/server/utils/Utils.java
+++ b/src/main/java/caosdb/server/utils/Utils.java
@@ -47,24 +47,6 @@ public class Utils {
   /** Secure random number generator, for secret random numbers. */
   private static final SecureRandom srand = new SecureRandom();
 
-  /**
-   * Check whether obj is non-null and can be parsed to an integer.
-   *
-   * @param obj The object to check.
-   * @return true if obj is not null and obj.toString() can be parsed as an integer
-   */
-  public static boolean isNonNullInteger(final Object obj) {
-    if (obj == null) {
-      return false;
-    }
-    try {
-      Integer.parseInt(obj.toString());
-    } catch (final NumberFormatException e) {
-      return false;
-    }
-    return true;
-  }
-
   /**
    * Regular expression pattern that checks a mail for RFC822 compliance.
    *
diff --git a/src/test/java/caosdb/server/entity/xml/PropertyToElementStrategyTest.java b/src/test/java/caosdb/server/entity/xml/PropertyToElementStrategyTest.java
index 7e0feec8915283170b2964420d3ef1f6a42c0031..0d60ebd70feebe2e8e8810f296908e8b3a74100e 100644
--- a/src/test/java/caosdb/server/entity/xml/PropertyToElementStrategyTest.java
+++ b/src/test/java/caosdb/server/entity/xml/PropertyToElementStrategyTest.java
@@ -89,7 +89,7 @@ public class PropertyToElementStrategyTest {
     PropertyToElementStrategy strategy = new PropertyToElementStrategy();
     SetFieldStrategy setFieldStrategy = new SetFieldStrategy().addSelection(parse("height"));
     EntityInterface property = windowProperty;
-    ((ReferenceValue) property.getValue()).setEntity(window);
+    ((ReferenceValue) property.getValue()).setEntity(window, true);
     Element element = strategy.toElement(property, setFieldStrategy);
     assertEquals("Property", element.getName());
     assertEquals("2345", element.getAttributeValue("id"));