mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Fix rebuilding of inverse Sets in Hibernate
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3677 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -148,24 +148,12 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ChildAssoc that = (ChildAssoc) obj;
|
ChildAssoc that = (ChildAssoc) obj;
|
||||||
if (EqualsHelper.nullSafeEquals(id, that.getId()))
|
return EqualsHelper.nullSafeEquals(id, that.getId());
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
EqualsHelper.nullSafeEquals(this.getChild().getId(), that.getChild().getId())
|
|
||||||
&& EqualsHelper.nullSafeEquals(this.getQname(), that.getQname())
|
|
||||||
&& EqualsHelper.nullSafeEquals(this.getParent().getId(), that.getParent().getId())
|
|
||||||
&& EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
{
|
{
|
||||||
return (qName == null ? 0 : qName.hashCode());
|
return (id == null ? 0 : id.hashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -251,8 +251,8 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
assoc1.setQname(QName.createQName(null, "number1"));
|
assoc1.setQname(QName.createQName(null, "number1"));
|
||||||
assoc1.setChildNodeName("number1");
|
assoc1.setChildNodeName("number1");
|
||||||
assoc1.setChildNodeNameCrc(1);
|
assoc1.setChildNodeNameCrc(1);
|
||||||
assoc1.buildAssociation(containerNode, contentNode);
|
|
||||||
getSession().save(assoc1);
|
getSession().save(assoc1);
|
||||||
|
assoc1.buildAssociation(containerNode, contentNode);
|
||||||
|
|
||||||
// make another association between the same two parent and child nodes
|
// make another association between the same two parent and child nodes
|
||||||
ChildAssoc assoc2 = new ChildAssocImpl();
|
ChildAssoc assoc2 = new ChildAssocImpl();
|
||||||
@@ -261,8 +261,8 @@ public class HibernateNodeTest extends BaseSpringTest
|
|||||||
assoc2.setQname(QName.createQName(null, "number2"));
|
assoc2.setQname(QName.createQName(null, "number2"));
|
||||||
assoc2.setChildNodeName("number2");
|
assoc2.setChildNodeName("number2");
|
||||||
assoc2.setChildNodeNameCrc(2);
|
assoc2.setChildNodeNameCrc(2);
|
||||||
assoc2.buildAssociation(containerNode, contentNode);
|
|
||||||
getSession().save(assoc2);
|
getSession().save(assoc2);
|
||||||
|
assoc2.buildAssociation(containerNode, contentNode);
|
||||||
|
|
||||||
assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
|
assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
|
||||||
assertNotSame("Assoc equals failure", assoc1, assoc2);
|
assertNotSame("Assoc equals failure", assoc1, assoc2);
|
||||||
|
@@ -80,6 +80,7 @@ import org.springframework.util.Assert;
|
|||||||
public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
||||||
{
|
{
|
||||||
private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
|
private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
|
||||||
|
private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths");
|
||||||
|
|
||||||
private DictionaryService dictionaryService;
|
private DictionaryService dictionaryService;
|
||||||
private NodeDaoService nodeDaoService;
|
private NodeDaoService nodeDaoService;
|
||||||
@@ -1361,6 +1362,24 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// done
|
// done
|
||||||
|
if (loggerPaths.isDebugEnabled())
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(256);
|
||||||
|
if (primaryOnly)
|
||||||
|
{
|
||||||
|
sb.append("Primary paths");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append("Paths");
|
||||||
|
}
|
||||||
|
sb.append(" for node ").append(nodeRef);
|
||||||
|
for (Path path : paths)
|
||||||
|
{
|
||||||
|
sb.append("\n").append(" ").append(path);
|
||||||
|
}
|
||||||
|
loggerPaths.debug(sb);
|
||||||
|
}
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,7 +20,9 @@ import java.io.Serializable;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||||
@@ -55,6 +57,8 @@ import org.alfresco.service.cmr.repository.NodeRef;
|
|||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.hibernate.ObjectDeletedException;
|
import org.hibernate.ObjectDeletedException;
|
||||||
import org.hibernate.Query;
|
import org.hibernate.Query;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
@@ -82,6 +86,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
|
private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings";
|
||||||
private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress";
|
private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress";
|
||||||
|
|
||||||
|
private static Log logger = LogFactory.getLog(HibernateNodeDaoServiceImpl.class);
|
||||||
|
|
||||||
/** a uuid identifying this unique instance */
|
/** a uuid identifying this unique instance */
|
||||||
private final String uuid;
|
private final String uuid;
|
||||||
|
|
||||||
@@ -415,22 +421,47 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
* Manually ensures that all cascading of associations is taken care of
|
* Manually ensures that all cascading of associations is taken care of
|
||||||
*/
|
*/
|
||||||
public void deleteNode(Node node, boolean cascade)
|
public void deleteNode(Node node, boolean cascade)
|
||||||
|
{
|
||||||
|
Set<Long> deletedChildAssocIds = new HashSet<Long>(10);
|
||||||
|
deleteNodeInternal(node, cascade, deletedChildAssocIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* @param cascade true to cascade delete
|
||||||
|
* @param deletedChildAssocIds previously deleted child associations
|
||||||
|
*/
|
||||||
|
private void deleteNodeInternal(Node node, boolean cascade, Set<Long> deletedChildAssocIds)
|
||||||
{
|
{
|
||||||
// delete all parent assocs
|
// delete all parent assocs
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Deleting parent assocs of node " + node.getId());
|
||||||
|
}
|
||||||
|
|
||||||
Collection<ChildAssoc> parentAssocs = node.getParentAssocs();
|
Collection<ChildAssoc> parentAssocs = node.getParentAssocs();
|
||||||
parentAssocs = new ArrayList<ChildAssoc>(parentAssocs);
|
parentAssocs = new ArrayList<ChildAssoc>(parentAssocs);
|
||||||
for (ChildAssoc assoc : parentAssocs)
|
for (ChildAssoc assoc : parentAssocs)
|
||||||
{
|
{
|
||||||
deleteChildAssoc(assoc, false); // we don't cascade upwards
|
deleteChildAssocInternal(assoc, false, deletedChildAssocIds); // we don't cascade upwards
|
||||||
}
|
}
|
||||||
// delete all child assocs
|
// delete all child assocs
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Deleting child assocs of node " + node.getId());
|
||||||
|
}
|
||||||
Collection<ChildAssoc> childAssocs = getChildAssocs(node);
|
Collection<ChildAssoc> childAssocs = getChildAssocs(node);
|
||||||
childAssocs = new ArrayList<ChildAssoc>(childAssocs);
|
childAssocs = new ArrayList<ChildAssoc>(childAssocs);
|
||||||
for (ChildAssoc assoc : childAssocs)
|
for (ChildAssoc assoc : childAssocs)
|
||||||
{
|
{
|
||||||
deleteChildAssoc(assoc, cascade); // potentially cascade downwards
|
deleteChildAssocInternal(assoc, cascade, deletedChildAssocIds); // potentially cascade downwards
|
||||||
}
|
}
|
||||||
// delete all node associations to and from
|
// delete all node associations to and from
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Deleting source and target assocs of node " + node.getId());
|
||||||
|
}
|
||||||
List<NodeAssoc> nodeAssocs = getNodeAssocsToAndFrom(node);
|
List<NodeAssoc> nodeAssocs = getNodeAssocsToAndFrom(node);
|
||||||
for (NodeAssoc assoc : nodeAssocs)
|
for (NodeAssoc assoc : nodeAssocs)
|
||||||
{
|
{
|
||||||
@@ -501,9 +532,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
assoc.setChildNodeNameCrc(-1L); // random names compete only with each other
|
assoc.setChildNodeNameCrc(-1L); // random names compete only with each other
|
||||||
assoc.setQname(qname);
|
assoc.setQname(qname);
|
||||||
assoc.setIsPrimary(isPrimary);
|
assoc.setIsPrimary(isPrimary);
|
||||||
assoc.buildAssociation(parentNode, childNode);
|
|
||||||
// persist it, catching the duplicate child name
|
// persist it, catching the duplicate child name
|
||||||
getHibernateTemplate().save(assoc);
|
getHibernateTemplate().save(assoc);
|
||||||
|
// maintain inverse sets
|
||||||
|
assoc.buildAssociation(parentNode, childNode);
|
||||||
// done
|
// done
|
||||||
return assoc;
|
return assoc;
|
||||||
}
|
}
|
||||||
@@ -565,7 +597,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
{
|
{
|
||||||
Integer count = (Integer) getHibernateTemplate().execute(callback);
|
Integer count = (Integer) getHibernateTemplate().execute(callback);
|
||||||
// refresh the entity directly
|
// refresh the entity directly
|
||||||
getHibernateTemplate().refresh(childAssoc);
|
|
||||||
if (count.intValue() == 0)
|
if (count.intValue() == 0)
|
||||||
{
|
{
|
||||||
if (logger.isDebugEnabled())
|
if (logger.isDebugEnabled())
|
||||||
@@ -573,6 +604,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
logger.debug("ChildAssoc not updated: " + childAssoc.getId());
|
logger.debug("ChildAssoc not updated: " + childAssoc.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getHibernateTemplate().refresh(childAssoc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (DataIntegrityViolationException e)
|
catch (DataIntegrityViolationException e)
|
||||||
{
|
{
|
||||||
@@ -690,21 +725,53 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually enforces cascade deletions down primary associations
|
* Public level entry-point.
|
||||||
*/
|
*/
|
||||||
public void deleteChildAssoc(ChildAssoc assoc, boolean cascade)
|
public void deleteChildAssoc(ChildAssoc assoc, boolean cascade)
|
||||||
{
|
{
|
||||||
|
Set<Long> deletedChildAssocIds = new HashSet<Long>(10);
|
||||||
|
deleteChildAssocInternal(assoc, cascade, deletedChildAssocIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cascade deletion of child associations, recording the IDs of deleted assocs.
|
||||||
|
*
|
||||||
|
* @param assoc the association to delete
|
||||||
|
* @param cascade true to cascade to the child node of the association
|
||||||
|
* @param deletedChildAssocIds already-deleted associations
|
||||||
|
*/
|
||||||
|
private void deleteChildAssocInternal(ChildAssoc assoc, boolean cascade, Set<Long> deletedChildAssocIds)
|
||||||
|
{
|
||||||
|
Long childAssocId = assoc.getId();
|
||||||
|
if (deletedChildAssocIds.contains(childAssocId))
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Ignoring parent-child association " + assoc.getId());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug(
|
||||||
|
"Deleting parent-child association " + assoc.getId() +
|
||||||
|
(cascade ? " with" : " without") + " cascade:" +
|
||||||
|
assoc.getParent().getId() + " -> " + assoc.getChild().getId());
|
||||||
|
}
|
||||||
|
|
||||||
Node childNode = assoc.getChild();
|
Node childNode = assoc.getChild();
|
||||||
|
|
||||||
// maintain inverse association sets
|
// maintain inverse association sets
|
||||||
assoc.removeAssociation();
|
assoc.removeAssociation();
|
||||||
// remove instance
|
// remove instance
|
||||||
getHibernateTemplate().delete(assoc);
|
getHibernateTemplate().delete(assoc);
|
||||||
|
deletedChildAssocIds.add(childAssocId); // ensure that we don't attempt to delete it twice
|
||||||
|
|
||||||
if (cascade && assoc.getIsPrimary()) // the assoc is primary
|
if (cascade && assoc.getIsPrimary()) // the assoc is primary
|
||||||
{
|
{
|
||||||
// delete the child node
|
// delete the child node
|
||||||
deleteNode(childNode, cascade);
|
deleteNodeInternal(childNode, cascade, deletedChildAssocIds);
|
||||||
/*
|
/*
|
||||||
* The child node deletion will cascade delete all assocs to
|
* The child node deletion will cascade delete all assocs to
|
||||||
* and from it, but we have safely removed this one, so no
|
* and from it, but we have safely removed this one, so no
|
||||||
|
@@ -41,7 +41,6 @@ import org.alfresco.repo.dictionary.M2Model;
|
|||||||
import org.alfresco.repo.node.BaseNodeServiceTest;
|
import org.alfresco.repo.node.BaseNodeServiceTest;
|
||||||
import org.alfresco.repo.search.QueryParameterDefImpl;
|
import org.alfresco.repo.search.QueryParameterDefImpl;
|
||||||
import org.alfresco.repo.search.QueryRegisterComponent;
|
import org.alfresco.repo.search.QueryRegisterComponent;
|
||||||
import org.alfresco.repo.search.impl.lucene.analysis.NumericEncoder;
|
|
||||||
import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer;
|
import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer;
|
||||||
import org.alfresco.repo.search.results.ChildAssocRefResultSet;
|
import org.alfresco.repo.search.results.ChildAssocRefResultSet;
|
||||||
import org.alfresco.repo.search.results.DetachedResultSet;
|
import org.alfresco.repo.search.results.DetachedResultSet;
|
||||||
|
Reference in New Issue
Block a user