[hibernate-commits] Hibernate SVN: r14308 - core/branches/Branch_3_2/src/org/hibernate/engine.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Sun Feb 3 00:57:08 EST 2008


Author: steve.ebersole at jboss.com
Date: 2008-02-03 00:57:08 -0500 (Sun, 03 Feb 2008)
New Revision: 14308

Modified:
   core/branches/Branch_3_2/src/org/hibernate/engine/ActionQueue.java
Log:
HHH-2957 : ActionQueue insertions sorting performance

Modified: core/branches/Branch_3_2/src/org/hibernate/engine/ActionQueue.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/engine/ActionQueue.java	2008-02-03 05:56:39 UTC (rev 14307)
+++ core/branches/Branch_3_2/src/org/hibernate/engine/ActionQueue.java	2008-02-03 05:57:08 UTC (rev 14308)
@@ -1,31 +1,57 @@
-// $Id$
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2007, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * 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.engine;
 
-import org.hibernate.action.EntityInsertAction;
-import org.hibernate.action.EntityDeleteAction;
-import org.hibernate.action.Executable;
-import org.hibernate.action.EntityUpdateAction;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.action.BulkOperationCleanupAction;
 import org.hibernate.action.CollectionRecreateAction;
 import org.hibernate.action.CollectionRemoveAction;
 import org.hibernate.action.CollectionUpdateAction;
+import org.hibernate.action.EntityDeleteAction;
 import org.hibernate.action.EntityIdentityInsertAction;
-import org.hibernate.action.BulkOperationCleanupAction;
-import org.hibernate.HibernateException;
-import org.hibernate.AssertionFailure;
+import org.hibernate.action.EntityInsertAction;
+import org.hibernate.action.EntityUpdateAction;
+import org.hibernate.action.Executable;
 import org.hibernate.cache.CacheException;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.hibernate.type.Type;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.io.ObjectInputStream;
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.ObjectOutputStream;
-
 /**
  * Responsible for maintaining the queue of actions related to events.
  * </p>
@@ -167,7 +193,7 @@
 		final boolean invalidateQueryCache = session.getFactory().getSettings().isQueryCacheEnabled();
 		for ( int i = 0; i < size; i++ ) {
 			try {
-				Executable exec = ( Executable ) executions.get(i);
+				Executable exec = ( Executable ) executions.get( i );
 				try {
 					exec.afterTransactionCompletion( success );
 				}
@@ -177,11 +203,11 @@
 					}
 				}
 			}
-			catch (CacheException ce) {
+			catch ( CacheException ce ) {
 				log.error( "could not release a cache lock", ce );
 				// continue loop
 			}
-			catch (Exception e) {
+			catch ( Exception e ) {
 				throw new AssertionFailure( "Exception releasing cache locks", e );
 			}
 		}
@@ -193,16 +219,17 @@
 	 * 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.
+	 *         tables; false otherwise.
 	 */
 	public boolean areTablesToBeUpdated(Set tables) {
 		return areTablesToUpdated( updates, tables ) ||
-		       areTablesToUpdated( insertions, tables ) ||
-		       areTablesToUpdated( deletions, tables ) ||
-		       areTablesToUpdated( collectionUpdates, tables ) ||
-		       areTablesToUpdated( collectionCreations, tables ) ||
-		       areTablesToUpdated( collectionRemovals, tables );
+				areTablesToUpdated( insertions, tables ) ||
+				areTablesToUpdated( deletions, tables ) ||
+				areTablesToUpdated( collectionUpdates, tables ) ||
+				areTablesToUpdated( collectionCreations, tables ) ||
+				areTablesToUpdated( collectionRemovals, tables );
 	}
 
 	/**
@@ -217,10 +244,12 @@
 	private static boolean areTablesToUpdated(List executables, Set tablespaces) {
 		int size = executables.size();
 		for ( int j = 0; j < size; j++ ) {
-			Serializable[] spaces = ( (Executable) executables.get(j) ).getPropertySpaces();
+			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] );
+					if ( log.isDebugEnabled() ) {
+						log.debug( "changes must be flushed to space: " + spaces[i] );
+					}
 					return true;
 				}
 			}
@@ -231,7 +260,7 @@
 	private void executeActions(List list) throws HibernateException {
 		int size = list.size();
 		for ( int i = 0; i < size; i++ ) {
-			execute( (Executable) list.get(i) );
+			execute( ( Executable ) list.get( i ) );
 		}
 		list.clear();
 		session.getBatcher().executeBatch();
@@ -242,18 +271,18 @@
 		if ( executable.hasAfterTransactionCompletion() || lockQueryCache ) {
 			executions.add( executable );
 		}
-		if (lockQueryCache) {
+		if ( lockQueryCache ) {
 			session.getFactory()
-				.getUpdateTimestampsCache()
-				.preinvalidate( executable.getPropertySpaces() );
+					.getUpdateTimestampsCache()
+					.preinvalidate( executable.getPropertySpaces() );
 		}
 		executable.execute();
 	}
 
 	private void prepareActions(List queue) throws HibernateException {
 		int size = queue.size();
-		for ( int i=0; i<size; i++ ) {
-			Executable executable = ( Executable ) queue.get(i);
+		for ( int i = 0; i < size; i++ ) {
+			Executable executable = ( Executable ) queue.get( i );
 			executable.beforeExecutions();
 		}
 	}
@@ -265,13 +294,13 @@
 	 */
 	public String toString() {
 		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("]")
+				.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();
 	}
 
@@ -328,73 +357,11 @@
 	 * violations
 	 */
 	private void sortInsertActions() {
-		// 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() );
-			}
-		}
+		new InsertActionSorter().sort();
 	}
 
 	public ArrayList cloneDeletions() {
-		return (ArrayList) deletions.clone();
+		return ( ArrayList ) deletions.clone();
 	}
 
 	public void clearFromFlushNeededCheck(int previousCollectionRemovalSize) {
@@ -403,18 +370,18 @@
 		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);
+		for ( int i = collectionRemovals.size() - 1; i >= previousCollectionRemovalSize; i-- ) {
+			collectionRemovals.remove( i );
 		}
 	}
 
 	public boolean hasAnyQueuedActions() {
 		return updates.size() > 0 ||
-		       insertions.size() > 0 ||
-		       deletions.size() > 0 ||
-		       collectionUpdates.size() > 0 ||
-		       collectionRemovals.size() > 0 ||
-		       collectionCreations.size() > 0;
+				insertions.size() > 0 ||
+				deletions.size() > 0 ||
+				collectionUpdates.size() > 0 ||
+				collectionRemovals.size() > 0 ||
+				collectionCreations.size() > 0;
 	}
 
 	/**
@@ -422,6 +389,7 @@
 	 * action queue
 	 *
 	 * @param oos The stream to which the action queue should get written
+	 *
 	 * @throws IOException
 	 */
 	public void serialize(ObjectOutputStream oos) throws IOException {
@@ -475,11 +443,12 @@
 	 * action queue
 	 *
 	 * @param ois The stream from which to read the action queue
+	 *
 	 * @throws IOException
 	 */
 	public static ActionQueue deserialize(
 			ObjectInputStream ois,
-	        SessionImplementor session) throws IOException, ClassNotFoundException {
+			SessionImplementor session) throws IOException, ClassNotFoundException {
 		log.trace( "deserializing action-queue" );
 		ActionQueue rtn = new ActionQueue( session );
 
@@ -527,4 +496,120 @@
 		return rtn;
 	}
 
+
+	/**
+	 * Sorts the insert actions using more hashes.
+	 *
+	 * @author Jay Erb
+	 */
+	private class InsertActionSorter {
+
+		// the mapping of entity names to their latest batch numbers.
+		private HashMap latestBatches = new HashMap();
+		private HashMap entityBatchNumber;
+
+		// the map of batch numbers to EntityInsertAction lists
+		private HashMap actionBatches = new HashMap();
+
+		public InsertActionSorter() {
+			//optimize the hash size to eliminate a rehash.
+			entityBatchNumber = new HashMap( insertions.size() + 1, 1.0f );
+		}
+
+		/**
+		 * Sort the insert actions.
+		 */
+		public void sort() {
+
+			// the list of entity names that indicate the batch number
+			for ( Iterator actionItr = insertions.iterator(); actionItr.hasNext(); ) {
+				EntityInsertAction action = ( EntityInsertAction ) actionItr.next();
+				// remove the current element from insertions. It will be added back later.
+				String entityName = action.getEntityName();
+
+				// the entity associated with the current action.
+				Object currentEntity = action.getInstance();
+
+				Integer batchNumber;
+				if ( latestBatches.containsKey( entityName ) ) {
+					// There is already an existing batch for this type of entity.
+					// Check to see if the latest batch is acceptable.
+					batchNumber = findBatchNumber( action, entityName );
+				}
+				else {
+					// add an entry for this type of entity.
+					// we can be assured that all referenced entities have already
+					// been processed,
+					// so specify that this entity is with the latest batch.
+					// doing the batch number before adding the name to the list is
+					// a faster way to get an accurate number.
+
+					batchNumber = new Integer( actionBatches.size() );
+					latestBatches.put( entityName, batchNumber );
+				}
+				entityBatchNumber.put( currentEntity, batchNumber );
+				addToBatch( batchNumber, action );
+			}
+			insertions.clear();
+
+			// now rebuild the insertions list. There is a batch for each entry in the name list.
+			for ( int i = 0; i < actionBatches.size(); i++ ) {
+				List batch = ( List ) actionBatches.get( new Integer( i ) );
+				for ( Iterator batchItr = batch.iterator(); batchItr.hasNext(); ) {
+					EntityInsertAction action = ( EntityInsertAction ) batchItr.next();
+					insertions.add( action );
+				}
+			}
+		}
+
+		/**
+		 * Finds an acceptable batch for this entity to be a member.
+		 */
+		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.
+
+			// the current batch number is the latest batch for this entity type.
+			Integer latestBatchNumberForType = ( Integer ) latestBatches.get( entityName );
+
+			// loop through all the associations of the current entity and make sure that they are processed
+			// before the current batch number
+			Object[] propertyValues = action.getState();
+			Type[] propertyTypes = action.getPersister().getClassMetadata()
+					.getPropertyTypes();
+
+			for ( int i = 0; i < propertyValues.length; i++ ) {
+				Object value = propertyValues[i];
+				Type type = propertyTypes[i];
+				if ( type.isEntityType() && value != null ) {
+					// find the batch number associated with the current association, if any.
+					Integer associationBatchNumber = ( Integer ) entityBatchNumber.get( value );
+					if ( associationBatchNumber != null && associationBatchNumber.compareTo( latestBatchNumberForType ) > 0 ) {
+						// create a new batch for this type. The batch number is the number of current batches.
+						latestBatchNumberForType = new Integer( actionBatches.size() );
+						latestBatches.put( entityName, latestBatchNumberForType );
+						// since this entity will now be processed in the latest possible batch,
+						// we can be assured that it will come after all other associations,
+						// there's not need to continue checking.
+						break;
+					}
+				}
+			}
+			return latestBatchNumberForType;
+		}
+
+		private void addToBatch(Integer batchNumber, EntityInsertAction action) {
+			List actions = ( List ) actionBatches.get( batchNumber );
+
+			if ( actions == null ) {
+				actions = new LinkedList();
+				actionBatches.put( batchNumber, actions );
+			}
+			actions.add( action );
+		}
+
+	}
+
 }




More information about the hibernate-commits mailing list