HHH-4545 - Allow o.h.action.Executable to register for either (or both) before or after transaction completion callbacks

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@17913 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2009-11-04 21:19:21 +00:00
parent ecaf3990b8
commit 9c74610a9d
30 changed files with 420 additions and 233 deletions

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
* third-party contributors as indicated by either @author tags or express
* copyright attribution statements applied by the authors. All
* third-party contributions are distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.action;
import org.hibernate.engine.SessionImplementor;
/**
* Contract representing some process that needs to occur during after transaction completion.
*
* @author Steve Ebersole
*/
public interface AfterTransactionCompletionProcess {
/**
* Perform whatever processing is encapsulated here after completion of the transaction.
*
* @param success Did the transaction complete successfully? True means it did.
* @param session The session on which the transaction is completing.
*/
public void doAfterTransactionCompletion(boolean success, SessionImplementor session);
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
* third-party contributors as indicated by either @author tags or express
* copyright attribution statements applied by the authors. All
* third-party contributions are distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.action;
import org.hibernate.engine.SessionImplementor;
/**
* Contract representing some process that needs to occur during before transaction completion.
*
* @author Steve Ebersole
*/
public interface BeforeTransactionCompletionProcess {
/**
* Perform whatever processing is encapsulated here before completion of the transaction.
*
* @param session The session on which the transaction is preparing to complete.
*/
public void doBeforeTransactionCompletion(SessionImplementor session);
}

View File

@ -70,19 +70,13 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
* @param session The session to which this request is tied.
* @param affectedQueryables The affected entity persisters.
*/
public BulkOperationCleanupAction(
SessionImplementor session,
Queryable[] affectedQueryables) {
public BulkOperationCleanupAction(SessionImplementor session, Queryable[] affectedQueryables) {
SessionFactoryImplementor factory = session.getFactory();
ArrayList tmpSpaces = new ArrayList();
for ( int i = 0; i < affectedQueryables.length; i++ ) {
tmpSpaces.addAll( Arrays.asList( affectedQueryables[i].getQuerySpaces() ) );
if ( affectedQueryables[i].hasCache() ) {
entityCleanups.add(
new EntityCleanup(
affectedQueryables[i].getCacheAccessStrategy()
)
);
entityCleanups.add( new EntityCleanup( affectedQueryables[i].getCacheAccessStrategy() ) );
}
Set roles = factory.getCollectionRolesByEntityParticipant( affectedQueryables[i].getEntityName() );
if ( roles != null ) {
@ -91,11 +85,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
String role = ( String ) itr.next();
CollectionPersister collectionPersister = factory.getCollectionPersister( role );
if ( collectionPersister.hasCache() ) {
collectionCleanups.add(
new CollectionCleanup(
collectionPersister.getCacheAccessStrategy()
)
);
collectionCleanups.add( new CollectionCleanup( collectionPersister.getCacheAccessStrategy() ) );
}
}
}
@ -129,11 +119,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
if ( affectedEntity( tableSpaces, entitySpaces ) ) {
tmpSpaces.addAll( Arrays.asList( entitySpaces ) );
if ( persister.hasCache() ) {
entityCleanups.add(
new EntityCleanup(
persister.getCacheAccessStrategy()
)
);
entityCleanups.add( new EntityCleanup( persister.getCacheAccessStrategy() ) );
}
Set roles = session.getFactory().getCollectionRolesByEntityParticipant( persister.getEntityName() );
if ( roles != null ) {
@ -143,9 +129,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
CollectionPersister collectionPersister = factory.getCollectionPersister( role );
if ( collectionPersister.hasCache() ) {
collectionCleanups.add(
new CollectionCleanup(
collectionPersister.getCacheAccessStrategy()
)
new CollectionCleanup( collectionPersister.getCacheAccessStrategy() )
);
}
}
@ -169,9 +153,7 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
* @return True if there are affected table spaces and any of the incoming
* check table spaces occur in that set.
*/
private boolean affectedEntity(
Set affectedTableSpaces,
Serializable[] checkTableSpaces) {
private boolean affectedEntity(Set affectedTableSpaces, Serializable[] checkTableSpaces) {
if ( affectedTableSpaces == null || affectedTableSpaces.isEmpty() ) {
return true;
}
@ -188,22 +170,26 @@ public class BulkOperationCleanupAction implements Executable, Serializable {
return affectedTableSpaces;
}
public boolean hasAfterTransactionCompletion() {
return true;
public BeforeTransactionCompletionProcess getBeforeTransactionCompletionProcess() {
return null;
}
public void afterTransactionCompletion(boolean success) throws HibernateException {
Iterator itr = entityCleanups.iterator();
while ( itr.hasNext() ) {
final EntityCleanup cleanup = ( EntityCleanup ) itr.next();
cleanup.release();
}
public AfterTransactionCompletionProcess getAfterTransactionCompletionProcess() {
return new AfterTransactionCompletionProcess() {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
Iterator itr = entityCleanups.iterator();
while ( itr.hasNext() ) {
final EntityCleanup cleanup = ( EntityCleanup ) itr.next();
cleanup.release();
}
itr = collectionCleanups.iterator();
while ( itr.hasNext() ) {
final CollectionCleanup cleanup = ( CollectionCleanup ) itr.next();
cleanup.release();
}
itr = collectionCleanups.iterator();
while ( itr.hasNext() ) {
final CollectionCleanup cleanup = ( CollectionCleanup ) itr.next();
cleanup.release();
}
}
};
}
public void beforeExecutions() throws HibernateException {

View File

@ -39,15 +39,13 @@ import java.io.Serializable;
/**
* Any action relating to insert/update/delete of a collection
*
* @author Gavin King
*/
public abstract class CollectionAction implements Executable, Serializable, Comparable {
private transient CollectionPersister persister;
private final Serializable key;
private Serializable finalKey;
private final SessionImplementor session;
private SoftLock lock;
private final String collectionRole;
private final PersistentCollection collection;
@ -62,31 +60,45 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
this.collectionRole = persister.getRole();
this.collection = collection;
}
protected PersistentCollection getCollection() {
return collection;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
protected void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
persister = session.getFactory().getCollectionPersister( collectionRole );
}
public void afterTransactionCompletion(boolean success) throws CacheException {
public final void beforeExecutions() throws CacheException {
// we need to obtain the lock before any actions are
// executed, since this may be an inverse="true"
// bidirectional association and it is one of the
// earlier entity actions which actually updates
// the database (this action is resposible for
// second-level cache invalidation only)
if ( persister.hasCache() ) {
final CacheKey ck = new CacheKey(
key,
persister.getKeyType(),
persister.getRole(),
session.getEntityMode(),
session.getFactory()
);
persister.getCacheAccessStrategy().unlockItem( ck, lock );
final CacheKey ck = new CacheKey(
key,
persister.getKeyType(),
persister.getRole(),
session.getEntityMode(),
session.getFactory()
);
final SoftLock lock = persister.getCacheAccessStrategy().lockItem( ck, null );
// the old behavior used key as opposed to getKey()
afterTransactionProcess = new CacheCleanupProcess( key, persister, lock );
}
}
public boolean hasAfterTransactionCompletion() {
return persister.hasCache();
public BeforeTransactionCompletionProcess getBeforeTransactionCompletionProcess() {
return null;
}
private AfterTransactionCompletionProcess afterTransactionProcess;
public AfterTransactionCompletionProcess getAfterTransactionCompletionProcess() {
return afterTransactionProcess;
}
public Serializable[] getPropertySpaces() {
@ -98,7 +110,7 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
}
protected final Serializable getKey() {
finalKey = key;
Serializable finalKey = key;
if ( key instanceof DelayedPostInsertIdentifier ) {
// need to look it up from the persistence-context
finalKey = session.getPersistenceContext().getEntry( collection.getOwner() ).getId();
@ -114,25 +126,6 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
return session;
}
public final void beforeExecutions() throws CacheException {
// we need to obtain the lock before any actions are
// executed, since this may be an inverse="true"
// bidirectional association and it is one of the
// earlier entity actions which actually updates
// the database (this action is resposible for
// second-level cache invalidation only)
if ( persister.hasCache() ) {
final CacheKey ck = new CacheKey(
key,
persister.getKeyType(),
persister.getRole(),
session.getEntityMode(),
session.getFactory()
);
lock = persister.getCacheAccessStrategy().lockItem( ck, null );
}
}
protected final void evict() throws CacheException {
if ( persister.hasCache() ) {
CacheKey ck = new CacheKey(
@ -164,6 +157,29 @@ public abstract class CollectionAction implements Executable, Serializable, Comp
.compare( key, action.key, session.getEntityMode() );
}
}
private static class CacheCleanupProcess implements AfterTransactionCompletionProcess {
private final Serializable key;
private final CollectionPersister persister;
private final SoftLock lock;
private CacheCleanupProcess(Serializable key, CollectionPersister persister, SoftLock lock) {
this.key = key;
this.persister = persister;
this.lock = lock;
}
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
final CacheKey ck = new CacheKey(
key,
persister.getKeyType(),
persister.getRole(),
session.getEntityMode(),
session.getFactory()
);
persister.getCacheAccessStrategy().unlockItem( ck, lock );
}
}
}

View File

@ -40,7 +40,8 @@ import java.io.Serializable;
*
* @author Gavin King
*/
public abstract class EntityAction implements Executable, Serializable, Comparable {
public abstract class EntityAction
implements Executable, Serializable, Comparable, AfterTransactionCompletionProcess {
private final String entityName;
private final Serializable id;
@ -65,8 +66,22 @@ public abstract class EntityAction implements Executable, Serializable, Comparab
this.persister = persister;
}
public BeforeTransactionCompletionProcess getBeforeTransactionCompletionProcess() {
return null;
}
public AfterTransactionCompletionProcess getAfterTransactionCompletionProcess() {
return needsAfterTransactionCompletion()
? this
: null;
}
protected abstract boolean hasPostCommitEventListeners();
public boolean needsAfterTransactionCompletion() {
return persister.hasCache() || hasPostCommitEventListeners();
}
/**
* entity name accessor
*
@ -123,10 +138,6 @@ public abstract class EntityAction implements Executable, Serializable, Comparab
throw new AssertionFailure( "beforeExecutions() called for non-collection action" );
}
public boolean hasAfterTransactionCompletion() {
return persister.hasCache() || hasPostCommitEventListeners();
}
public String toString() {
return StringHelper.unqualify( getClass().getName() ) + MessageHelper.infoString( entityName, id );
}

View File

@ -42,12 +42,12 @@ import org.hibernate.event.EventSource;
import org.hibernate.persister.entity.EntityPersister;
public final class EntityDeleteAction extends EntityAction {
private final Object version;
private SoftLock lock;
private final boolean isCascadeDeleteEnabled;
private final Object[] state;
private SoftLock lock;
public EntityDeleteAction(
final Serializable id,
final Object[] state,
@ -86,7 +86,7 @@ public final class EntityDeleteAction extends EntityAction {
persister.getRootEntityName(),
session.getEntityMode(),
session.getFactory()
);
);
lock = persister.getCacheAccessStrategy().lockItem( ck, version );
}
else {
@ -112,19 +112,19 @@ public final class EntityDeleteAction extends EntityAction {
persistenceContext.removeEntity(key);
persistenceContext.removeProxy(key);
if ( persister.hasCache() ) persister.getCacheAccessStrategy().remove( ck );
if ( persister.hasCache() ) {
persister.getCacheAccessStrategy().remove( ck );
}
postDelete();
if ( getSession().getFactory().getStatistics().isStatisticsEnabled() && !veto ) {
getSession().getFactory().getStatisticsImplementor()
.deleteEntity( getPersister().getEntityName() );
getSession().getFactory().getStatisticsImplementor().deleteEntity( getPersister().getEntityName() );
}
}
private boolean preDelete() {
PreDeleteEventListener[] preListeners = getSession().getListeners()
.getPreDeleteEventListeners();
PreDeleteEventListener[] preListeners = getSession().getListeners().getPreDeleteEventListeners();
boolean veto = false;
if (preListeners.length>0) {
PreDeleteEvent preEvent = new PreDeleteEvent( getInstance(), getId(), state, getPersister() ,(EventSource) getSession() );
@ -169,29 +169,21 @@ public final class EntityDeleteAction extends EntityAction {
}
}
public void afterTransactionCompletion(boolean success) throws HibernateException {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) throws HibernateException {
if ( getPersister().hasCache() ) {
final CacheKey ck = new CacheKey(
getId(),
getPersister().getIdentifierType(),
final CacheKey ck = new CacheKey(
getId(),
getPersister().getIdentifierType(),
getPersister().getRootEntityName(),
getSession().getEntityMode(),
getSession().getEntityMode(),
getSession().getFactory()
);
);
getPersister().getCacheAccessStrategy().unlockItem( ck, lock );
}
postCommitDelete();
}
protected boolean hasPostCommitEventListeners() {
return getSession().getListeners().getPostCommitDeleteEventListeners().length>0;
return getSession().getListeners().getPostCommitDeleteEventListeners().length > 0;
}
}

View File

@ -37,7 +37,8 @@ import org.hibernate.event.PreInsertEventListener;
import org.hibernate.event.EventSource;
import org.hibernate.persister.entity.EntityPersister;
public final class EntityIdentityInsertAction extends EntityAction {
public final class EntityIdentityInsertAction extends EntityAction {
private final Object[] state;
private final boolean isDelayed;
private final EntityKey delayedEntityKey;
@ -53,11 +54,10 @@ public final class EntityIdentityInsertAction extends EntityAction {
super( session, null, instance, persister );
this.state = state;
this.isDelayed = isDelayed;
delayedEntityKey = isDelayed ? generateDelayedEntityKey() : null;
this.delayedEntityKey = isDelayed ? generateDelayedEntityKey() : null;
}
public void execute() throws HibernateException {
final EntityPersister persister = getPersister();
final SessionImplementor session = getSession();
final Object instance = getInstance();
@ -89,12 +89,29 @@ public final class EntityIdentityInsertAction extends EntityAction {
postInsert();
if ( session.getFactory().getStatistics().isStatisticsEnabled() && !veto ) {
session.getFactory().getStatisticsImplementor()
.insertEntity( getPersister().getEntityName() );
session.getFactory().getStatisticsImplementor().insertEntity( getPersister().getEntityName() );
}
}
public boolean needsAfterTransactionCompletion() {
//TODO: simply remove this override if we fix the above todos
return hasPostCommitEventListeners();
}
protected boolean hasPostCommitEventListeners() {
return getSession().getListeners().getPostCommitInsertEventListeners().length>0;
}
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
//TODO: reenable if we also fix the above todo
/*EntityPersister persister = getEntityPersister();
if ( success && persister.hasCache() && !persister.isCacheInvalidationRequired() ) {
persister.getCache().afterInsert( getGeneratedId(), cacheEntry );
}*/
postCommitInsert();
}
private void postInsert() {
if ( isDelayed ) {
getSession().getPersistenceContext().replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId );
@ -145,26 +162,6 @@ public final class EntityIdentityInsertAction extends EntityAction {
return veto;
}
//Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
public void afterTransactionCompletion(boolean success) throws HibernateException {
//TODO: reenable if we also fix the above todo
/*EntityPersister persister = getEntityPersister();
if ( success && persister.hasCache() && !persister.isCacheInvalidationRequired() ) {
persister.getCache().afterInsert( getGeneratedId(), cacheEntry );
}*/
postCommitInsert();
}
public boolean hasAfterTransactionCompletion() {
//TODO: simply remove this override
// if we fix the above todos
return hasPostCommitEventListeners();
}
protected boolean hasPostCommitEventListeners() {
return getSession().getListeners().getPostCommitInsertEventListeners().length>0;
}
public final Serializable getGeneratedId() {
return generatedId;
}

View File

@ -116,7 +116,6 @@ public final class EntityInsertAction extends EntityAction {
session.getEntityMode(),
session.getFactory()
);
// boolean put = persister.getCache().insert(ck, cacheEntry);
boolean put = persister.getCacheAccessStrategy().insert( ck, cacheEntry, version );
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
@ -181,8 +180,10 @@ public final class EntityInsertAction extends EntityAction {
return veto;
}
//Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
public void afterTransactionCompletion(boolean success) throws HibernateException {
/**
* {@inheritDoc}
*/
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) throws HibernateException {
EntityPersister persister = getPersister();
if ( success && isCachePutEnabled( persister, getSession() ) ) {
final CacheKey ck = new CacheKey(
@ -191,7 +192,7 @@ public final class EntityInsertAction extends EntityAction {
persister.getRootEntityName(),
getSession().getEntityMode(),
getSession().getFactory()
);
);
boolean put = persister.getCacheAccessStrategy().afterInsert( ck, cacheEntry, version );
if ( put && getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
@ -207,16 +208,9 @@ public final class EntityInsertAction extends EntityAction {
}
private boolean isCachePutEnabled(EntityPersister persister, SessionImplementor session) {
return persister.hasCache() &&
!persister.isCacheInvalidationRequired() &&
session.getCacheMode().isPutEnabled();
return persister.hasCache()
&& !persister.isCacheInvalidationRequired()
&& session.getCacheMode().isPutEnabled();
}
}

View File

@ -46,14 +46,13 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.TypeFactory;
public final class EntityUpdateAction extends EntityAction {
private final Object[] state;
private final Object[] previousState;
private final Object previousVersion;
private Object nextVersion;
private final int[] dirtyFields;
private final boolean hasDirtyCollection;
private final Object rowId;
private Object nextVersion;
private Object cacheEntry;
private SoftLock lock;
@ -149,7 +148,7 @@ public final class EntityUpdateAction extends EntityAction {
nextVersion = Versioning.getVersion( state, persister );
}
}
// have the entity entry perform post-update processing, passing it the
// have the entity entry doAfterTransactionCompletion post-update processing, passing it the
// update state and the new version (if one).
entry.postUpdate( instance, state, nextVersion );
}
@ -240,7 +239,11 @@ public final class EntityUpdateAction extends EntityAction {
return veto;
}
public void afterTransactionCompletion(boolean success) throws CacheException {
protected boolean hasPostCommitEventListeners() {
return getSession().getListeners().getPostCommitUpdateEventListeners().length>0;
}
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) throws CacheException {
EntityPersister persister = getPersister();
if ( persister.hasCache() ) {
@ -266,10 +269,6 @@ public final class EntityUpdateAction extends EntityAction {
postCommitUpdate();
}
protected boolean hasPostCommitEventListeners() {
return getSession().getListeners().getPostCommitUpdateEventListeners().length>0;
}
}

View File

@ -34,29 +34,43 @@ import java.io.Serializable;
* together with required second-level cache management.
*
* @author Gavin King
* @author Steve Ebersole
*/
public interface Executable {
/**
* Called before executing any actions
*/
public void beforeExecutions() throws HibernateException;
/**
* Execute this action
*/
public void execute() throws HibernateException;
/**
* Do we need to retain this instance until after the
* transaction completes?
* @return false if this class defines a no-op
* <tt>hasAfterTransactionCompletion()</tt>
*/
public boolean hasAfterTransactionCompletion();
/**
* Called after the transaction completes
*/
public void afterTransactionCompletion(boolean success) throws HibernateException;
/**
* What spaces (tables) are affected by this action?
*
* @return The spaces affected by this action.
*/
public Serializable[] getPropertySpaces();
/**
* Called before executing any actions. Gives actions a chance to perform any preparation.
*
* @throws HibernateException Indicates a problem during preparation.
*/
public void beforeExecutions() throws HibernateException;
/**
* Execute this action
*
* @throws HibernateException Indicates a problem during execution.
*/
public void execute() throws HibernateException;
/**
* Get the after-transaction-completion process, if any, for this action.
*
* @return The after-transaction-completion process, or null if we have no
* after-transaction-completion process
*/
public AfterTransactionCompletionProcess getAfterTransactionCompletionProcess();
/**
* Get the before-transaction-completion process, if any, for this action.
*
* @return The before-transaction-completion process, or null if we have no
* before-transaction-completion process
*/
public BeforeTransactionCompletionProcess getBeforeTransactionCompletionProcess();
}

View File

@ -319,7 +319,7 @@ public class ThreadLocalSessionContext implements CurrentSessionContext {
// method call to pass through since the real session
// will complain by throwing an appropriate exception;
// NOTE that allowing close() above has the same basic effect,
// but we capture that there simply to perform the unbind...
// but we capture that there simply to doAfterTransactionCompletion the unbind...
}
else if ( !realSession.getTransaction().isActive() ) {
// limit the methods available if no transaction is active

View File

@ -287,7 +287,7 @@ public class MySQLDialect extends Dialect {
public Boolean performTemporaryTableDDLInIsolation() {
// because we [drop *temporary* table...] we do not
// have to perform these in isolation.
// have to doAfterTransactionCompletion these in isolation.
return Boolean.FALSE;
}

View File

@ -34,6 +34,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,6 +50,8 @@ import org.hibernate.action.EntityIdentityInsertAction;
import org.hibernate.action.EntityInsertAction;
import org.hibernate.action.EntityUpdateAction;
import org.hibernate.action.Executable;
import org.hibernate.action.AfterTransactionCompletionProcess;
import org.hibernate.action.BeforeTransactionCompletionProcess;
import org.hibernate.cache.CacheException;
import org.hibernate.type.Type;
@ -82,7 +85,8 @@ public class ActionQueue {
private ArrayList collectionUpdates;
private ArrayList collectionRemovals;
private ArrayList executions;
private AfterTransactionCompletionProcessQueue afterTransactionProcesses;
private BeforeTransactionCompletionProcessQueue beforeTransactionProcesses;
/**
* Constructs an action queue bound to the given session.
@ -103,7 +107,8 @@ public class ActionQueue {
collectionRemovals = new ArrayList( INIT_QUEUE_LIST_SIZE );
collectionUpdates = new ArrayList( INIT_QUEUE_LIST_SIZE );
executions = new ArrayList( INIT_QUEUE_LIST_SIZE * 3 );
afterTransactionProcesses = new AfterTransactionCompletionProcessQueue( session );
beforeTransactionProcesses = new BeforeTransactionCompletionProcessQueue( session );
}
public void clear() {
@ -145,8 +150,15 @@ public class ActionQueue {
}
public void addAction(BulkOperationCleanupAction cleanupAction) {
// Add these directly to the executions queue
executions.add( cleanupAction );
registerProcess( cleanupAction.getAfterTransactionCompletionProcess() );
}
public void registerProcess(AfterTransactionCompletionProcess process) {
afterTransactionProcesses.register( process );
}
public void registerProcess(BeforeTransactionCompletionProcess process) {
beforeTransactionProcesses.register( process );
}
/**
@ -189,29 +201,14 @@ public class ActionQueue {
* @param success Was the transaction successful.
*/
public void afterTransactionCompletion(boolean success) {
int size = executions.size();
final boolean invalidateQueryCache = session.getFactory().getSettings().isQueryCacheEnabled();
for ( int i = 0; i < size; i++ ) {
try {
Executable exec = ( Executable ) executions.get( i );
try {
exec.afterTransactionCompletion( success );
}
finally {
if ( invalidateQueryCache ) {
session.getFactory().getUpdateTimestampsCache().invalidate( exec.getPropertySpaces() );
}
}
}
catch ( CacheException ce ) {
log.error( "could not release a cache lock", ce );
// continue loop
}
catch ( Exception e ) {
throw new AssertionFailure( "Exception releasing cache locks", e );
}
}
executions.clear();
afterTransactionProcesses.afterTransactionCompletion( success );
}
/**
* Execute any registered {@link BeforeTransactionCompletionProcess}
*/
public void beforeTransactionCompletion() {
beforeTransactionProcesses.beforeTransactionCompletion();
}
/**
@ -267,16 +264,18 @@ public class ActionQueue {
}
public void execute(Executable executable) {
final boolean lockQueryCache = session.getFactory().getSettings().isQueryCacheEnabled();
if ( executable.hasAfterTransactionCompletion() || lockQueryCache ) {
executions.add( executable );
try {
executable.execute();
}
if ( lockQueryCache ) {
session.getFactory()
.getUpdateTimestampsCache()
.preinvalidate( executable.getPropertySpaces() );
finally {
beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() );
if ( session.getFactory().getSettings().isQueryCacheEnabled() ) {
final String[] spaces = (String[]) executable.getPropertySpaces();
afterTransactionProcesses.addSpacesToInvalidate( spaces );
session.getFactory().getUpdateTimestampsCache().preinvalidate( executable.getPropertySpaces() );
}
afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() );
}
executable.execute();
}
private void prepareActions(List queue) throws HibernateException {
@ -376,7 +375,8 @@ public class ActionQueue {
}
public boolean hasAfterTransactionActions() {
return executions.size() > 0;
// todo : method is not used anywhere; why is it here?
return afterTransactionProcesses.processes.size() > 0;
}
public boolean hasAnyQueuedActions() {
@ -394,7 +394,7 @@ public class ActionQueue {
*
* @param oos The stream to which the action queue should get written
*
* @throws IOException
* @throws IOException Indicates an error writing to the stream
*/
public void serialize(ObjectOutputStream oos) throws IOException {
log.trace( "serializing action-queue" );
@ -447,8 +447,12 @@ public class ActionQueue {
* action queue
*
* @param ois The stream from which to read the action queue
* @param session The session to which the action queue belongs
*
* @throws IOException
* @return The deserialized action queue
*
* @throws IOException indicates a problem reading from the stream
* @throws ClassNotFoundException Generally means we were unable to locate user classes.
*/
public static ActionQueue deserialize(
ObjectInputStream ois,
@ -500,6 +504,93 @@ public class ActionQueue {
return rtn;
}
private static class BeforeTransactionCompletionProcessQueue {
private SessionImplementor session;
private List processes = new ArrayList();
private BeforeTransactionCompletionProcessQueue(SessionImplementor session) {
this.session = session;
}
public void register(BeforeTransactionCompletionProcess process) {
if ( process == null ) {
return;
}
processes.add( process );
}
public void beforeTransactionCompletion() {
final int size = processes.size();
for ( int i = 0; i < size; i++ ) {
try {
BeforeTransactionCompletionProcess process = ( BeforeTransactionCompletionProcess ) processes.get( i );
process.doBeforeTransactionCompletion( session );
}
catch ( HibernateException he ) {
throw he;
}
catch ( Exception e ) {
throw new AssertionFailure( "Unable to perform beforeTransactionCompletion callback", e );
}
}
processes.clear();
}
}
private static class AfterTransactionCompletionProcessQueue {
private SessionImplementor session;
private Set querySpacesToInvalidate = new HashSet();
private List processes = new ArrayList( INIT_QUEUE_LIST_SIZE * 3 );
private AfterTransactionCompletionProcessQueue(SessionImplementor session) {
this.session = session;
}
public void addSpacesToInvalidate(String[] spaces) {
if ( spaces == null ) {
return;
}
for ( int i = 0, max = spaces.length; i < max; i++ ) {
addSpaceToInvalidate( spaces[i] );
}
}
public void addSpaceToInvalidate(String space) {
querySpacesToInvalidate.add( space );
}
public void register(AfterTransactionCompletionProcess process) {
if ( process == null ) {
return;
}
processes.add( process );
}
public void afterTransactionCompletion(boolean success) {
final int size = processes.size();
for ( int i = 0; i < size; i++ ) {
try {
AfterTransactionCompletionProcess process = ( AfterTransactionCompletionProcess ) processes.get( i );
process.doAfterTransactionCompletion( success, session );
}
catch ( CacheException ce ) {
log.error( "could not release a cache lock", ce );
// continue loop
}
catch ( Exception e ) {
throw new AssertionFailure( "Exception releasing cache locks", e );
}
}
processes.clear();
if ( session.getFactory().getSettings().isQueryCacheEnabled() ) {
session.getFactory().getUpdateTimestampsCache().invalidate(
( String[] ) querySpacesToInvalidate.toArray( new String[ querySpacesToInvalidate.size()] )
);
}
querySpacesToInvalidate.clear();
}
}
/**
* Sorts the insert actions using more hashes.
@ -507,7 +598,6 @@ public class ActionQueue {
* @author Jay Erb
*/
private class InsertActionSorter {
// the mapping of entity names to their latest batch numbers.
private HashMap latestBatches = new HashMap();
private HashMap entityBatchNumber;
@ -567,10 +657,16 @@ public class ActionQueue {
}
/**
* Finds an acceptable batch for this entity to be a member.
* Finds an acceptable batch for this entity to be a member as part of the {@link InsertActionSorter}
*
* @param action The action being sorted
* @param entityName The name of the entity affected by the action
*
* @return An appropriate batch number; todo document this process better
*/
private Integer findBatchNumber(EntityInsertAction action,
String entityName) {
private Integer findBatchNumber(
EntityInsertAction action,
String entityName) {
// loop through all the associated entities and make sure they have been
// processed before the latest
// batch associated with this entity type.

View File

@ -179,7 +179,7 @@ public class NativeSQLQueryPlan implements Serializable {
( ( EventSource ) session ).getActionQueue().addAction( action );
}
else {
action.afterTransactionCompletion( true );
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
}
}

View File

@ -126,7 +126,7 @@ public class Isolater {
connection = session.getBatcher().openConnection();
// perform the actual work
// doAfterTransactionCompletion the actual work
work.doWork( connection );
// if everything went ok, commit the transaction and close the obtained

View File

@ -287,7 +287,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY;
}
// check nullability but do not perform command execute
// check nullability but do not doAfterTransactionCompletion command execute
// we'll use scheduled updates for that.
new Nullability(session).checkNullability( values, persister, true );

View File

@ -547,7 +547,7 @@ public class QueryTranslatorImpl implements FilterTranslator {
if ( persister.isMultiTable() ) {
// even here, if only properties mapped to the "base table" are referenced
// in the set and where clauses, this could be handled by the BasicDelegate.
// TODO : decide if it is better performance-wise to perform that check, or to simply use the MultiTableUpdateDelegate
// TODO : decide if it is better performance-wise to doAfterTransactionCompletion that check, or to simply use the MultiTableUpdateDelegate
return new MultiTableUpdateExecutor( walker );
}
else {

View File

@ -245,7 +245,7 @@ public abstract class AbstractStatementExecutor implements StatementExecutor {
( ( EventSource ) session ).getActionQueue().addAction( action );
}
else {
action.afterTransactionCompletion( true );
action.getAfterTransactionCompletionProcess().doAfterTransactionCompletion( true, session );
}
}

View File

@ -59,7 +59,7 @@ public class MultiTableDeleteExecutor extends AbstractStatementExecutor {
super( walker, log );
if ( !walker.getSessionFactoryHelper().getFactory().getDialect().supportsTemporaryTables() ) {
throw new HibernateException( "cannot perform multi-table deletes using dialect not supporting temp tables" );
throw new HibernateException( "cannot doAfterTransactionCompletion multi-table deletes using dialect not supporting temp tables" );
}
DeleteStatement deleteStatement = ( DeleteStatement ) walker.getAST();

View File

@ -63,7 +63,7 @@ public class MultiTableUpdateExecutor extends AbstractStatementExecutor {
super( walker, log );
if ( !walker.getSessionFactoryHelper().getFactory().getDialect().supportsTemporaryTables() ) {
throw new HibernateException( "cannot perform multi-table updates using dialect not supporting temp tables" );
throw new HibernateException( "cannot doAfterTransactionCompletion multi-table updates using dialect not supporting temp tables" );
}
UpdateStatement updateStatement = ( UpdateStatement ) walker.getAST();

View File

@ -333,7 +333,7 @@ public class FromClause extends HqlSqlWalkerNode implements HqlSqlTokenTypes, Di
// the subquery has already been performed (meaning that for
// theta-join dialects, the join conditions have already been moved
// over to the where clause). A "simple" solution here might to
// perform "join post processing" once for the entire query (including
// doAfterTransactionCompletion "join post processing" once for the entire query (including
// any subqueries) at one fell swoop
}

View File

@ -220,7 +220,7 @@ public class IntoClause extends HqlSqlWalkerNode implements DisplayableNode {
return true;
}
// otherwise, perform a "deep equivalence" check...
// otherwise, doAfterTransactionCompletion a "deep equivalence" check...
if ( !target.getReturnedClass().isAssignableFrom( source.getReturnedClass() ) ) {
return false;

View File

@ -423,6 +423,7 @@ public final class SessionImpl extends AbstractSessionImpl
public void beforeTransactionCompletion(Transaction tx) {
log.trace( "before transaction completion" );
actionQueue.beforeTransactionCompletion();
if ( rootSession == null ) {
try {
interceptor.beforeTransactionCompletion(tx);
@ -1185,7 +1186,7 @@ public final class SessionImpl extends AbstractSessionImpl
errorIfClosed();
checkTransactionSynchStatus();
if ( query == null ) {
throw new IllegalArgumentException("attempt to perform delete-by-query with null query");
throw new IllegalArgumentException("attempt to doAfterTransactionCompletion delete-by-query with null query");
}
if ( log.isTraceEnabled() ) {

View File

@ -161,7 +161,7 @@ public class Expectations {
public static final Expectation NONE = new Expectation() {
public void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition) {
// explicitly perform no checking...
// explicitly doAfterTransactionCompletion no checking...
}
public int prepare(PreparedStatement statement) {

View File

@ -352,7 +352,7 @@ public abstract class Loader {
throw JDBCExceptionHelper.convert(
factory.getSQLExceptionConverter(),
sqle,
"could not perform sequential read of results (forward)",
"could not doAfterTransactionCompletion sequential read of results (forward)",
getSQLString()
);
}
@ -400,7 +400,7 @@ public abstract class Loader {
}
// We call getKeyFromResultSet() here so that we can know the
// key value upon which to perform the breaking logic. However,
// key value upon which to doAfterTransactionCompletion the breaking logic. However,
// it is also then called from getRowFromResultSet() which is certainly
// not the most efficient. But the call here is needed, and there
// currently is no other way without refactoring of the doQuery()/getRowFromResultSet()
@ -419,7 +419,7 @@ public abstract class Loader {
throw JDBCExceptionHelper.convert(
factory.getSQLExceptionConverter(),
sqle,
"could not perform sequential read of results (forward)",
"could not doAfterTransactionCompletion sequential read of results (forward)",
getSQLString()
);
}
@ -538,14 +538,14 @@ public abstract class Loader {
// at the first physical row we are interested in loading
resultSet.next();
// and perform the load
// and doAfterTransactionCompletion the load
return sequentialLoad( resultSet, session, queryParameters, returnProxies, keyToRead );
}
catch ( SQLException sqle ) {
throw JDBCExceptionHelper.convert(
factory.getSQLExceptionConverter(),
sqle,
"could not perform sequential read of results (forward)",
"could not doAfterTransactionCompletion sequential read of results (forward)",
getSQLString()
);
}

View File

@ -2794,7 +2794,7 @@ public abstract class AbstractEntityPersister
// need to treat this as if it where optimistic-lock="all" (dirty does *not* make sense);
// first we need to locate the "loaded" state
//
// Note, it potentially could be a proxy, so perform the location the safe way...
// Note, it potentially could be a proxy, so doAfterTransactionCompletion the location the safe way...
EntityKey key = new EntityKey( id, this, session.getEntityMode() );
Object entity = session.getPersistenceContext().getEntity( key );
if ( entity != null ) {

View File

@ -120,7 +120,7 @@ public abstract class CollectionType extends AbstractType implements Association
}
public int getHashCode(Object x, EntityMode entityMode) {
throw new UnsupportedOperationException( "cannot perform lookups on collections" );
throw new UnsupportedOperationException( "cannot doAfterTransactionCompletion lookups on collections" );
}
/**

View File

@ -494,7 +494,7 @@ public class CompositeIdWithGeneratorTest extends FunctionalTestCase {
s = openSession();
t = s.beginTransaction();
// perform a find to show that it will wire together fine
// doAfterTransactionCompletion a find to show that it will wire together fine
PurchaseRecord foundRecord = (PurchaseRecord) s.get(PurchaseRecord.class,
new PurchaseRecord.Id(foundPurchaseNumber, foundPurchaseSequence)
);

View File

@ -527,14 +527,14 @@ public class ASTParserLoadingTest extends FunctionalTestCase {
assertEquals( 1, types.length );
assertTrue( types[0] instanceof ComponentType );
// Test the ability to perform comparisions between component values
// Test the ability to doAfterTransactionCompletion comparisions between component values
s.createQuery( "from Human h where h.name = h.name" ).list();
s.createQuery( "from Human h where h.name = :name" ).setParameter( "name", new Name() ).list();
s.createQuery( "from Human where name = :name" ).setParameter( "name", new Name() ).list();
s.createQuery( "from Human h where :name = h.name" ).setParameter( "name", new Name() ).list();
s.createQuery( "from Human h where :name <> h.name" ).setParameter( "name", new Name() ).list();
// Test the ability to perform comparisions between a component and an explicit row-value
// Test the ability to doAfterTransactionCompletion comparisions between a component and an explicit row-value
s.createQuery( "from Human h where h.name = ('John', 'X', 'Doe')" ).list();
s.createQuery( "from Human h where ('John', 'X', 'Doe') = h.name" ).list();
s.createQuery( "from Human h where ('John', 'X', 'Doe') <> h.name" ).list();

View File

@ -74,7 +74,7 @@ public class JPALockTest extends AbstractJPATest {
Long itemId = item.getId();
// perform the isolated update
// doAfterTransactionCompletion the isolated update
s1 = getSessions().openSession();
t1 = s1.beginTransaction();
item = (Item) s1.get( Item.class, itemId );