FileDocCategorySizeDatePackage
ActionQueue.javaAPI DocHibernate 3.2.517456Wed Apr 11 09:25:14 BST 2007org.hibernate.engine

ActionQueue

public class ActionQueue extends Object
Responsible for maintaining the queue of actions related to events.

The ActionQueue holds the DML operations queued as part of a session's transactional-write-behind semantics. DML operations are queued here until a flush forces them to be executed against the database.
author
Steve Ebersole

Fields Summary
private static final Log
log
private static final int
INIT_QUEUE_LIST_SIZE
private SessionImplementor
session
private ArrayList
insertions
private ArrayList
deletions
private ArrayList
updates
private ArrayList
collectionCreations
private ArrayList
collectionUpdates
private ArrayList
collectionRemovals
private ArrayList
executions
Constructors Summary
public ActionQueue(SessionImplementor session)
Constructs an action queue bound to the given session.

param
session The session "owning" this queue.


	                	 
	   
		this.session = session;
		init();
	
Methods Summary
public voidaddAction(org.hibernate.action.EntityIdentityInsertAction insert)

		insertions.add( insert );
	
public voidaddAction(org.hibernate.action.BulkOperationCleanupAction cleanupAction)

		// Add these directly to the executions queue
		executions.add( cleanupAction );
	
public voidaddAction(org.hibernate.action.EntityInsertAction action)

		insertions.add( action );
	
public voidaddAction(org.hibernate.action.EntityDeleteAction action)

		deletions.add( action );
	
public voidaddAction(org.hibernate.action.EntityUpdateAction action)

		updates.add( action );
	
public voidaddAction(org.hibernate.action.CollectionRecreateAction action)

		collectionCreations.add( action );
	
public voidaddAction(org.hibernate.action.CollectionRemoveAction action)

		collectionRemovals.add( action );
	
public voidaddAction(org.hibernate.action.CollectionUpdateAction action)

		collectionUpdates.add( action );
	
public voidafterTransactionCompletion(boolean success)
Performs cleanup of any held cache softlocks.

param
success Was the transaction successful.

		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();
	
public booleanareInsertionsOrDeletionsQueued()
Check whether any insertion or deletion actions are currently queued.

return
True if insertions or deletions are currently queued; false otherwise.

		return ( insertions.size() > 0 || deletions.size() > 0 );
	
public booleanareTablesToBeUpdated(java.util.Set tables)
Check whether the given tables/query-spaces are to be executed against given the currently queued actions.

param
tables The table/query-spaces to check.
return
True if we contain pending actions against any of the given tables; false otherwise.

		return areTablesToUpdated( updates, tables ) ||
		       areTablesToUpdated( insertions, tables ) ||
		       areTablesToUpdated( deletions, tables ) ||
		       areTablesToUpdated( collectionUpdates, tables ) ||
		       areTablesToUpdated( collectionCreations, tables ) ||
		       areTablesToUpdated( collectionRemovals, tables );
	
private static booleanareTablesToUpdated(java.util.List executables, java.util.Set tablespaces)

		int size = executables.size();
		for ( int j = 0; j < size; j++ ) {
			Serializable[] spaces = ( (Executable) executables.get(j) ).getPropertySpaces();
			for ( int i = 0; i < spaces.length; i++ ) {
				if ( tablespaces.contains( spaces[i] ) ) {
					if ( log.isDebugEnabled() ) log.debug( "changes must be flushed to space: " + spaces[i] );
					return true;
				}
			}
		}
		return false;
	
public voidclear()

		updates.clear();
		insertions.clear();
		deletions.clear();

		collectionCreations.clear();
		collectionRemovals.clear();
		collectionUpdates.clear();
	
public voidclearFromFlushNeededCheck(int previousCollectionRemovalSize)

		collectionCreations.clear();
		collectionUpdates.clear();
		updates.clear();
		// collection deletions are a special case since update() can add
		// deletions of collections not loaded by the session.
		for ( int i = collectionRemovals.size()-1; i >= previousCollectionRemovalSize; i-- ) {
			collectionRemovals.remove(i);
		}
	
public java.util.ArrayListcloneDeletions()

		return (ArrayList) deletions.clone();
	
public static org.hibernate.engine.ActionQueuedeserialize(java.io.ObjectInputStream ois, SessionImplementor session)
Used by the owning session to explicitly control deserialization of the action queue

param
ois The stream from which to read the action queue
throws
IOException

		log.trace( "deserializing action-queue" );
		ActionQueue rtn = new ActionQueue( session );

		int queueSize = ois.readInt();
		log.trace( "starting deserialization of [" + queueSize + "] insertions entries" );
		rtn.insertions = new ArrayList( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			rtn.insertions.add( ois.readObject() );
		}

		queueSize = ois.readInt();
		log.trace( "starting deserialization of [" + queueSize + "] deletions entries" );
		rtn.deletions = new ArrayList( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			rtn.deletions.add( ois.readObject() );
		}

		queueSize = ois.readInt();
		log.trace( "starting deserialization of [" + queueSize + "] updates entries" );
		rtn.updates = new ArrayList( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			rtn.updates.add( ois.readObject() );
		}

		queueSize = ois.readInt();
		log.trace( "starting deserialization of [" + queueSize + "] collectionUpdates entries" );
		rtn.collectionUpdates = new ArrayList( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			rtn.collectionUpdates.add( ois.readObject() );
		}

		queueSize = ois.readInt();
		log.trace( "starting deserialization of [" + queueSize + "] collectionRemovals entries" );
		rtn.collectionRemovals = new ArrayList( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			rtn.collectionRemovals.add( ois.readObject() );
		}

		queueSize = ois.readInt();
		log.trace( "starting deserialization of [" + queueSize + "] collectionCreations entries" );
		rtn.collectionCreations = new ArrayList( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			rtn.collectionCreations.add( ois.readObject() );
		}
		return rtn;
	
public voidexecute(org.hibernate.action.Executable executable)

		final boolean lockQueryCache = session.getFactory().getSettings().isQueryCacheEnabled();
		if ( executable.hasAfterTransactionCompletion() || lockQueryCache ) {
			executions.add( executable );
		}
		if (lockQueryCache) {
			session.getFactory()
				.getUpdateTimestampsCache()
				.preinvalidate( executable.getPropertySpaces() );
		}
		executable.execute();
	
public voidexecuteActions()
Perform all currently queued actions.

throws
HibernateException error executing queued actions.

		executeActions( insertions );
		executeActions( updates );
		executeActions( collectionRemovals );
		executeActions( collectionUpdates );
		executeActions( collectionCreations );
		executeActions( deletions );
	
private voidexecuteActions(java.util.List list)

		int size = list.size();
		for ( int i = 0; i < size; i++ ) {
			execute( (Executable) list.get(i) );
		}
		list.clear();
		session.getBatcher().executeBatch();
	
public voidexecuteInserts()
Perform all currently queued entity-insertion actions.

throws
HibernateException error executing queued insertion actions.

		executeActions( insertions );
	
public booleanhasAnyQueuedActions()

		return updates.size() > 0 ||
		       insertions.size() > 0 ||
		       deletions.size() > 0 ||
		       collectionUpdates.size() > 0 ||
		       collectionRemovals.size() > 0 ||
		       collectionCreations.size() > 0;
	
private voidinit()

		insertions = new ArrayList( INIT_QUEUE_LIST_SIZE );
		deletions = new ArrayList( INIT_QUEUE_LIST_SIZE );
		updates = new ArrayList( INIT_QUEUE_LIST_SIZE );

		collectionCreations = new ArrayList( INIT_QUEUE_LIST_SIZE );
		collectionRemovals = new ArrayList( INIT_QUEUE_LIST_SIZE );
		collectionUpdates = new ArrayList( INIT_QUEUE_LIST_SIZE );

		executions = new ArrayList( INIT_QUEUE_LIST_SIZE * 3 );
	
public intnumberOfCollectionCreations()

		return collectionCreations.size();
	
public intnumberOfCollectionRemovals()

		return collectionRemovals.size();
	
public intnumberOfCollectionUpdates()

		return collectionUpdates.size();
	
public intnumberOfDeletions()

		return deletions.size();
	
public intnumberOfInsertions()

		return insertions.size();
	
public intnumberOfUpdates()

		return updates.size();
	
public voidprepareActions()
Prepares the internal action queues for execution.

throws
HibernateException error preparing actions.

		prepareActions( collectionRemovals );
		prepareActions( collectionUpdates );
		prepareActions( collectionCreations );
	
private voidprepareActions(java.util.List queue)

		int size = queue.size();
		for ( int i=0; i<size; i++ ) {
			Executable executable = ( Executable ) queue.get(i);
			executable.beforeExecutions();
		}
	
public voidserialize(java.io.ObjectOutputStream oos)
Used by the owning session to explicitly control serialization of the action queue

param
oos The stream to which the action queue should get written
throws
IOException

		log.trace( "serializing action-queue" );

		int queueSize = insertions.size();
		log.trace( "starting serialization of [" + queueSize + "] insertions entries" );
		oos.writeInt( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			oos.writeObject( insertions.get( i ) );
		}

		queueSize = deletions.size();
		log.trace( "starting serialization of [" + queueSize + "] deletions entries" );
		oos.writeInt( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			oos.writeObject( deletions.get( i ) );
		}

		queueSize = updates.size();
		log.trace( "starting serialization of [" + queueSize + "] updates entries" );
		oos.writeInt( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			oos.writeObject( updates.get( i ) );
		}

		queueSize = collectionUpdates.size();
		log.trace( "starting serialization of [" + queueSize + "] collectionUpdates entries" );
		oos.writeInt( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			oos.writeObject( collectionUpdates.get( i ) );
		}

		queueSize = collectionRemovals.size();
		log.trace( "starting serialization of [" + queueSize + "] collectionRemovals entries" );
		oos.writeInt( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			oos.writeObject( collectionRemovals.get( i ) );
		}

		queueSize = collectionCreations.size();
		log.trace( "starting serialization of [" + queueSize + "] collectionCreations entries" );
		oos.writeInt( queueSize );
		for ( int i = 0; i < queueSize; i++ ) {
			oos.writeObject( collectionCreations.get( i ) );
		}
	
public voidsortActions()

		if ( session.getFactory().getSettings().isOrderUpdatesEnabled() ) {
			//sort the updates by pk
			java.util.Collections.sort( updates );
		}
		if ( session.getFactory().getSettings().isOrderInsertsEnabled() ) {
			sortInsertActions();
		}
	
public voidsortCollectionActions()

		if ( session.getFactory().getSettings().isOrderUpdatesEnabled() ) {
			//sort the updates by fk
			java.util.Collections.sort( collectionCreations );
			java.util.Collections.sort( collectionUpdates );
			java.util.Collections.sort( collectionRemovals );
		}
	
private voidsortInsertActions()
Order the {@link #insertions} queue such that we group inserts against the same entity together (without violating constraints). The original order is generated by cascade order, which in turn is based on the directionality of foreign-keys. So even though we will be changing the ordering here, we need to make absolutely certain that we do not circumvent this FK ordering to the extent of causing constraint violations

		// IMPLEMENTATION NOTES:
		//
		// The main data structure in this ordering algorithm is the 'positionToAction'
		// map.  Essentially this can be thought of as an put-ordered map (the problem with
		// actually implementing it that way and doing away with the 'nameList' is that
		// we'd end up having potential duplicate key values).  'positionToAction' maitains
		// a mapping from a position within the 'nameList' structure to a "partial queue"
		// of actions.

		HashMap positionToAction = new HashMap();
		List nameList = new ArrayList();

		loopInsertion: while( !insertions.isEmpty() ) {
			EntityInsertAction action = ( EntityInsertAction ) insertions.remove( 0 );
			String thisEntityName = action.getEntityName();

			// see if we have already encountered this entity-name...
			if ( ! nameList.contains( thisEntityName ) ) {
				// we have not, so create the proper entries in nameList and positionToAction
				ArrayList segmentedActionQueue = new ArrayList();
				segmentedActionQueue.add( action );
				nameList.add( thisEntityName );
				positionToAction.put( new Integer( nameList.indexOf( thisEntityName ) ), segmentedActionQueue );
			}
			else {
				// we have seen it before, so we need to determine if this insert action is
				// is depenedent upon a previously processed action in terms of FK
				// relationships (this FK checking is done against the entity's property-state
				// associated with the action...)
				int lastPos = nameList.lastIndexOf( thisEntityName );
				Object[] states = action.getState();
				for ( int i = 0; i < states.length; i++ ) {
					for ( int j = 0; j < nameList.size(); j++ ) {
						ArrayList tmpList = ( ArrayList ) positionToAction.get( new Integer( j ) );
						for ( int k = 0; k < tmpList.size(); k++ ) {
							final EntityInsertAction checkAction = ( EntityInsertAction ) tmpList.get( k );
							if ( checkAction.getInstance() == states[i] && j > lastPos ) {
								// 'checkAction' is inserting an entity upon which 'action'
								// depends...
								// note: this is an assumption and may not be correct in the case of one-to-one
								ArrayList segmentedActionQueue = new ArrayList();
								segmentedActionQueue.add( action );
								nameList.add( thisEntityName );
								positionToAction.put(new Integer( nameList.lastIndexOf( thisEntityName ) ), segmentedActionQueue );
								continue loopInsertion;
							}
						}
					}
				}

				ArrayList actionQueue = ( ArrayList ) positionToAction.get( new Integer( lastPos ) );
 				actionQueue.add( action );
 			}
 		}

 		// now iterate back through positionToAction map and move entityInsertAction back to insertion list
		for ( int p = 0; p < nameList.size(); p++ ) {
			ArrayList actionQueue = ( ArrayList ) positionToAction.get( new Integer( p ) );
			Iterator itr = actionQueue.iterator();
			while ( itr.hasNext() ) {
				insertions.add( itr.next() );
			}
		}
	
public java.lang.StringtoString()
Returns a string representation of the object.

return
a string representation of the object.

		return new StringBuffer()
				.append("ActionQueue[insertions=").append(insertions)
				.append(" updates=").append(updates)
		        .append(" deletions=").append(deletions)
				.append(" collectionCreations=").append(collectionCreations)
				.append(" collectionRemovals=").append(collectionRemovals)
				.append(" collectionUpdates=").append(collectionUpdates)
		        .append("]")
				.toString();