mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Rework APIs of new getChildAssocs with result count limit
- Use regular pattern of get -> select in the DAO - All getChildren* batching done after the query - Unit tests - In progress: Added option (low level select only) to constrain by assoc ID git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31318 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -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<NodeRef> nodeRefs;
|
||||
@@ -2935,18 +2934,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
Pair<Long, NodeRef> parentNodePair,
|
||||
Pair<Long, NodeRef> 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<QName> 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<QName> assocTypeQNames,
|
||||
|
@@ -185,7 +185,7 @@ public class ChildAssocEntity
|
||||
*/
|
||||
public ChildAssocEntity()
|
||||
{
|
||||
ordered = true;
|
||||
ordered = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -503,15 +503,15 @@ public interface NodeDAO extends NodeBulkLoader
|
||||
* @param assocQName the association qname to filter on; <tt>null</tt> 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<ChildAssociationRef> 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 <tt>QName</tt>.
|
||||
|
@@ -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<ChildAssociationRef> 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<ChildAssociationRef> result = new LinkedList<ChildAssociationRef>();
|
||||
final List<NodeRef> toLoad = new LinkedList<NodeRef>();
|
||||
|
||||
// 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<Long, ChildAssociationRef> childAssocPair, Pair<Long, NodeRef> parentNodePair,
|
||||
Pair<Long, NodeRef> 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
|
||||
|
@@ -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<Void> outerCallback = new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
@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<ChildAssociationRef> 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<ChildAssociationRef> 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()
|
||||
{
|
||||
|
@@ -1707,8 +1707,33 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||
{
|
||||
// Get the node
|
||||
Pair<Long, NodeRef> nodePair = getNodePairNotNull(nodeRef);
|
||||
|
||||
// We have a callback handler to filter results
|
||||
final List<ChildAssociationRef> results = new ArrayList<ChildAssociationRef>(10);
|
||||
ChildAssocRefQueryCallback callback = new ChildAssocRefQueryCallback()
|
||||
{
|
||||
public boolean preLoadNodes()
|
||||
{
|
||||
return preload;
|
||||
}
|
||||
|
||||
public boolean handle(
|
||||
Pair<Long, ChildAssociationRef> childAssocPair,
|
||||
Pair<Long, NodeRef> parentNodePair,
|
||||
Pair<Long, NodeRef> 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<ChildAssociationRef> getChildAssocs(NodeRef nodeRef, Set<QName> childNodeTypeQNames)
|
||||
|
Reference in New Issue
Block a user