Added 'version' column to ADM entities

- A patch will assign initial version values to the entities
 - Deprecated TransactionUtil in favour of the RetryingTransactionHelper
 - Renamed RetryingTransactionHelper.Callback to RetryingTransactionHelper.RetryingTransactionCallback
   The name Callback clashes with many other classes in the classpath
 - Moved loads of components to be included in the retry behaviour
Duplicate name checks
 - This is done using a query, but the entity update is not written to the database early
 - Concurrent adds of the same-named child node will only fail at the end of the transaction
 - TODO: Detect the duplicate violation during transaction retrying
Workaround for ADMLuceneTest
 - Disable session size resource management during tests


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5823 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2007-06-01 12:40:17 +00:00
parent bbbd18923f
commit 819c7084a2
45 changed files with 818 additions and 230 deletions

View File

@@ -33,6 +33,7 @@ import org.alfresco.repo.domain.ChildAssoc;
import org.alfresco.repo.domain.Node;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
/**
* @author Derek Hulley
@@ -42,6 +43,7 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
private static final long serialVersionUID = -8993272236626580410L;
private Long id;
private Long version;
private Node parent;
private Node child;
private QName typeQName;
@@ -127,6 +129,32 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
}
}
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
else if (obj == this)
{
return true;
}
else if (!(obj instanceof ChildAssoc))
{
return false;
}
ChildAssoc that = (ChildAssoc) obj;
return (EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName())
&& EqualsHelper.nullSafeEquals(this.getQname(), that.getQname())
&& EqualsHelper.nullSafeEquals(this.getChild(), that.getChild())
&& EqualsHelper.nullSafeEquals(this.getParent(), that.getParent()));
}
public int hashCode()
{
return (child == null ? 0 : child.hashCode());
}
public String toString()
{
StringBuffer sb = new StringBuffer(32);
@@ -192,6 +220,20 @@ public class ChildAssocImpl implements ChildAssoc, Serializable
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Node getParent()
{
return parent;

View File

@@ -42,18 +42,14 @@ public class DbAccessControlEntryImpl extends LifecycleAdapter
{
private static final long serialVersionUID = -418837862334064582L;
/** The object id */
private long id;
private Long id;
private Long version;
/** The container of these entries */
private DbAccessControlList accessControlList;
/** The permission to which this applies (non null - all is a special string) */
private DbPermission permission;
/** The recipient to which this applies (non null - all is a special string) */
private DbAuthority authority;
/** Is this permission allowed? */
private boolean allowed;
@@ -113,7 +109,7 @@ public class DbAccessControlEntryImpl extends LifecycleAdapter
return hashCode;
}
public long getId()
public Long getId()
{
return id;
}
@@ -121,11 +117,26 @@ public class DbAccessControlEntryImpl extends LifecycleAdapter
/**
* For Hibernate use
*/
/* package */ void setId(long id)
@SuppressWarnings("unused")
private void setId(Long id)
{
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public DbAccessControlList getAccessControlList()
{
return accessControlList;

View File

@@ -51,7 +51,8 @@ public class DbAccessControlListImpl extends LifecycleAdapter
private static Log logger = LogFactory.getLog(DbAccessControlListImpl.class);
private long id;
private Long id;
private Long version;
private Set<DbAccessControlEntry> entries;
private boolean inherits;
@@ -94,7 +95,7 @@ public class DbAccessControlListImpl extends LifecycleAdapter
return (inherits == false ? 0 : 17);
}
public long getId()
public Long getId()
{
return id;
}
@@ -103,11 +104,25 @@ public class DbAccessControlListImpl extends LifecycleAdapter
* Hibernate use
*/
@SuppressWarnings("unused")
private void setId(long id)
private void setId(Long id)
{
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Set<DbAccessControlEntry> getEntries()
{
return entries;

View File

@@ -47,6 +47,7 @@ public class DbAuthorityImpl extends LifecycleAdapter
private static Log logger = LogFactory.getLog(DbAuthorityImpl.class);
private Long version;
private String recipient;
private Set<String> externalKeys;
@@ -105,6 +106,20 @@ public class DbAuthorityImpl extends LifecycleAdapter
return super.onDelete(session);
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public String getRecipient()
{
return recipient;

View File

@@ -48,7 +48,8 @@ public class DbPermissionImpl extends LifecycleAdapter
private static Log logger = LogFactory.getLog(DbPermissionImpl.class);
private long id;
private Long id;
private Long version;
private QName typeQname;
private String name;
@@ -120,7 +121,7 @@ public class DbPermissionImpl extends LifecycleAdapter
return super.onDelete(session);
}
public long getId()
public Long getId()
{
return id;
}
@@ -129,11 +130,25 @@ public class DbPermissionImpl extends LifecycleAdapter
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setId(long id)
private void setId(Long id)
{
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public QName getTypeQname()
{
return typeQname;

View File

@@ -49,6 +49,7 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.BaseSpringTest;
@@ -178,6 +179,8 @@ public class HibernateNodeTest extends BaseSpringTest
// Sybase
// expected
}
// Just clear out any pending changes
getSession().clear();
}
/**
@@ -584,4 +587,58 @@ public class HibernateNodeTest extends BaseSpringTest
getSession().flush();
getSession().clear();
}
public void testDeletesAndFlush() throws Exception
{
// Create parent node
Node parentNode = new NodeImpl();
parentNode.setStore(store);
parentNode.setUuid(GUID.generate());
parentNode.setTypeQName(ContentModel.TYPE_CONTAINER);
Long nodeIdOne = (Long) getSession().save(parentNode);
// Create child node
Node childNode = new NodeImpl();
childNode.setStore(store);
childNode.setUuid(GUID.generate());
childNode.setTypeQName(ContentModel.TYPE_CONTENT);
Long nodeIdTwo = (Long) getSession().save(childNode);
// Get them into the database
getSession().flush();
// Now create a loads of associations
int assocCount = 1000;
List<Long> assocIds = new ArrayList<Long>(assocCount);
for (int i = 0; i < assocCount; i++)
{
ChildAssoc assoc = new ChildAssocImpl();
assoc.buildAssociation(parentNode, childNode);
assoc.setIsPrimary(false);
assoc.setTypeQName(QName.createQName(null, "TYPE"));
assoc.setQname(QName.createQName(null, "" + System.nanoTime()));
assoc.setChildNodeName(GUID.generate()); // It must be unique
assoc.setChildNodeNameCrc(-1L);
Long assocId = (Long) getSession().save(assoc);
assocIds.add(assocId);
}
// Flush and clear the lot
getSession().flush();
getSession().clear();
// Now we delete the entities, flushing and clearing every 100 deletes
int count = 0;
for (Long assocId : assocIds)
{
// Load the entity
ChildAssoc assoc = (ChildAssoc) getSession().get(ChildAssocImpl.class, assocId);
assertNotNull("Entity should exist", assoc);
getSession().delete(assoc);
// Do we flush and clear
if (count % 100 == 0)
{
getSession().flush();
getSession().clear();
}
count++;
}
}
}

View File

@@ -38,6 +38,9 @@
<!-- the store-unique identifier -->
<property name="uuid" column="uuid" type="string" length="36" />
</natural-id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
<property name="typeQName" column="type_qname" type="QName" length="255" not-null="true" />
<!-- forward assoc to access control list (optional) -->
<many-to-one
@@ -115,6 +118,8 @@
<key-property name="identifier" length="100" />
<key-property name="guid" length="36" />
</composite-id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
<!-- forward assoc to transaction -->
<many-to-one
name="transaction"
@@ -148,13 +153,15 @@
<id name="id" column="id" type="long" >
<generator class="native" />
</id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
<!-- forward assoc to parent node -->
<many-to-one
name="parent"
class="org.alfresco.repo.domain.hibernate.NodeImpl"
lazy="proxy"
fetch="select"
optimistic-lock="true"
optimistic-lock="false"
not-null="true"
unique-key="UIDX_CHILD_NAME" >
<column name="parent_node_id" not-null="true" />
@@ -165,7 +172,7 @@
lazy="proxy"
fetch="select"
class="org.alfresco.repo.domain.hibernate.NodeImpl"
optimistic-lock="true"
optimistic-lock="false"
not-null="true" >
<column name="child_node_id" not-null="true"/>
</many-to-one>
@@ -190,6 +197,7 @@
<many-to-one
name="source"
class="org.alfresco.repo.domain.hibernate.NodeImpl"
optimistic-lock="false"
lazy="false"
fetch="join"
not-null="true" >
@@ -199,6 +207,7 @@
<many-to-one
name="target"
class="org.alfresco.repo.domain.hibernate.NodeImpl"
optimistic-lock="false"
lazy="false"
fetch="join"
not-null="true" >
@@ -206,6 +215,8 @@
</many-to-one>
<property name="typeQName" column="type_qname" type="QName" length="255" not-null="true" />
</natural-id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
</class>
<query name="store.GetAllStores">
@@ -272,6 +283,17 @@
assoc.id
</query>
<query name="node.GetChildAssocByShortName">
select
assoc
from
org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc
where
assoc.parent.id = :parentId and
assoc.childNodeName = :childNodeName and
assoc.childNodeNameCrc = :childNodeNameCrc
</query>
<query name="node.GetChildAssocByTypeAndName">
select
assoc

View File

@@ -44,7 +44,8 @@ public class NodeAssocImpl implements NodeAssoc, Serializable
{
private static final long serialVersionUID = 864534636913524867L;
private long id;
private Long id;
private Long version;
private Node source;
private Node target;
private QName typeQName;
@@ -149,7 +150,7 @@ public class NodeAssocImpl implements NodeAssoc, Serializable
return (typeQName == null ? 0 : typeQName.hashCode());
}
public long getId()
public Long getId()
{
return id;
}
@@ -163,6 +164,20 @@ public class NodeAssocImpl implements NodeAssoc, Serializable
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Node getSource()
{
return source;

View File

@@ -56,6 +56,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
private static final long serialVersionUID = -2101330674810283053L;
private Long id;
private Long version;
private Store store;
private String uuid;
private QName typeQName;
@@ -179,6 +180,20 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Store getStore()
{
return store;

View File

@@ -42,6 +42,7 @@ public class NodeStatusImpl implements NodeStatus, Serializable
private static final long serialVersionUID = -802747893314715639L;
private NodeKey key;
private Long version;
private Node node;
private Transaction transaction;
@@ -85,6 +86,20 @@ public class NodeStatusImpl implements NodeStatus, Serializable
this.key = key;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Node getNode()
{
return node;

View File

@@ -19,6 +19,8 @@
<generator class="native" />
</id>
<version column="version" name="version" type="long" />
<set name="entries"
inverse="true"
lazy="false"
@@ -74,6 +76,8 @@
not-null="true" />
</natural-id>
<version column="version" name="version" type="long" />
<property name="allowed" column="allowed" type="boolean" not-null="true" />
</class>
@@ -97,6 +101,8 @@
<property name="name" type="string" length="100" column="name" />
</natural-id>
<version column="version" name="version" type="long" />
</class>
<class
@@ -111,6 +117,8 @@
<id name="recipient" column="recipient" type="string" length="100" />
<version column="version" name="version" type="long" />
<set
name="externalKeys"
table="alf_auth_ext_keys"

View File

@@ -41,6 +41,7 @@ public class ServerImpl extends LifecycleAdapter implements Server, Serializable
private static final long serialVersionUID = 8063452519040344479L;
private Long id;
private Long version;
private String ipAddress;
public ServerImpl()
@@ -77,6 +78,20 @@ public class ServerImpl extends LifecycleAdapter implements Server, Serializable
return ipAddress;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public void setIpAddress(String ipAddress)
{
this.ipAddress = ipAddress;

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.domain.hibernate;
import java.lang.reflect.Method;
import java.util.Map;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.util.resource.MethodResourceManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -50,11 +51,50 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
*/
public class SessionSizeResourceManager extends HibernateDaoSupport implements MethodResourceManager
{
/** key to store the local flag to disable resource control during the current transaction */
private static final String KEY_DISABLE_IN_TRANSACTION = "SessionSizeResourceManager.DisableInTransaction";
private static Log logger = LogFactory.getLog(SessionSizeResourceManager.class);
/** Default 1000 */
private int threshold = 1000;
private int threshold;
/**
* Disable resource management for the duration of the current transaction. This is temporary
* and relies on an active transaction.
*/
public static void setDisableInTransaction()
{
AlfrescoTransactionSupport.bindResource(KEY_DISABLE_IN_TRANSACTION, Boolean.TRUE);
}
/**
* @return Returns true if the resource management must be ignored in the current transaction.
* If <code>false</code>, the global setting will take effect.
*
* @see #setDisableInTransaction()
*/
public static boolean isDisableInTransaction()
{
Boolean disableInTransaction = (Boolean) AlfrescoTransactionSupport.getResource(KEY_DISABLE_IN_TRANSACTION);
if (disableInTransaction == null || disableInTransaction == Boolean.FALSE)
{
return false;
}
else
{
return true;
}
}
/**
* Default public constructor required for bean instantiation.
*/
public SessionSizeResourceManager()
{
this.threshold = 1000;
}
/**
* Set the {@link Session#clear()} threshold. If the number of entities and collections in the
* current session exceeds this number, then the session will be cleared. Have you read the
@@ -74,6 +114,12 @@ public class SessionSizeResourceManager extends HibernateDaoSupport implements M
long transactionElapsedTimeNs,
Method currentMethod)
{
if (isDisableInTransaction())
{
// Don't do anything
return;
}
// We are go for interfering
Session session = getSession(false);
SessionStatistics stats = session.getStatistics();
int entityCount = stats.getEntityCount();

View File

@@ -19,6 +19,8 @@
<key-property name="protocol" length="50" />
<key-property name="identifier" length="100" />
</composite-id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
<!-- forward assoc to root node -->
<many-to-one
name="rootNode"

View File

@@ -44,6 +44,7 @@ public class StoreImpl implements Store, Serializable
private static final long serialVersionUID = -6135740209100885890L;
private StoreKey key;
private Long version;
private Node rootNode;
private transient ReadLock refReadLock;
@@ -148,6 +149,20 @@ public class StoreImpl implements Store, Serializable
}
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public Node getRootNode()
{
return rootNode;

View File

@@ -19,6 +19,8 @@
<id name="id" column="id" type="long" >
<generator class="native" />
</id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
<!-- forward assoc to server IP -->
<many-to-one
name="server"
@@ -48,6 +50,8 @@
<natural-id>
<property name="ipAddress" column="ip_address" type="string" length="15" not-null="true" />
</natural-id>
<!-- Optimistic locking -->
<version column="version" name="version" type="long" />
</class>
<query name="server.getServerByIpAddress">

View File

@@ -42,6 +42,7 @@ public class TransactionImpl extends LifecycleAdapter implements Transaction, Se
private static final long serialVersionUID = -8264339795578077552L;
private Long id;
private Long version;
private String changeTxnId;
private Server server;
@@ -74,6 +75,20 @@ public class TransactionImpl extends LifecycleAdapter implements Transaction, Se
this.id = id;
}
public Long getVersion()
{
return version;
}
/**
* For Hibernate use
*/
@SuppressWarnings("unused")
private void setVersion(Long version)
{
this.version = version;
}
public String getChangeTxnId()
{
return changeTxnId;