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 f636838e14..7a61f90485 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
@@ -116,6 +116,10 @@
+
+
+
+
@@ -404,6 +408,17 @@
+
+ update alf_node set
+ version = version + 1,
+ transaction_id = #{idOne}
+ where
+ id in
+
+ #{item}
+
+
+
update alf_node_assoc set
assoc_index = #{assocIndex}
diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml
index c371e55481..69f78d34eb 100644
--- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml
@@ -11,4 +11,20 @@
+
+
\ No newline at end of file
diff --git a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml
index ee4d233790..33ce29c1f4 100644
--- a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml
+++ b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml
@@ -4,13 +4,32 @@
-
+
+
+
+
+
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index 542c55121d..11f3dba8ae 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -85,7 +85,6 @@ import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.ReadWriteLockExecuter;
import org.alfresco.util.SerializationUtils;
-import org.apache.commons.lang.mutable.MutableInt;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.ConcurrencyFailureException;
@@ -448,56 +447,55 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
* where the child associations or nodes are modified en-masse.
*
* @param parentNodeId the parent node of all child nodes to be invalidated (may be null)
+ * @param touchNodes true to also touch the nodes
* @return the number of child associations found (might be capped)
*/
- private int invalidateNodeChildrenCaches(Long parentNodeId)
+ private int invalidateNodeChildrenCaches(Long parentNodeId, boolean primary, boolean touchNodes)
{
- // Select all children
- final MutableInt count = new MutableInt(0);
- ChildAssocRefQueryCallback callback = new ChildAssocRefQueryCallback()
+ Long txnId = getCurrentTransaction().getId();
+
+ int count = 0;
+ List childNodeIds = new ArrayList(256);
+ Long minAssocIdInclusive = Long.MIN_VALUE;
+ while (minAssocIdInclusive != null)
{
- private boolean isClearOn = false;
-
- public boolean preLoadNodes()
+ childNodeIds.clear();
+ List childAssocs = selectChildNodeIds(
+ parentNodeId,
+ Boolean.valueOf(primary),
+ minAssocIdInclusive,
+ 256);
+ // Remove the cache entries as we go
+ for (ChildAssocEntity childAssoc : childAssocs)
{
- return false;
- }
-
- @Override
- public boolean orderResults()
- {
- return false;
- }
-
- public boolean handle(
- Pair childAssocPair,
- Pair parentNodePair,
- Pair childNodePair)
- {
- if (isClearOn)
+ Long childAssocId = childAssoc.getId();
+ if (childAssocId.compareTo(minAssocIdInclusive) < 0)
{
- // We have already decided to drop ALL cache entries
- return false;
+ throw new RuntimeException("Query results did not increase for assoc ID");
}
- else if (count.intValue() >= 1000)
+ else
{
- // That's enough. Instead of walking thousands of entries
- // we just drop the cache at this stage
- AbstractNodeDAOImpl.this.clearCaches();
- isClearOn = true;
- return false; // No more, please
+ minAssocIdInclusive = new Long(childAssocId.longValue() + 1L);
}
- count.increment();
- invalidateNodeCaches(childNodePair.getFirst());
- return true;
+ // Invalidate the node cache
+ Long childNodeId = childAssoc.getChildNode().getId();
+ childNodeIds.add(childNodeId);
+ invalidateNodeCaches(childNodeId);
+ count++;
}
-
- public void done()
+ // Bring all the nodes into the transaction, if required
+ if (touchNodes)
{
- }
- };
- selectChildAssocs(parentNodeId, null, null, null, null, null, callback);
- return count.intValue();
+ updateNodes(txnId, childNodeIds);
+ }
+ // Now break out if we didn't have the full set of results
+ if (childAssocs.size() < 256)
+ {
+ break;
+ }
+ }
+ // Done
+ return count;
}
/**
@@ -508,10 +506,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
private void invalidateNodeCaches(Long nodeId)
{
// Take the current value from the nodesCache and use that to invalidate the other caches
- Pair nodePair = nodesCache.getByKey(nodeId);
- if (nodePair != null)
+ Node node = nodesCache.getValue(nodeId);
+ if (node != null)
{
- NodeVersionKey nodeVersionKey = nodePair.getSecond().getNodeVersionKey();
+ NodeVersionKey nodeVersionKey = node.getNodeVersionKey();
invalidateNodeCaches(nodeVersionKey, true, true, true);
}
// Finally remove the node reference
@@ -1279,7 +1277,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// The new node will have new data not present in the cache, yet
// TODO: Look to move the data in a cache-efficient way
invalidateNodeCaches(newChildNodeId);
- invalidateNodeChildrenCaches(newChildNodeId);
+ invalidateNodeChildrenCaches(newChildNodeId, true, true);
+ invalidateNodeChildrenCaches(newChildNodeId, false, true);
// Now update the original to be 'deleted'
NodeUpdateEntity childNodeUpdate = new NodeUpdateEntity();
childNodeUpdate.setId(childNodeId);
@@ -1339,7 +1338,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
childAssocRetryingHelper.doWithRetry(callback);
// Check for cyclic relationships
- cycleCheck(newChildNodeId);
+ // TODO: This adds a lot of overhead when moving hierarchies.
+ // While getPaths is faster, it would be better to avoid the parentAssocsCache
+ // completely.
+ getPaths(newChildNode.getNodePair(), false);
+// cycleCheck(newChildNodeId);
// Update ACLs for moved tree
Long newParentAclId = newParentNode.getAclId();
@@ -1637,7 +1640,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
primaryParentNodeId,
optionalOldSharedAlcIdInAdditionToNull,
newSharedAclId);
- invalidateNodeChildrenCaches(primaryParentNodeId);
+ invalidateNodeChildrenCaches(primaryParentNodeId, true, false);
}
public void deleteNode(Long nodeId)
@@ -1689,7 +1692,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
deleteNodeAssocsToAndFrom(nodeId);
// Remove child associations (invalidate children)
- invalidateNodeChildrenCaches(nodeId);
+ invalidateNodeChildrenCaches(nodeId, true, true);
+ invalidateNodeChildrenCaches(nodeId, false, true);
deleteChildAssocsToAndFrom(nodeId);
// Remove aspects
@@ -2996,7 +3000,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
parentNodeId,
assocTypeQName,
assocQName,
- null,
maxResults,
new ChildAssocRefBatchingQueryCallback(resultsCallback));
}
@@ -3902,6 +3905,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
protected abstract int updateStore(StoreEntity store);
protected abstract Long insertNode(NodeEntity node);
protected abstract int updateNode(NodeUpdateEntity nodeUpdate);
+ protected abstract int updateNodes(Long txnId, List nodeIds);
protected abstract void updatePrimaryChildrenSharedAclId(
Long txnId,
Long primaryParentNodeId,
@@ -3947,6 +3951,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
protected abstract int updateChildAssocsUniqueName(Long childNodeId, String name);
protected abstract int deleteChildAssocsToAndFrom(Long nodeId);
protected abstract ChildAssocEntity selectChildAssoc(Long assocId);
+ protected abstract List selectChildNodeIds(
+ Long nodeId,
+ Boolean isPrimary,
+ Long minAssocIdInclusive,
+ int maxResults);
protected abstract List selectPrimaryChildAcls(Long nodeId);
protected abstract List selectChildAssoc(
Long parentNodeId,
@@ -3968,7 +3977,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Long parentNodeId,
QName assocTypeQName,
QName assocQName,
- Long minAssocIdInclusive,
int maxResults,
ChildAssocRefQueryCallback resultsCallback);
protected abstract void selectChildAssocs(
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 8fc300a8ef..0ef4de257c 100644
--- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
@@ -85,6 +85,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String SELECT_STORE_ROOT_NODE_BY_REF = "alfresco.node.select_StoreRootNodeByRef";
private static final String INSERT_NODE = "alfresco.node.insert.insert_Node";
private static final String UPDATE_NODE = "alfresco.node.update_Node";
+ private static final String UPDATE_NODE_BULK_TOUCH = "alfresco.node.update_NodeBulkTouch";
private static final String DELETE_NODE_BY_ID = "alfresco.node.delete_NodeById";
private static final String DELETE_NODES_BY_TXN_COMMIT_TIME = "alfresco.node.delete_NodesByTxnCommitTime";
private static final String SELECT_NODE_BY_ID = "alfresco.node.select_NodeById";
@@ -109,6 +110,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String SELECT_NODE_ASSOCS_BY_TARGET = "alfresco.node.select_NodeAssocsByTarget";
private static final String SELECT_NODE_ASSOC_BY_ID = "alfresco.node.select_NodeAssocById";
private static final String SELECT_NODE_ASSOCS_MAX_INDEX = "alfresco.node.select_NodeAssocsMaxId";
+ private static final String SELECT_CHILD_NODE_IDS = "alfresco.node.select.children.select_ChildNodeIds_Limited";
private static final String SELECT_NODE_PRIMARY_CHILD_ACLS = "alfresco.node.select_NodePrimaryChildAcls";
private static final String INSERT_CHILD_ASSOC = "alfresco.node.insert.insert_ChildAssoc";
private static final String DELETE_CHILD_ASSOC_BY_ID = "alfresco.node.delete_ChildAssocById";
@@ -317,6 +319,19 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
return template.update(UPDATE_NODE, nodeUpdate);
}
+ @Override
+ protected int updateNodes(Long txnId, List nodeIds)
+ {
+ if (nodeIds.size() == 0)
+ {
+ return 0;
+ }
+ IdsEntity ids = new IdsEntity();
+ ids.setIdOne(txnId);
+ ids.setIds(nodeIds);
+ return template.update(UPDATE_NODE_BULK_TOUCH, ids);
+ }
+
@Override
protected void updatePrimaryChildrenSharedAclId(
Long txnId,
@@ -880,6 +895,25 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
return (ChildAssocEntity) template.selectOne(SELECT_CHILD_ASSOC_BY_ID, assoc);
}
+ @SuppressWarnings("unchecked")
+ @Override
+ protected List selectChildNodeIds(
+ Long nodeId,
+ Boolean isPrimary,
+ Long minAssocIdInclusive,
+ int maxResults)
+ {
+ ChildAssocEntity assoc = new ChildAssocEntity();
+ NodeEntity parentNode = new NodeEntity();
+ parentNode.setId(nodeId);
+ assoc.setParentNode(parentNode);
+ assoc.setPrimary(isPrimary);
+ assoc.setId(minAssocIdInclusive);
+
+ RowBounds rowBounds = new RowBounds(0, maxResults);
+ return (List) template.selectList(SELECT_CHILD_NODE_IDS, assoc, rowBounds);
+ }
+
@SuppressWarnings("unchecked")
@Override
public List selectPrimaryChildAcls(Long nodeId)
@@ -1053,7 +1087,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
Long parentNodeId,
QName assocTypeQName,
QName assocQName,
- Long minAssocIdInclusive,
final int maxResults,
ChildAssocRefQueryCallback resultsCallback)
{
@@ -1062,7 +1095,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
NodeEntity parentNode = new NodeEntity();
parentNode.setId(parentNodeId);
assoc.setParentNode(parentNode);
- assoc.setId(minAssocIdInclusive);
// Type QName
if (assocTypeQName != null)