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:
parent
48c2ec7e91
commit
1005f19b1a
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue