diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java
index 92f5aa841aed5f759d433076700a8b1ce13e26dc..bfb1497d71a5ede778255f6640038d647a001547 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java
@@ -26,6 +26,7 @@ package org.caosdb.server.database.backend.transaction;
 
 import java.util.LinkedList;
 import java.util.List;
+import org.apache.shiro.subject.Subject;
 import org.caosdb.server.database.BackendTransaction;
 import org.caosdb.server.database.exceptions.EntityDoesNotExistException;
 import org.caosdb.server.datatype.CollectionValue;
@@ -38,6 +39,7 @@ import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.entity.Role;
 import org.caosdb.server.entity.container.Container;
 import org.caosdb.server.entity.wrapper.Property;
+import org.caosdb.server.permissions.EntityPermission;
 import org.caosdb.server.query.Query;
 import org.caosdb.server.query.Query.Selection;
 import org.caosdb.server.utils.EntityStatus;
@@ -57,19 +59,22 @@ import org.caosdb.server.utils.EntityStatus;
 public class RetrieveFullEntityTransaction extends BackendTransaction {
 
   private final Container<? extends EntityInterface> container;
+  private final Subject subject;
 
-  public RetrieveFullEntityTransaction(final EntityInterface entity) {
-    final Container<EntityInterface> c = new Container<>();
-    c.add(entity);
-    this.container = c;
+  @SuppressWarnings("unchecked")
+  public RetrieveFullEntityTransaction(final EntityInterface entity, Subject subject) {
+    this(new Container<>(), subject);
+    ((Container<EntityInterface>) this.container).add(entity);
   }
 
-  public RetrieveFullEntityTransaction(final Container<? extends EntityInterface> container) {
+  public RetrieveFullEntityTransaction(
+      final Container<? extends EntityInterface> container, final Subject subject) {
     this.container = container;
+    this.subject = subject;
   }
 
-  public RetrieveFullEntityTransaction(final EntityID id) {
-    this(new RetrieveEntity(id));
+  public RetrieveFullEntityTransaction(final EntityID id, Subject subject) {
+    this(new RetrieveEntity(id), subject);
   }
 
   @Override
@@ -220,9 +225,17 @@ public class RetrieveFullEntityTransaction extends BackendTransaction {
   private void resolveReferenceValue(
       final ReferenceValue value, final List<Selection> selections, final String propertyName) {
     final RetrieveEntity ref = new RetrieveEntity(value.getId());
-    // recursion! (Only for the matching selections)
-    retrieveFullEntity(ref, getSubSelects(selections, propertyName));
-    value.setEntity(ref, true);
+
+    if (this.subject != null) {
+      // recursion! (Only for the matching selections)
+      retrieveFullEntity(ref, getSubSelects(selections, propertyName));
+
+      // check whether the referenced entity is readable
+      if (!ref.getEntityACL().isPermitted(this.subject, EntityPermission.RETRIEVE_ENTITY)) {
+        return;
+      }
+      value.setEntity(ref, true);
+    }
   }
 
   /**
diff --git a/src/main/java/org/caosdb/server/entity/RetrieveEntity.java b/src/main/java/org/caosdb/server/entity/RetrieveEntity.java
index 00a120fbf023024eae8acc5788a9260d020a956d..e1b9e8c06fbc0d14a8719bd50635464a4c117326 100644
--- a/src/main/java/org/caosdb/server/entity/RetrieveEntity.java
+++ b/src/main/java/org/caosdb/server/entity/RetrieveEntity.java
@@ -24,6 +24,14 @@
  */
 package org.caosdb.server.entity;
 
+/**
+ * Entity which is to be retrieved (i.e. read-only).
+ *
+ * <p>This class only exposes those constructors which are necessary for Entity which are to be
+ * retrieved (e.g. Entity(id)) and hide the others (e.g. Entity(XML-Represenation)).
+ *
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
+ */
 public class RetrieveEntity extends Entity {
 
   public RetrieveEntity() {
diff --git a/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java b/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java
index 35d94aa79fd62a04f8b0dae1a17829a1106c2d97..bbe1749c91e2b102a7c41e78e00cd5f02904a5ff 100644
--- a/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java
+++ b/src/main/java/org/caosdb/server/entity/xml/EntityToElementStrategy.java
@@ -169,14 +169,16 @@ public class EntityToElementStrategy implements ToElementStrategy {
         // processing SELECT Queries.
         final EntityInterface ref = ((ReferenceValue) entity.getValue()).getEntity();
         if (ref != null) {
-          if (entity.hasDatatype()) {
-            setDatatype(entity, element);
-          }
           ref.addToElement(element, serializeFieldStrategy);
-          // the referenced entity has been appended. Return here to suppress
-          // adding the reference id as well.
-          return;
+        } else {
+          entity.getValue().addToElement(element);
+        }
+        if (entity.hasDatatype()) {
+          setDatatype(entity, element);
         }
+        // the referenced entity has been appended. Return here to suppress
+        // adding the reference id as well.
+        return;
       } else if (entity.isReferenceList() && serializeFieldStrategy.isToBeSet("_referenced")) {
         // Append the all referenced entities. This needs to be done when we are
         // processing SELECT Queries.
diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java
index 6ee5f9d0122aa417c357e71ef6d6ea0516007215..597495f2d16d6e27229660d2341962d1dce20f91 100644
--- a/src/main/java/org/caosdb/server/query/POV.java
+++ b/src/main/java/org/caosdb/server/query/POV.java
@@ -405,6 +405,12 @@ public class POV implements EntityFilterInterface {
     }
     final long t2 = System.currentTimeMillis();
     query.addBenchmark(measurement(".initPOVRefidsTable()"), t2 - t1);
+
+    if (this.refIdsTable != null) {
+      query.getQuery().filterIntermediateResult(this.refIdsTable);
+    }
+    final long t3 = System.currentTimeMillis();
+    query.addBenchmark(measurement(".filterRefidsWithoutRetrievePermission"), t3 - t2);
     try (PreparedStatement stmt =
         query.getConnection().prepareCall("call initPOVPropertiesTable(?,?,?)")) {
       // initPOVPropertiesTable(in pid INT UNSIGNED, in pname
@@ -455,12 +461,12 @@ public class POV implements EntityFilterInterface {
         }
       }
     }
-    final long t3 = System.currentTimeMillis();
-    query.addBenchmark(measurement(""), t3 - t2);
+    final long t4 = System.currentTimeMillis();
+    query.addBenchmark(measurement(""), t4 - t3);
 
     if (this.refIdsTable != null) {
       query.getQuery().applyQueryTemplates(query, this.refIdsTable);
-      query.addBenchmark(measurement(".applyQueryTemplates()"), System.currentTimeMillis() - t3);
+      query.addBenchmark(measurement(".applyQueryTemplates()"), System.currentTimeMillis() - t4);
     }
 
     if (hasSubProperty() && this.targetSet != null) {
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index 117176ec5c6a1fcb10e588b860926b02eb76a2a9..168b7ac8b987ecfd75355580fd6af6409a54c4ac 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -72,11 +72,21 @@ import org.caosdb.server.permissions.EntityPermission;
 import org.caosdb.server.query.CQLParser.CqContext;
 import org.caosdb.server.query.CQLParsingErrorListener.ParsingError;
 import org.caosdb.server.transaction.EntityTransactionInterface;
+import org.caosdb.server.transaction.Retrieve;
 import org.caosdb.server.transaction.Transaction;
 import org.caosdb.server.transaction.WriteTransaction;
 import org.jdom2.Element;
 import org.slf4j.Logger;
 
+/**
+ * This class represents a single, complete Query execution from the parsing of the query string to
+ * the resulting list of entity ids.
+ *
+ * <p>This class handles caching of queries and checking retrieve permissions as well. It does not,
+ * however, retrieve the resulting entities; this is handled by the {@link Retrieve} class.
+ *
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
+ */
 public class Query implements QueryInterface, ToElementable, EntityTransactionInterface {
 
   /** Class which represents the selection of (sub)properties. */
diff --git a/src/main/java/org/caosdb/server/transaction/Retrieve.java b/src/main/java/org/caosdb/server/transaction/Retrieve.java
index 643c1201b530af9821c9a5c1a7b62f7c5d04cf52..54500c5932d7d8e4af41b5d88f79a2f5f914492f 100644
--- a/src/main/java/org/caosdb/server/transaction/Retrieve.java
+++ b/src/main/java/org/caosdb/server/transaction/Retrieve.java
@@ -114,7 +114,7 @@ public class Retrieve extends Transaction<RetrieveContainer> {
 
   private void retrieveFullEntities(final RetrieveContainer container, final Access access)
       throws Exception {
-    execute(new RetrieveFullEntityTransaction(container), access);
+    execute(new RetrieveFullEntityTransaction(container, getTransactor()), access);
   }
 
   @Override
diff --git a/src/main/java/org/caosdb/server/transaction/UpdateACL.java b/src/main/java/org/caosdb/server/transaction/UpdateACL.java
index 289b5d53a14c642b4ac85bd9c02dd5c69484fa91..4bb27c2399d97bca0035ea27b5f7e4797428b18e 100644
--- a/src/main/java/org/caosdb/server/transaction/UpdateACL.java
+++ b/src/main/java/org/caosdb/server/transaction/UpdateACL.java
@@ -67,7 +67,8 @@ public class UpdateACL extends Transaction<TransactionContainer>
       oldContainer.add(new UpdateEntity(e.getId(), null));
     }
 
-    RetrieveFullEntityTransaction t = new RetrieveFullEntityTransaction(oldContainer);
+    RetrieveFullEntityTransaction t =
+        new RetrieveFullEntityTransaction(oldContainer, getTransactor());
     execute(t, getAccess());
 
     // the entities in this container only have an id and an ACL. -> Replace
diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
index 939d0bf6473611cb1a0bb1ee28bfd91a568babd3..08f80915f362274cbe2a20ee844afb7075864252 100644
--- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
@@ -165,10 +165,10 @@ public class WriteTransaction extends Transaction<WritableContainer>
 
     // Retrieve a container which contains all IDs of those entities
     // which are to be updated.
-    execute(new RetrieveFullEntityTransaction(oldContainer), getAccess());
+    execute(new RetrieveFullEntityTransaction(oldContainer, getTransactor()), getAccess());
 
     // Retrieve all entities which are to be deleted.
-    execute(new RetrieveFullEntityTransaction(deleteContainer), getAccess());
+    execute(new RetrieveFullEntityTransaction(deleteContainer, getTransactor()), getAccess());
 
     // Check if any updates are to be processed.
     for (final EntityInterface entity : getContainer()) {
diff --git a/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java b/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java
index cfd8fcc9ba39cdf68dbf43be77de30f81c4fbec9..bb209f0eb5ea2a7438b7488955cb1a1d22424ca5 100644
--- a/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java
+++ b/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java
@@ -23,8 +23,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.Callable;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.Permission;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.ExecutionException;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.Subject;
+import org.caosdb.server.CaosDBServer;
+import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.datatype.ReferenceValue;
 import org.caosdb.server.entity.Entity;
 import org.caosdb.server.entity.EntityID;
@@ -32,15 +45,168 @@ import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.entity.xml.PropertyToElementStrategyTest;
+import org.caosdb.server.permissions.EntityACL;
 import org.caosdb.server.query.Query.Selection;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 public class RetrieveFullEntityTest {
 
+  @BeforeAll
+  public static void setup() throws IOException {
+    CaosDBServer.initServerProperties();
+  }
+
   @Test
   public void testRetrieveSubEntities() {
     final RetrieveFullEntityTransaction r =
-        new RetrieveFullEntityTransaction(new EntityID("0")) {
+        new RetrieveFullEntityTransaction(
+            new EntityID("0"),
+            new Subject() {
+
+              @Override
+              public Object getPrincipal() {
+                return new Principal("Bla", "Blub");
+              }
+
+              @Override
+              public PrincipalCollection getPrincipals() {
+                return null;
+              }
+
+              @Override
+              public boolean isPermitted(String permission) {
+                assertEquals(
+                    permission, org.caosdb.server.permissions.EntityPermission.RETRIEVE_ENTITY);
+                return true;
+              }
+
+              @Override
+              public boolean isPermitted(Permission permission) {
+                return false;
+              }
+
+              @Override
+              public boolean[] isPermitted(String... permissions) {
+                return null;
+              }
+
+              @Override
+              public boolean[] isPermitted(List<Permission> permissions) {
+                return null;
+              }
+
+              @Override
+              public boolean isPermittedAll(String... permissions) {
+                return false;
+              }
+
+              @Override
+              public boolean isPermittedAll(Collection<Permission> permissions) {
+                return false;
+              }
+
+              @Override
+              public void checkPermission(String permission) throws AuthorizationException {}
+
+              @Override
+              public void checkPermission(Permission permission) throws AuthorizationException {}
+
+              @Override
+              public void checkPermissions(String... permissions) throws AuthorizationException {}
+
+              @Override
+              public void checkPermissions(Collection<Permission> permissions)
+                  throws AuthorizationException {}
+
+              @Override
+              public boolean hasRole(String roleIdentifier) {
+                return false;
+              }
+
+              @Override
+              public boolean[] hasRoles(List<String> roleIdentifiers) {
+                return null;
+              }
+
+              @Override
+              public boolean hasAllRoles(Collection<String> roleIdentifiers) {
+                return false;
+              }
+
+              @Override
+              public void checkRole(String roleIdentifier) throws AuthorizationException {}
+
+              @Override
+              public void checkRoles(Collection<String> roleIdentifiers)
+                  throws AuthorizationException {}
+
+              @Override
+              public void checkRoles(String... roleIdentifiers) throws AuthorizationException {}
+
+              @Override
+              public void login(AuthenticationToken token) throws AuthenticationException {}
+
+              @Override
+              public boolean isAuthenticated() {
+                return false;
+              }
+
+              @Override
+              public boolean isRemembered() {
+                return false;
+              }
+
+              @Override
+              public Session getSession() {
+                return null;
+              }
+
+              @Override
+              public Session getSession(boolean create) {
+                return null;
+              }
+
+              @Override
+              public void logout() {}
+
+              @Override
+              public <V> V execute(Callable<V> callable) throws ExecutionException {
+                return null;
+              }
+
+              @Override
+              public void execute(Runnable runnable) {}
+
+              @Override
+              public <V> Callable<V> associateWith(Callable<V> callable) {
+                return null;
+              }
+
+              @Override
+              public Runnable associateWith(Runnable runnable) {
+                return null;
+              }
+
+              @Override
+              public void runAs(PrincipalCollection principals)
+                  throws NullPointerException, IllegalStateException {}
+
+              @Override
+              public boolean isRunAs() {
+                return false;
+              }
+
+              @Override
+              public PrincipalCollection getPreviousPrincipals() {
+                return null;
+              }
+
+              @Override
+              public PrincipalCollection releaseRunAs() {
+                return null;
+              }
+            }) {
 
           /** Mock-up */
           @Override
@@ -53,6 +219,7 @@ public class RetrieveFullEntityTest {
             assertEquals("description", selections.get(0).getSelector());
 
             e.setDescription("A heart-shaped window.");
+            e.setEntityACL(new EntityACL());
           }
           ;
         };