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:
Derek Hulley
2006-09-04 14:04:25 +00:00
parent 6b85d8125e
commit d470cafedc
5 changed files with 96 additions and 23 deletions

View File

@@ -148,24 +148,12 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
return false;
}
ChildAssoc that = (ChildAssoc) obj;
if (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())
);
}
return EqualsHelper.nullSafeEquals(id, that.getId());
}
public int hashCode()
{
return (qName == null ? 0 : qName.hashCode());
return (id == null ? 0 : id.hashCode());
}
/**

View File

@@ -251,8 +251,8 @@ public class HibernateNodeTest extends BaseSpringTest
assoc1.setQname(QName.createQName(null, "number1"));
assoc1.setChildNodeName("number1");
assoc1.setChildNodeNameCrc(1);
assoc1.buildAssociation(containerNode, contentNode);
getSession().save(assoc1);
assoc1.buildAssociation(containerNode, contentNode);
// make another association between the same two parent and child nodes
ChildAssoc assoc2 = new ChildAssocImpl();
@@ -261,8 +261,8 @@ public class HibernateNodeTest extends BaseSpringTest
assoc2.setQname(QName.createQName(null, "number2"));
assoc2.setChildNodeName("number2");
assoc2.setChildNodeNameCrc(2);
assoc2.buildAssociation(containerNode, contentNode);
getSession().save(assoc2);
assoc2.buildAssociation(containerNode, contentNode);
assertFalse("Hashcode incorrent", assoc2.hashCode() == 0);
assertNotSame("Assoc equals failure", assoc1, assoc2);

View File

@@ -80,6 +80,7 @@ import org.springframework.util.Assert;
public class DbNodeServiceImpl extends AbstractNodeServiceImpl
{
private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class);
private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths");
private DictionaryService dictionaryService;
private NodeDaoService nodeDaoService;
@@ -1361,6 +1362,24 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
}
// 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;
}

View File

@@ -20,7 +20,9 @@ import java.io.Serializable;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
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.namespace.QName;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.ObjectDeletedException;
import org.hibernate.Query;
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_SERVER_BY_IPADDRESS = "server.getServerByIpAddress";
private static Log logger = LogFactory.getLog(HibernateNodeDaoServiceImpl.class);
/** a uuid identifying this unique instance */
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
*/
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
if (logger.isDebugEnabled())
{
logger.debug("Deleting parent assocs of node " + node.getId());
}
Collection<ChildAssoc> parentAssocs = node.getParentAssocs();
parentAssocs = new ArrayList<ChildAssoc>(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
if (logger.isDebugEnabled())
{
logger.debug("Deleting child assocs of node " + node.getId());
}
Collection<ChildAssoc> childAssocs = getChildAssocs(node);
childAssocs = new ArrayList<ChildAssoc>(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
if (logger.isDebugEnabled())
{
logger.debug("Deleting source and target assocs of node " + node.getId());
}
List<NodeAssoc> nodeAssocs = getNodeAssocsToAndFrom(node);
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.setQname(qname);
assoc.setIsPrimary(isPrimary);
assoc.buildAssociation(parentNode, childNode);
// persist it, catching the duplicate child name
getHibernateTemplate().save(assoc);
// maintain inverse sets
assoc.buildAssociation(parentNode, childNode);
// done
return assoc;
}
@@ -565,7 +597,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
{
Integer count = (Integer) getHibernateTemplate().execute(callback);
// refresh the entity directly
getHibernateTemplate().refresh(childAssoc);
if (count.intValue() == 0)
{
if (logger.isDebugEnabled())
@@ -573,6 +604,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
logger.debug("ChildAssoc not updated: " + childAssoc.getId());
}
}
else
{
getHibernateTemplate().refresh(childAssoc);
}
}
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)
{
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();
// maintain inverse association sets
assoc.removeAssociation();
// remove instance
getHibernateTemplate().delete(assoc);
deletedChildAssocIds.add(childAssocId); // ensure that we don't attempt to delete it twice
if (cascade && assoc.getIsPrimary()) // the assoc is primary
{
// delete the child node
deleteNode(childNode, cascade);
deleteNodeInternal(childNode, cascade, deletedChildAssocIds);
/*
* The child node deletion will cascade delete all assocs to
* and from it, but we have safely removed this one, so no

View File

@@ -41,7 +41,6 @@ import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.node.BaseNodeServiceTest;
import org.alfresco.repo.search.QueryParameterDefImpl;
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.results.ChildAssocRefResultSet;
import org.alfresco.repo.search.results.DetachedResultSet;