diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
index 7aaf7e6327..f636838e14 100644
--- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml
@@ -917,6 +917,7 @@
where
parentNode.id = #{parentNode.id}
+ = #{id}]]>
and assoc.child_node_id = #{childNode.id}
and assoc.type_qname_id in
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index a7416490da..e5849c9efb 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -55,9 +55,9 @@ import org.alfresco.repo.domain.usage.UsageDAO;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.security.permissions.AccessControlListProperties;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.TransactionAwareSingleton;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
-import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
@@ -71,20 +71,20 @@ import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.InvalidStoreRefException;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.ReadOnlyServerException;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.EqualsHelper.MapValueComparison;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.ReadWriteLockExecuter;
import org.alfresco.util.SerializationUtils;
-import org.alfresco.util.EqualsHelper.MapValueComparison;
import org.apache.commons.lang.mutable.MutableInt;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -2900,7 +2900,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
*/
private class ChildAssocRefBatchingQueryCallback implements ChildAssocRefQueryCallback
{
- private static final int BATCH_SIZE = 256 * 4;
private final ChildAssocRefQueryCallback callback;
private final boolean preload;
private final List nodeRefs;
@@ -2935,18 +2934,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Pair parentNodePair,
Pair childNodePair)
{
- if (!preload)
+ if (preload)
{
- return callback.handle(childAssocPair, parentNodePair, childNodePair);
+ nodeRefs.add(childNodePair.getSecond());
}
- // Batch it
- if (nodeRefs.size() >= BATCH_SIZE)
- {
- cacheNodes(nodeRefs);
- nodeRefs.clear();
- }
- nodeRefs.add(childNodePair.getSecond());
-
return callback.handle(childAssocPair, parentNodePair, childNodePair);
}
public void done()
@@ -2957,7 +2948,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
cacheNodes(nodeRefs);
nodeRefs.clear();
}
-
+ // Done
callback.done();
}
}
@@ -2977,6 +2968,23 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
new ChildAssocRefBatchingQueryCallback(resultsCallback));
}
+ @Override
+ public void getChildAssocs(
+ Long parentNodeId,
+ QName assocTypeQName,
+ QName assocQName,
+ int maxResults,
+ ChildAssocRefQueryCallback resultsCallback)
+ {
+ selectChildAssocs(
+ parentNodeId,
+ assocTypeQName,
+ assocQName,
+ null,
+ maxResults,
+ new ChildAssocRefBatchingQueryCallback(resultsCallback));
+ }
+
public void getChildAssocs(Long parentNodeId, Set assocTypeQNames, ChildAssocRefQueryCallback resultsCallback)
{
switch (assocTypeQNames.size())
@@ -3926,6 +3934,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Boolean isPrimary,
Boolean sameStore,
ChildAssocRefQueryCallback resultsCallback);
+ protected abstract void selectChildAssocs(
+ Long parentNodeId,
+ QName assocTypeQName,
+ QName assocQName,
+ Long minAssocIdInclusive,
+ int maxResults,
+ ChildAssocRefQueryCallback resultsCallback);
protected abstract void selectChildAssocs(
Long parentNodeId,
Set assocTypeQNames,
diff --git a/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java b/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java
index b13dfedf1e..3d56a600cc 100644
--- a/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java
+++ b/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java
@@ -185,7 +185,7 @@ public class ChildAssocEntity
*/
public ChildAssocEntity()
{
- ordered = true;
+ ordered = false;
}
@Override
diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
index 1096fe3dfc..fe9194efa0 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
@@ -503,15 +503,15 @@ public interface NodeDAO extends NodeBulkLoader
* @param assocQName the association qname to filter on; null for no filtering
* @param maxResults the maximum number of results to return. The query will be terminated efficiently
* after that number of results
- * @param preload should the child nodes be batch loaded?
+ * @param resultsCallback the callback that will be called with the results
* @return a list of child associations
*/
- public List getChildAssocs(
+ public void getChildAssocs(
Long parentNodeId,
QName assocTypeQName,
QName assocQName,
final int maxResults,
- boolean preload);
+ ChildAssocRefQueryCallback resultsCallback);
/**
* Get the child associations of a given parent node, optionally filtering on type QName.
diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
index da202758a6..d681912e4b 100644
--- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -1014,6 +1013,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
{
if (!assoc.setTypeQNameAll(qnameDAO, assocTypeQName, false))
{
+ resultsCallback.done();
return; // Shortcut
}
}
@@ -1022,6 +1022,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
{
if (!assoc.setQNameAll(qnameDAO, assocQName, false))
{
+ resultsCallback.done();
return; // Shortcut
}
}
@@ -1045,25 +1046,29 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
resultsCallback.done();
}
- public List getChildAssocs(
+ @Override
+ public void selectChildAssocs(
Long parentNodeId,
QName assocTypeQName,
QName assocQName,
+ Long minAssocIdInclusive,
final int maxResults,
- boolean preload)
+ ChildAssocRefQueryCallback resultsCallback)
{
ChildAssocEntity assoc = new ChildAssocEntity();
// Parent
NodeEntity parentNode = new NodeEntity();
parentNode.setId(parentNodeId);
assoc.setParentNode(parentNode);
+ assoc.setId(minAssocIdInclusive);
// Type QName
if (assocTypeQName != null)
{
if (!assoc.setTypeQNameAll(qnameDAO, assocTypeQName, false))
{
- return Collections.emptyList(); // Shortcut
+ resultsCallback.done();
+ return; // Shortcut
}
}
// QName
@@ -1071,49 +1076,23 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
{
if (!assoc.setQNameAll(qnameDAO, assocQName, false))
{
- return Collections.emptyList(); // Shortcut
+ resultsCallback.done();
+ return; // Shortcut
}
}
- final List result = new LinkedList();
- final List toLoad = new LinkedList();
-
- // We can't invoke the row handler whilst the limited query is running as it's illegal on some databases (MySQL)
- List> entities = template.selectList(SELECT_CHILD_ASSOCS_OF_PARENT_LIMITED, assoc, new RowBounds(0,
- maxResults));
- ChildAssocResultHandler rowHandler = new ChildAssocResultHandler(new ChildAssocRefQueryCallback(){
-
- @Override
- public boolean handle(Pair childAssocPair, Pair parentNodePair,
- Pair childNodePair)
- {
- result.add(childAssocPair.getSecond());
- toLoad.add(childNodePair.getSecond());
- return true;
- }
-
- @Override
- public void done()
- {
- }
-
- @Override
- public boolean preLoadNodes()
- {
- return false;
- }});
+ ChildAssocResultHandler resultHandler = new ChildAssocResultHandler(resultsCallback);
+
+ RowBounds rowBounds = new RowBounds(0, maxResults);
+ List> entities = template.selectList(SELECT_CHILD_ASSOCS_OF_PARENT_LIMITED, assoc, rowBounds);
final DefaultResultContext resultContext = new DefaultResultContext();
for (Object entity : entities)
{
resultContext.nextResultObject(entity);
- rowHandler.handleResult(resultContext);
- }
- if (preload && !toLoad.isEmpty())
- {
- cacheNodes(toLoad);
+ resultHandler.handleResult(resultContext);
}
- return result;
+ resultsCallback.done();
}
@Override
diff --git a/source/java/org/alfresco/repo/node/NodeServiceTest.java b/source/java/org/alfresco/repo/node/NodeServiceTest.java
index 441373a55b..bc2039112b 100644
--- a/source/java/org/alfresco/repo/node/NodeServiceTest.java
+++ b/source/java/org/alfresco/repo/node/NodeServiceTest.java
@@ -241,11 +241,16 @@ public class NodeServiceTest extends TestCase
public void testConcurrentArchive() throws Exception
{
final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
+ final NodeRef[] nodesPrimer = new NodeRef[1];
+ buildNodeHierarchy(workspaceRootNodeRef, nodesPrimer);
final NodeRef[] nodesOne = new NodeRef[10];
buildNodeHierarchy(workspaceRootNodeRef, nodesOne);
final NodeRef[] nodesTwo = new NodeRef[10];
buildNodeHierarchy(workspaceRootNodeRef, nodesTwo);
+ // Prime the root of the archive store (first child adds inherited ACL)
+ nodeService.deleteNode(nodesPrimer[0]);
+
RetryingTransactionCallback outerCallback = new RetryingTransactionCallback()
{
@Override
@@ -426,8 +431,41 @@ public class NodeServiceTest extends TestCase
}
}
+ public void testGetChildren_Limited()
+ {
+ // Create a node and loads of children
+ final NodeRef[] liveNodeRefs = new NodeRef[10];
+ final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
+
+ buildNodeHierarchy(workspaceRootNodeRef, liveNodeRefs);
+
+ // Hook 3rd and subsequent children into 1st child
+ for (int i = 2; i < liveNodeRefs.length; i++)
+ {
+ nodeService.addChild(
+ liveNodeRefs[0],
+ liveNodeRefs[i],
+ ContentModel.ASSOC_CONTAINS,
+ QName.createQName(NAMESPACE, "secondary"));
+ }
+
+ // Do limited queries each time
+ for (int i = 1; i < liveNodeRefs.length; i++)
+ {
+ List childAssocRefs = nodeService.getChildAssocs(liveNodeRefs[0], null, null, i, true);
+ assertEquals("Expected exact number of child assocs", i, childAssocRefs.size());
+ }
+
+ // Repeat, but don't preload
+ for (int i = 1; i < liveNodeRefs.length; i++)
+ {
+ List childAssocRefs = nodeService.getChildAssocs(liveNodeRefs[0], null, null, i, false);
+ assertEquals("Expected exact number of child assocs", i, childAssocRefs.size());
+ }
+ }
+
/**
- * Checks that the node caches react correct when a node is deleted
+ * Checks that the node caches react correctly when a node is deleted
*/
public void testCaches_DeleteNode()
{
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
index 3b36c93427..c69e6408a4 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java
@@ -1707,8 +1707,33 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
+
+ // We have a callback handler to filter results
+ final List results = new ArrayList(10);
+ ChildAssocRefQueryCallback callback = new ChildAssocRefQueryCallback()
+ {
+ public boolean preLoadNodes()
+ {
+ return preload;
+ }
+
+ public boolean handle(
+ Pair childAssocPair,
+ Pair parentNodePair,
+ Pair childNodePair)
+ {
+ results.add(childAssocPair.getSecond());
+ return true;
+ }
+
+ public void done()
+ {
+ }
+ };
// Get the assocs pointing to it
- return nodeDAO.getChildAssocs(nodePair.getFirst(), typeQName, qname, maxResults, preload);
+ nodeDAO.getChildAssocs(nodePair.getFirst(), typeQName, qname, maxResults, callback);
+ // Done
+ return results;
}
public List getChildAssocs(NodeRef nodeRef, Set childNodeTypeQNames)