diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
index ec0c0c1b0a067d529ecb72309d7fe83a8b37f819..7af4022595f64b909231c72c83ae1e151a9d06c1 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
@@ -27,10 +27,14 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
+import org.apache.shiro.SecurityUtils;
+import org.caosdb.server.database.DatabaseUtils;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.entity.Role;
+import org.caosdb.server.permissions.EntityACL;
+import org.caosdb.server.permissions.EntityPermission;
 
 public class MySQLRetrieveAll extends MySQLTransaction implements RetrieveAllImpl {
 
@@ -38,17 +42,20 @@ public class MySQLRetrieveAll extends MySQLTransaction implements RetrieveAllImp
     super(access);
   }
 
-  public static final String STMT_GET_ALL_HEAD = "Select id from entities where id > 99";
+  public static final String STMT_GET_ALL_HEAD =
+      "SELECT e.id AS ID, a.acl AS ACL FROM entities AS e JOIN entity_acl AS a ON (e.acl = a.id) WHERE e.id > 99";
   public static final String STMT_ENTITY_WHERE_CLAUSE =
-      " AND ( role=? OR role='"
+      " AND ( e.role='"
+          + Role.Record
+          + "' OR e.role='"
           + Role.RecordType
-          + "' OR role='"
+          + "' OR e.role='"
           + Role.Property
-          + "' OR role='"
+          + "' OR e.role='"
           + Role.File
           + "'"
           + " )";
-  public static final String STMT_OTHER_ROLES = " AND role=?";
+  public static final String STMT_OTHER_ROLES = " AND e.role=?";
 
   @Override
   public List<Integer> execute(final String role) throws TransactionException {
@@ -58,10 +65,7 @@ public class MySQLRetrieveAll extends MySQLTransaction implements RetrieveAllImp
               + (role.equalsIgnoreCase("ENTITY") ? STMT_ENTITY_WHERE_CLAUSE : STMT_OTHER_ROLES);
       final PreparedStatement stmt = prepareStatement(STMT_GET_ALL);
 
-      if (role.equalsIgnoreCase("ENTITY")) {
-        stmt.setString(1, Role.Record.toString());
-
-      } else {
+      if (!role.equalsIgnoreCase("ENTITY")) {
         stmt.setString(1, role);
       }
 
@@ -69,7 +73,11 @@ public class MySQLRetrieveAll extends MySQLTransaction implements RetrieveAllImp
       try {
         final ArrayList<Integer> ret = new ArrayList<Integer>();
         while (rs.next()) {
-          ret.add(rs.getInt(1));
+          String acl = DatabaseUtils.bytes2UTF8(rs.getBytes("ACL"));
+          if (EntityACL.deserialize(acl)
+              .isPermitted(SecurityUtils.getSubject(), EntityPermission.RETRIEVE_ENTITY)) {
+            ret.add(rs.getInt("ID"));
+          }
         }
         return ret;
       } finally {
diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java
index 27570ec81636c7bb9a20bde04fb68f07b4956fea..5522b9be33f2225412c0ba473d7c5c0eec2e4507 100644
--- a/src/main/java/org/caosdb/server/entity/Entity.java
+++ b/src/main/java/org/caosdb/server/entity/Entity.java
@@ -36,6 +36,7 @@ import org.apache.shiro.authz.Permission;
 import org.apache.shiro.subject.Subject;
 import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.CaosDBException;
+import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.database.proto.SparseEntity;
 import org.caosdb.server.database.proto.VerySparseEntity;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
@@ -104,10 +105,12 @@ public class Entity extends AbstractObservable implements EntityInterface {
   public void checkPermission(final Subject subject, final Permission permission) {
     try {
       if (!this.hasPermission(subject, permission)) {
+        String user = "The current user ";
+        if (subject.getPrincipal() instanceof Principal) {
+          user = ((Principal) subject.getPrincipal()).getUsername();
+        }
         throw new AuthorizationException(
-            subject.getPrincipal().toString()
-                + " doesn't have permission "
-                + permission.toString());
+            user + " doesn't have permission " + permission.toString());
       }
     } catch (final NullPointerException e) {
       throw new AuthorizationException("This entity doesn't have an ACL!");
diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
index 86b65fa8b8e875b700b947b8e63bf175e7a7c511..7a7056eb79ce1b5a322a71492cc483821a3b8de7 100644
--- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
+++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
@@ -25,12 +25,14 @@ package org.caosdb.server.jobs.core;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import org.apache.shiro.subject.Subject;
@@ -288,12 +290,6 @@ public abstract class EntityStateJob extends EntityJob {
     private EntityACL stateACL = null;
     private Map<String, String> stateProperties;
 
-    public State(String stateName, String stateModelName) throws Message {
-      // TODO Make constructors private or protected and outsource to caching factory methods.
-      this.stateName = stateName;
-      this.stateModelName = stateModelName;
-    }
-
     public State(EntityInterface stateEntity, EntityInterface stateModelEntity) throws Message {
       this.stateEntity = stateEntity;
       this.stateDescription = stateEntity.getDescription();
@@ -364,10 +360,7 @@ public abstract class EntityStateJob extends EntityJob {
     @Override
     public void addToElement(Element ret) {
       Element e = new Element(STATE_XML_TAG);
-      if (this.stateProperties == null && this.stateEntity != null) {
-        this.stateProperties = createStateProperties(this.stateEntity);
-      }
-      if (this.stateProperties != null && this.stateProperties.size() > 0) {
+      if (this.stateProperties != null) {
         this.stateProperties.forEach(
             (String key, String value) -> {
               e.setAttribute(key, value);
@@ -385,19 +378,13 @@ public abstract class EntityStateJob extends EntityJob {
       if (this.stateId != null) {
         e.setAttribute(STATE_ATTRIBUTE_ID, Integer.toString(this.stateId));
       }
-      if (this.stateModelEntity != null) {
-        try {
-          this.getStateModel()
-              .transitions
-              .forEach(
-                  (Transition t) -> {
-                    if (t.isFromState(this) && t.isPermitted(getUser())) {
-                      e.addContent(t.toElement());
-                    }
-                  });
-        } catch (Message m) {
-          getEntity().addError(m);
-        }
+      if (this.stateModel != null) {
+        this.stateModel.transitions.forEach(
+            (Transition t) -> {
+              if (t.isFromState(this) && t.isPermitted(getUser())) {
+                e.addContent(t.toElement());
+              }
+            });
       }
       ret.addContent(e);
     }
@@ -412,7 +399,7 @@ public abstract class EntityStateJob extends EntityJob {
 
     public StateModel getStateModel() throws Message {
       if (this.stateModel == null) {
-        this.stateModel = new StateModel(getStateModelEntity());
+        this.stateModel = new StateModel(this.stateModelEntity);
       }
       return this.stateModel;
     }
@@ -448,20 +435,11 @@ public abstract class EntityStateJob extends EntityJob {
       return stateProperty;
     }
 
-    public EntityInterface getStateEntity() throws Message {
-      if (this.stateEntity == null) {
-        this.stateEntity = retrieveStateEntity(this.getStateName());
-        this.stateDescription = this.stateEntity.getDescription();
-        this.stateId = this.stateEntity.getId();
-        this.stateACL = createStateACL(this.stateEntity.getEntityACL());
-      }
+    public EntityInterface getStateEntity() {
       return this.stateEntity;
     }
 
-    public EntityInterface getStateModelEntity() throws Message {
-      if (this.stateModelEntity == null) {
-        this.stateModelEntity = retrieveStateModelEntity(this.getStateModelName());
-      }
+    public EntityInterface getStateModelEntity() {
       return this.stateModelEntity;
     }
 
@@ -570,7 +548,13 @@ public abstract class EntityStateJob extends EntityJob {
         for (IndexedSingleValue val : vals) {
           if (val.getWrapped() instanceof ReferenceValue) {
             Integer refid = ((ReferenceValue) val.getWrapped()).getId();
-            EntityInterface transition = retrieveValidEntity(refid);
+
+            String key = "transition" + Integer.toString(refid);
+            EntityInterface transition = getCached(key);
+            if (transition == null) {
+              transition = retrieveValidEntity(refid);
+              putCache(key, transition);
+            }
             result.add(new Transition(transition));
           }
         }
@@ -653,7 +637,7 @@ public abstract class EntityStateJob extends EntityJob {
     }
   }
 
-  protected EntityInterface retrieveStateEntity(String stateName) throws Message {
+  private EntityInterface retrieveStateEntity(String stateName) throws Message {
     try {
       return retrieveValidEntity(retrieveValidIDByName(stateName));
     } catch (EntityDoesNotExistException e) {
@@ -661,7 +645,7 @@ public abstract class EntityStateJob extends EntityJob {
     }
   }
 
-  protected EntityInterface retrieveStateModelEntity(String stateModel) throws Message {
+  private EntityInterface retrieveStateModelEntity(String stateModel) throws Message {
     try {
       return retrieveValidEntity(retrieveValidIDByName(stateModel));
     } catch (EntityDoesNotExistException e) {
@@ -670,7 +654,12 @@ public abstract class EntityStateJob extends EntityJob {
   }
 
   protected EntityInterface getStateRecordType() throws Message {
-    return retrieveValidSparseEntityByName(STATE_RECORD_TYPE_NAME);
+    EntityInterface stateRecordType = getCached(STATE_RECORD_TYPE_NAME);
+    if (stateRecordType == null) {
+      stateRecordType = retrieveValidSparseEntityByName(STATE_RECORD_TYPE_NAME);
+      putCache(STATE_RECORD_TYPE_NAME, stateRecordType);
+    }
+    return stateRecordType;
   }
 
   protected State getState() {
@@ -724,11 +713,11 @@ public abstract class EntityStateJob extends EntityJob {
 
   /** Get the {@code ClientMessage}s which denote a state. */
   protected List<ClientMessage> getStateClientMessages(EntityInterface entity, boolean remove) {
-    Iterator<Message> stateMessages = entity.getMessages(STATE_XML_TAG).iterator();
+    Iterator<ToElementable> stateMessages = entity.getMessages().iterator();
     List<ClientMessage> result = new ArrayList<>();
     while (stateMessages.hasNext()) {
-      Message s = stateMessages.next();
-      if (s instanceof ClientMessage) {
+      ToElementable s = stateMessages.next();
+      if (s instanceof ClientMessage && STATE_XML_TAG.equals(((ClientMessage) s).getType())) {
         if (remove) {
           stateMessages.remove();
         }
@@ -751,7 +740,22 @@ public abstract class EntityStateJob extends EntityJob {
     if (stateName == null) {
       throw STATE_NOT_SPECIFIED;
     }
-    return new State(stateName, stateModel);
+    String stateModelKey = "statemodel:" + stateModel;
+
+    EntityInterface stateModelEntity = getCached(stateModelKey);
+    if (stateModelEntity == null) {
+      stateModelEntity = retrieveStateModelEntity(stateModel);
+      putCache(stateModelKey, stateModelEntity);
+    }
+
+    String stateKey = "namestate:" + stateName;
+
+    EntityInterface stateEntity = getCached(stateKey);
+    if (stateEntity == null) {
+      stateEntity = retrieveStateEntity(stateName);
+      putCache(stateKey, stateEntity);
+    }
+    return new State(stateEntity, stateModelEntity);
   }
 
   /**
@@ -765,15 +769,15 @@ public abstract class EntityStateJob extends EntityJob {
    * @throws Message
    */
   protected State createState(Property p) throws Message {
-    // TODO this should be cached
     try {
       p.parseValue();
       ReferenceValue refid = (ReferenceValue) p.getValue();
-      EntityInterface stateEntity = cache.get("state" + Integer.toString(refid.getId()));
-      boolean cached = true;
-      if (stateEntity == null || !cached) {
+      String key = "idstate" + Integer.toString(refid.getId());
+
+      EntityInterface stateEntity = getCached(key);
+      if (stateEntity == null) {
         stateEntity = retrieveValidEntity(refid.getId());
-        cache.put("state" + Integer.toString(refid.getId()), stateEntity);
+        putCache(key, stateEntity);
       }
 
       EntityInterface stateModelEntity = findStateModel(stateEntity);
@@ -786,12 +790,13 @@ public abstract class EntityStateJob extends EntityJob {
   }
 
   private static final Map<String, EntityInterface> cache = new HashMap<>();
+  private static final Set<Integer> id_in_cache = new HashSet<>();
 
   EntityInterface findStateModel(EntityInterface stateEntity) throws Exception {
-    // TODO this should be cached
-
     boolean cached = true;
-    EntityInterface result = cache.get("modelof" + Integer.toString(stateEntity.getId()));
+    String key = "modelof" + Integer.toString(stateEntity.getId());
+
+    EntityInterface result = getCached(key);
     if (result != null && cached) {
       return result;
     }
@@ -809,7 +814,40 @@ public abstract class EntityStateJob extends EntityJob {
             c);
     query.execute(getTransaction().getAccess());
     result = retrieveValidEntity(c.get(0).getId());
-    cache.put("modelof" + Integer.toString(stateEntity.getId()), result);
+    putCache(key, result);
+    return result;
+  }
+
+  private EntityInterface getCached(String key) {
+    EntityInterface result;
+    synchronized (cache) {
+      result = cache.get(key);
+    }
     return result;
   }
+
+  private void putCache(String key, EntityInterface value) {
+    synchronized (cache) {
+      id_in_cache.add(value.getId());
+      cache.put(key, value);
+    }
+  }
+
+  protected void removeCached(EntityInterface entity) {
+    synchronized (cache) {
+      if (id_in_cache.contains(entity.getId())) {
+        id_in_cache.remove(entity.getId());
+
+        List<String> remove = new LinkedList<>();
+        for (Entry<String, EntityInterface> entry : cache.entrySet()) {
+          if (entry.getValue().getId().equals(entity.getId())) {
+            remove.add(entry.getKey());
+          }
+        }
+        for (String key : remove) {
+          cache.remove(key);
+        }
+      }
+    }
+  }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java b/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java
index e21d76c9e5e5f356026cb94d8d8a5f098caeaa8e..bda4b8ebe885aa49df13ecf36ab14663c7ead270 100644
--- a/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java
+++ b/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java
@@ -16,6 +16,8 @@ public class InheritInitialState extends EntityStateJob {
       State parentState = getFirstParentState();
       if (parentState != null) {
         getEntity().addMessage(parentState);
+        parentState.getStateEntity();
+        getEntity().setEntityACL(parentState.getStateACL());
       }
     } catch (Message e) {
       getEntity().addError(e);
diff --git a/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java b/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
index 5093d86a66a9524566fecc127b18fd034f607212..c9f1c7b7b6591ae58375f457ce7be9ae20c66a4b 100644
--- a/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
+++ b/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Objects;
 import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.entity.ClientMessage;
+import org.caosdb.server.entity.DeleteEntity;
 import org.caosdb.server.entity.Entity;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.InsertEntity;
@@ -35,6 +36,7 @@ import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Message.MessageType;
 import org.caosdb.server.entity.Role;
 import org.caosdb.server.entity.UpdateEntity;
+import org.caosdb.server.entity.WritableEntity;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.jobs.JobAnnotation;
 import org.caosdb.server.jobs.JobExecutionTime;
@@ -76,6 +78,9 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
         appendJob(MakeStateProperty.class);
         appendJob(MakeStateMessage.class);
       }
+      if (getEntity() instanceof WritableEntity || getEntity() instanceof DeleteEntity) {
+        removeCached(getEntity());
+      }
     }
   }
 
@@ -127,7 +132,9 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
             MessageType.Error, "Currently, each entity can only have one state at a time.");
       } else if (states.size() == 1) {
         newState = states.get(0);
-        transferEntityACL(getEntity(), newState);
+        if (getEntity().getRole() == Role.Record) {
+          transferEntityACL(getEntity(), newState);
+        }
       }
     } catch (Message m) {
       getEntity().addError(m);
diff --git a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
index b0fc4bad949588d3a34c64f9fc1d5acf6135d0cb..6e98704a5f6d73199993f2ed531e6d6277934e64 100644
--- a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
+++ b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
@@ -58,23 +58,15 @@ public class MakeStateMessage extends EntityStateJob {
       try {
         // fetch all state properties and remove them from the entity (indicated by "true")
         List<Property> stateProperties = getStateProperties(true);
+        State stateMessage = getState(false);
 
-        // only add the State during Retrieve. In all other cases, the State is already present.
-        if (stateProperties != null
-            && stateProperties.size() > 0
-            && getTransaction() instanceof Retrieve) {
-          if (isSparse()) {
-            for (Property s : stateProperties) {
-              getEntity().addMessage(getSparseStateMessage(s));
-            }
-          } else {
-            for (Property s : stateProperties) {
-              State stateMessage = createState(s);
-              stateMessage
-                  .getStateModel(); // trigger retrieval of state model because when the XML Writer
-              // calls the addToElement method, it is to late.
-              getEntity().addMessage(stateMessage);
-            }
+        if (stateMessage != null) {
+          // trigger retrieval of state model because when the XML Writer calls the addToElement
+          // method, it is to late.
+          stateMessage.getStateModel();
+        } else if (stateProperties != null && stateProperties.size() > 0) {
+          for (Property s : stateProperties) {
+            getEntity().addMessage(getMessage(s, isSparse()));
           }
         }
       } catch (Message e) {
@@ -83,6 +75,18 @@ public class MakeStateMessage extends EntityStateJob {
     }
   }
 
+  private ToElementable getMessage(Property s, boolean sparse) throws Message {
+    if (sparse) {
+      return getSparseStateMessage(s);
+    }
+    State stateMessage = createState(s);
+
+    // trigger retrieval of state model because when the XML Writer calls the addToElement method,
+    // it is to late.
+    stateMessage.getStateModel();
+    return stateMessage;
+  }
+
   private ToElementable getSparseStateMessage(Property s) {
     return new ToElementable() {
       @Override
diff --git a/src/main/java/org/caosdb/server/permissions/EntityACL.java b/src/main/java/org/caosdb/server/permissions/EntityACL.java
index df1915bd460079eb52dfc6d4f3bbf4fe42795918..5adaca78fc03e760a42f1715d2a6cfff56826fc9 100644
--- a/src/main/java/org/caosdb/server/permissions/EntityACL.java
+++ b/src/main/java/org/caosdb/server/permissions/EntityACL.java
@@ -379,7 +379,9 @@ public class EntityACL {
 
   public Element getPermissionsFor(final Subject subject) {
     final Element ret = new Element("Permissions");
-    final Set<EntityPermission> permissionsFor = getPermissionsFor(subject, this.acl);
+    final List<EntityACI> localAcl = new ArrayList<>(this.acl);
+    localAcl.addAll(GLOBAL_PERMISSIONS.acl);
+    final Set<EntityPermission> permissionsFor = getPermissionsFor(subject, localAcl);
     for (final EntityPermission p : permissionsFor) {
       ret.addContent(p.toElement());
     }