HHH-3860 - Cascading performance problems when session contains many entities

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18168 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2009-12-08 18:56:15 +00:00
parent 48c2ec7e91
commit 1005f19b1a
3 changed files with 89 additions and 7 deletions

View File

@ -152,6 +152,7 @@ public final class Cascade {
if ( style.doCascade( action ) ) { if ( style.doCascade( action ) ) {
cascadeProperty( cascadeProperty(
parent,
persister.getPropertyValue( parent, i, entityMode ), persister.getPropertyValue( parent, i, entityMode ),
types[i], types[i],
style, style,
@ -180,6 +181,7 @@ public final class Cascade {
* Cascade an action to the child or children * Cascade an action to the child or children
*/ */
private void cascadeProperty( private void cascadeProperty(
final Object parent,
final Object child, final Object child,
final Type type, final Type type,
final CascadeStyle style, final CascadeStyle style,
@ -191,6 +193,7 @@ public final class Cascade {
AssociationType associationType = (AssociationType) type; AssociationType associationType = (AssociationType) type;
if ( cascadeAssociationNow( associationType ) ) { if ( cascadeAssociationNow( associationType ) ) {
cascadeAssociation( cascadeAssociation(
parent,
child, child,
type, type,
style, style,
@ -200,7 +203,7 @@ public final class Cascade {
} }
} }
else if ( type.isComponentType() ) { else if ( type.isComponentType() ) {
cascadeComponent( child, (AbstractComponentType) type, anything ); cascadeComponent( parent, child, (AbstractComponentType) type, anything );
} }
} }
} }
@ -211,6 +214,7 @@ public final class Cascade {
} }
private void cascadeComponent( private void cascadeComponent(
final Object parent,
final Object child, final Object child,
final AbstractComponentType componentType, final AbstractComponentType componentType,
final Object anything) { final Object anything) {
@ -220,6 +224,7 @@ public final class Cascade {
CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i); CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i);
if ( componentPropertyStyle.doCascade(action) ) { if ( componentPropertyStyle.doCascade(action) ) {
cascadeProperty( cascadeProperty(
parent,
children[i], children[i],
types[i], types[i],
componentPropertyStyle, componentPropertyStyle,
@ -231,16 +236,17 @@ public final class Cascade {
} }
private void cascadeAssociation( private void cascadeAssociation(
final Object parent,
final Object child, final Object child,
final Type type, final Type type,
final CascadeStyle style, final CascadeStyle style,
final Object anything, final Object anything,
final boolean isCascadeDeleteEnabled) { final boolean isCascadeDeleteEnabled) {
if ( type.isEntityType() || type.isAnyType() ) { if ( type.isEntityType() || type.isAnyType() ) {
cascadeToOne( child, type, style, anything, isCascadeDeleteEnabled ); cascadeToOne( parent, child, type, style, anything, isCascadeDeleteEnabled );
} }
else if ( type.isCollectionType() ) { else if ( type.isCollectionType() ) {
cascadeCollection( child, style, anything, (CollectionType) type ); cascadeCollection( parent, child, style, anything, (CollectionType) type );
} }
} }
@ -248,6 +254,7 @@ public final class Cascade {
* Cascade an action to a collection * Cascade an action to a collection
*/ */
private void cascadeCollection( private void cascadeCollection(
final Object parent,
final Object child, final Object child,
final CascadeStyle style, final CascadeStyle style,
final Object anything, final Object anything,
@ -264,6 +271,7 @@ public final class Cascade {
//cascade to current collection elements //cascade to current collection elements
if ( elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType() ) { if ( elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType() ) {
cascadeCollectionElements( cascadeCollectionElements(
parent,
child, child,
type, type,
style, style,
@ -280,6 +288,7 @@ public final class Cascade {
* Cascade an action to a to-one association or any type * Cascade an action to a to-one association or any type
*/ */
private void cascadeToOne( private void cascadeToOne(
final Object parent,
final Object child, final Object child,
final Type type, final Type type,
final CascadeStyle style, final CascadeStyle style,
@ -289,14 +298,21 @@ public final class Cascade {
? ( (EntityType) type ).getAssociatedEntityName() ? ( (EntityType) type ).getAssociatedEntityName()
: null; : null;
if ( style.reallyDoCascade(action) ) { //not really necessary, but good for consistency... if ( style.reallyDoCascade(action) ) { //not really necessary, but good for consistency...
eventSource.getPersistenceContext().addChildParent(child, parent);
try {
action.cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled); action.cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled);
} }
finally {
eventSource.getPersistenceContext().removeChildParent(child);
}
}
} }
/** /**
* Cascade to the collection elements * Cascade to the collection elements
*/ */
private void cascadeCollectionElements( private void cascadeCollectionElements(
final Object parent,
final Object child, final Object child,
final CollectionType collectionType, final CollectionType collectionType,
final CascadeStyle style, final CascadeStyle style,
@ -318,6 +334,7 @@ public final class Cascade {
Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType, child); Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType, child);
while ( iter.hasNext() ) { while ( iter.hasNext() ) {
cascadeProperty( cascadeProperty(
parent,
iter.next(), iter.next(),
elemType, elemType,
style, style,

View File

@ -482,4 +482,17 @@ public interface PersistenceContext {
public void setReadOnly(Object entity, boolean readOnly); public void setReadOnly(Object entity, boolean readOnly);
void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId); void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId);
/**
* Put child/parent relation to cache for cascading op
* @param parent
* @param child
*/
public void addChildParent(Object parent, Object child);
/**
* Remove child/parent relation from cache
* @param parent
*/
public void removeChildParent(Object child);
} }

View File

@ -119,6 +119,10 @@ public class StatefulPersistenceContext implements PersistenceContext {
// yet loaded ... for now, this is purely transient! // yet loaded ... for now, this is purely transient!
private Map unownedCollections; private Map unownedCollections;
// Parent entities cache by their child for cascading
// May be empty or not contains all relation
private Map parentsByChild;
private int cascading = 0; private int cascading = 0;
private int loadCounter = 0; private int loadCounter = 0;
private boolean flushing = false; private boolean flushing = false;
@ -147,6 +151,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
collectionsByKey = new HashMap( INIT_COLL_SIZE ); collectionsByKey = new HashMap( INIT_COLL_SIZE );
arrayHolders = IdentityMap.instantiate( INIT_COLL_SIZE ); arrayHolders = IdentityMap.instantiate( INIT_COLL_SIZE );
parentsByChild = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
nullifiableEntityKeys = new HashSet(); nullifiableEntityKeys = new HashSet();
@ -214,6 +219,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
entitiesByKey.clear(); entitiesByKey.clear();
entitiesByUniqueKey.clear(); entitiesByUniqueKey.clear();
entityEntries.clear(); entityEntries.clear();
parentsByChild.clear();
entitySnapshotsByKey.clear(); entitySnapshotsByKey.clear();
collectionsByKey.clear(); collectionsByKey.clear();
collectionEntries.clear(); collectionEntries.clear();
@ -360,6 +366,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
while ( iter.hasNext() ) { while ( iter.hasNext() ) {
if ( iter.next()==entity ) iter.remove(); if ( iter.next()==entity ) iter.remove();
} }
// Clear all parent cache
parentsByChild.clear();
entitySnapshotsByKey.remove(key); entitySnapshotsByKey.remove(key);
nullifiableEntityKeys.remove(key); nullifiableEntityKeys.remove(key);
getBatchFetchQueue().removeBatchLoadableEntityKey(key); getBatchFetchQueue().removeBatchLoadableEntityKey(key);
@ -1104,8 +1112,18 @@ public class StatefulPersistenceContext implements PersistenceContext {
final EntityPersister persister = session.getFactory().getEntityPersister( entityName ); final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
final CollectionPersister collectionPersister = session.getFactory().getCollectionPersister( collectionRole ); final CollectionPersister collectionPersister = session.getFactory().getCollectionPersister( collectionRole );
// try cache lookup first
Object parent = parentsByChild.get(childEntity);
if (parent != null) {
if (isFoundInParent(propertyName, childEntity, persister, collectionPersister, parent)) {
return getEntry(parent).getId();
}
else {
parentsByChild.remove(childEntity); // remove wrong entry
}
}
// iterate all the entities currently associated with the persistence context. // iterate all the entities currently associated with the persistence context.
Iterator entities = entityEntries.entrySet().iterator(); Iterator entities = IdentityMap.entries(entityEntries).iterator();
while ( entities.hasNext() ) { while ( entities.hasNext() ) {
final Map.Entry me = ( Map.Entry ) entities.next(); final Map.Entry me = ( Map.Entry ) entities.next();
final EntityEntry entityEntry = ( EntityEntry ) me.getValue(); final EntityEntry entityEntry = ( EntityEntry ) me.getValue();
@ -1207,7 +1225,26 @@ public class StatefulPersistenceContext implements PersistenceContext {
.getEntityPersister(entity); .getEntityPersister(entity);
CollectionPersister cp = session.getFactory() CollectionPersister cp = session.getFactory()
.getCollectionPersister(entity + '.' + property); .getCollectionPersister(entity + '.' + property);
Iterator entities = entityEntries.entrySet().iterator();
// try cache lookup first
Object parent = parentsByChild.get(childEntity);
if (parent != null) {
Object index = getIndexInParent(property, childEntity, persister, cp, parent);
if (index==null && mergeMap!=null) {
Object unmergedInstance = mergeMap.get(parent);
Object unmergedChild = mergeMap.get(childEntity);
if ( unmergedInstance!=null && unmergedChild!=null ) {
index = getIndexInParent(property, unmergedChild, persister, cp, unmergedInstance);
}
}
if (index!=null) {
return index;
}
parentsByChild.remove(childEntity); // remove wrong entry
}
Iterator entities = IdentityMap.entries(entityEntries).iterator();
while ( entities.hasNext() ) { while ( entities.hasNext() ) {
Map.Entry me = (Map.Entry) entities.next(); Map.Entry me = (Map.Entry) entities.next();
EntityEntry ee = (EntityEntry) me.getValue(); EntityEntry ee = (EntityEntry) me.getValue();
@ -1277,6 +1314,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) { public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) {
Object entity = entitiesByKey.remove( oldKey ); Object entity = entitiesByKey.remove( oldKey );
EntityEntry oldEntry = ( EntityEntry ) entityEntries.remove( entity ); EntityEntry oldEntry = ( EntityEntry ) entityEntries.remove( entity );
parentsByChild.clear();
EntityKey newKey = new EntityKey( generatedId, oldEntry.getPersister(), getSession().getEntityMode() ); EntityKey newKey = new EntityKey( generatedId, oldEntry.getPersister(), getSession().getEntityMode() );
addEntity( newKey, entity ); addEntity( newKey, entity );
@ -1487,4 +1525,18 @@ public class StatefulPersistenceContext implements PersistenceContext {
return rtn; return rtn;
} }
/**
* @see org.hibernate.engine.PersistenceContext#addChildParent(java.lang.Object, java.lang.Object)
*/
public void addChildParent(Object child, Object parent) {
parentsByChild.put(child, parent);
}
/**
* @see org.hibernate.engine.PersistenceContext#removeChildParent(java.lang.Object)
*/
public void removeChildParent(Object child) {
parentsByChild.remove(child);
}
} }