Author: steve.ebersole(a)jboss.com
Date: 2009-12-08 13:55:27 -0500 (Tue, 08 Dec 2009)
New Revision: 18167
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/Cascade.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/PersistenceContext.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
Log:
HHH-3860 - Cascading performance problems when session contains many entities
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/Cascade.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/Cascade.java 2009-12-08
16:32:59 UTC (rev 18166)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/Cascade.java 2009-12-08
18:55:27 UTC (rev 18167)
@@ -152,6 +152,7 @@
if ( style.doCascade( action ) ) {
cascadeProperty(
+ parent,
persister.getPropertyValue( parent, i, entityMode ),
types[i],
style,
@@ -180,6 +181,7 @@
* 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 @@
AssociationType associationType = (AssociationType) type;
if ( cascadeAssociationNow( associationType ) ) {
cascadeAssociation(
+ parent,
child,
type,
style,
@@ -200,7 +203,7 @@
}
}
else if ( type.isComponentType() ) {
- cascadeComponent( child, (AbstractComponentType) type, anything );
+ cascadeComponent( parent, child, (AbstractComponentType) type, anything );
}
}
}
@@ -211,6 +214,7 @@
}
private void cascadeComponent(
+ final Object parent,
final Object child,
final AbstractComponentType componentType,
final Object anything) {
@@ -220,6 +224,7 @@
CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i);
if ( componentPropertyStyle.doCascade(action) ) {
cascadeProperty(
+ parent,
children[i],
types[i],
componentPropertyStyle,
@@ -231,16 +236,17 @@
}
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 @@
* 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 @@
//cascade to current collection elements
if ( elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType() ) {
cascadeCollectionElements(
+ parent,
child,
type,
style,
@@ -280,6 +288,7 @@
* 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 @@
? ( (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 @@
* Cascade to the collection elements
*/
private void cascadeCollectionElements(
+ final Object parent,
final Object child,
final CollectionType collectionType,
final CascadeStyle style,
@@ -318,6 +334,7 @@
Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType,
child);
while ( iter.hasNext() ) {
cascadeProperty(
+ parent,
iter.next(),
elemType,
style,
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/PersistenceContext.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/PersistenceContext.java 2009-12-08
16:32:59 UTC (rev 18166)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/PersistenceContext.java 2009-12-08
18:55:27 UTC (rev 18167)
@@ -482,4 +482,17 @@
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);
}
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
===================================================================
---
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java 2009-12-08
16:32:59 UTC (rev 18166)
+++
core/branches/Branch_3_3/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java 2009-12-08
18:55:27 UTC (rev 18167)
@@ -119,6 +119,10 @@
// 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 @@
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 @@
entitiesByKey.clear();
entitiesByUniqueKey.clear();
entityEntries.clear();
+ parentsByChild.clear();
entitySnapshotsByKey.clear();
collectionsByKey.clear();
collectionEntries.clear();
@@ -360,6 +366,8 @@
while ( iter.hasNext() ) {
if ( iter.next()==entity ) iter.remove();
}
+ // Clear all parent cache
+ parentsByChild.clear();
entitySnapshotsByKey.remove(key);
nullifiableEntityKeys.remove(key);
getBatchFetchQueue().removeBatchLoadableEntityKey(key);
@@ -1100,8 +1108,18 @@
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();
@@ -1203,7 +1221,26 @@
.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();
@@ -1273,6 +1310,7 @@
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 );
@@ -1483,4 +1521,18 @@
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);
+ }
}