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

View File

@ -482,4 +482,17 @@ public interface PersistenceContext {
public void setReadOnly(Object entity, boolean readOnly);
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!
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 loadCounter = 0;
private boolean flushing = false;
@ -147,7 +151,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
collectionsByKey = new HashMap( INIT_COLL_SIZE );
arrayHolders = IdentityMap.instantiate( INIT_COLL_SIZE );
parentsByChild = IdentityMap.instantiateSequenced( INIT_COLL_SIZE );
nullifiableEntityKeys = new HashSet();
initTransientState();
@ -214,6 +219,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
entitiesByKey.clear();
entitiesByUniqueKey.clear();
entityEntries.clear();
parentsByChild.clear();
entitySnapshotsByKey.clear();
collectionsByKey.clear();
collectionEntries.clear();
@ -360,6 +366,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
while ( iter.hasNext() ) {
if ( iter.next()==entity ) iter.remove();
}
// Clear all parent cache
parentsByChild.clear();
entitySnapshotsByKey.remove(key);
nullifiableEntityKeys.remove(key);
getBatchFetchQueue().removeBatchLoadableEntityKey(key);
@ -1104,8 +1112,18 @@ public class StatefulPersistenceContext implements PersistenceContext {
final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
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.
Iterator entities = entityEntries.entrySet().iterator();
Iterator entities = IdentityMap.entries(entityEntries).iterator();
while ( entities.hasNext() ) {
final Map.Entry me = ( Map.Entry ) entities.next();
final EntityEntry entityEntry = ( EntityEntry ) me.getValue();
@ -1207,7 +1225,26 @@ public class StatefulPersistenceContext implements PersistenceContext {
.getEntityPersister(entity);
CollectionPersister cp = session.getFactory()
.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() ) {
Map.Entry me = (Map.Entry) entities.next();
EntityEntry ee = (EntityEntry) me.getValue();
@ -1277,6 +1314,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) {
Object entity = entitiesByKey.remove( oldKey );
EntityEntry oldEntry = ( EntityEntry ) entityEntries.remove( entity );
parentsByChild.clear();
EntityKey newKey = new EntityKey( generatedId, oldEntry.getPersister(), getSession().getEntityMode() );
addEntity( newKey, entity );
@ -1487,4 +1525,18 @@ public class StatefulPersistenceContext implements PersistenceContext {
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);
}
}