Author: stliu
Date: 2010-07-07 09:08:31 -0400 (Wed, 07 Jul 2010)
New Revision: 19906
Added:
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.hbm.xml
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.java
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/A.java
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/B.java
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/C.java
Modified:
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/Cascade.java
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/PersistenceContext.java
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
Log:
JBPAPP-4562 HHH-3860 Cascading performance problems when session contains many entities
Modified:
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/Cascade.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/Cascade.java 2010-07-06
19:35:13 UTC (rev 19905)
+++
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/Cascade.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -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,9 @@
? ( (EntityType) type ).getAssociatedEntityName()
: null;
if ( style.reallyDoCascade(action) ) { //not really necessary, but good for
consistency...
+ eventSource.getPersistenceContext().addChildParent(child, parent);
action.cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled);
+ eventSource.getPersistenceContext().removeChildParent(child);
}
}
@@ -297,6 +308,7 @@
* Cascade to the collection elements
*/
private void cascadeCollectionElements(
+ final Object parent,
final Object child,
final CollectionType collectionType,
final CascadeStyle style,
@@ -318,6 +330,7 @@
Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType,
child);
while ( iter.hasNext() ) {
cascadeProperty(
+ parent,
iter.next(),
elemType,
style,
Modified:
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/PersistenceContext.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/PersistenceContext.java 2010-07-06
19:35:13 UTC (rev 19905)
+++
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/PersistenceContext.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -486,4 +486,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 child
+ */
+ public void removeChildParent(Object child);
}
Modified:
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java 2010-07-06
19:35:13 UTC (rev 19905)
+++
core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -118,7 +118,9 @@
// A container for collections we load up when the owning entity is not
// 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 +149,7 @@
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();
@@ -210,6 +212,7 @@
entitiesByKey.clear();
entitiesByUniqueKey.clear();
entityEntries.clear();
+ parentsByChild.clear();
entitySnapshotsByKey.clear();
collectionsByKey.clear();
collectionEntries.clear();
@@ -356,6 +359,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);
@@ -1093,9 +1098,18 @@
final String collectionRole = entityName + '.' + propertyName;
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();
@@ -1197,7 +1211,27 @@
.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();
@@ -1267,6 +1301,8 @@
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 );
@@ -1477,4 +1513,19 @@
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);
+ }
}
Added:
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.hbm.xml
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.hbm.xml
(rev 0)
+++
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.hbm.xml 2010-07-07
13:08:31 UTC (rev 19906)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping package="org.hibernate.test.perf.object">
+ <class name="A" table="A">
+ <id name="id" type="long" access="field">
+ <generator class="hilo">
+ <param name="max_lo">10000</param>
+ </generator>
+ </id>
+ <property name="a" type="integer"
access="field"/>
+ <map name="map" access="field" cascade="all">
+ <key column="a" not-null="true"/>
+ <map-key column="keycol" type="integer"/>
+ <one-to-many class="B"/>
+ </map>
+ </class>
+ <class name="B" table="B">
+ <id name="id" type="long" access="field">
+ <generator class="hilo">
+ <param name="max_lo">10000</param>
+ </generator>
+ </id>
+ <property name="b" type="integer"
access="field"/>
+ <list name="list" access="field" cascade="all">
+ <key column="keycol" not-null="true"/>
+ <list-index column="pos"/>
+ <one-to-many class="C"/>
+ </list>
+ </class>
+ <class name="C" table="C">
+ <id name="id" type="long" access="field">
+ <generator class="hilo">
+ <param name="max_lo">10000</param>
+ </generator>
+ </id>
+ <property name="c" type="integer"
access="field"/>
+ </class>
+</hibernate-mapping>
\ No newline at end of file
Added:
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.java
(rev 0)
+++
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/CascadingTest.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -0,0 +1,65 @@
+package org.hibernate.test.perf;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.test.perf.object.A;
+import org.hibernate.test.perf.object.B;
+import org.hibernate.test.perf.object.C;
+
+public class CascadingTest extends FunctionalTestCase {
+
+ public CascadingTest(String string) {
+ super(string);
+ }
+
+ public String[] getMappings() {
+ return new String[] { "perf/CascadingTest.hbm.xml" };
+ }
+
+ /**
+ * Make 5510 objects on database
+ */
+ public void testSaveSession() {
+ Session session = openSession();
+ Transaction tx = session.beginTransaction();
+ long timeTotal = System.currentTimeMillis();
+ for (int x = 0; x < 10; x++) {
+ makeObjects(session);
+ }
+ session.flush();
+ tx.commit();
+ session.close();
+ System.out.println("Total took " + ((System.currentTimeMillis() -
timeTotal)) + " ms");
+ }
+
+
+ /**
+ * Make 551 new objects
+ *
+ * @param session
+ */
+ private void makeObjects(Session session) {
+ A a = new A();
+ a.setA(-1);
+ a.setMap(new HashMap());
+ for (int i = 0; i < 50; i++) {
+ B b = new B();
+ b.setB(i);
+ b.setList(new ArrayList());
+ a.getMap().put(new Integer(i), b);
+ for (int j = 0; j < 10; j++) {
+ C c = new C();
+ c.setC(j * 1000000);
+ b.getList().add(c);
+ }
+ }
+ long time = System.currentTimeMillis();
+ session.save(a);
+ System.out.println("Save took " + ((System.currentTimeMillis() - time))
+ " ms");
+ time = System.currentTimeMillis();
+ }
+}
Added:
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/A.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/A.java
(rev 0)
+++
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/A.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -0,0 +1,32 @@
+package org.hibernate.test.perf.object;
+
+import java.util.Map;
+
+public class A {
+ protected long id;
+
+ protected int a;
+
+ protected Map map;
+
+ public int getA() {
+ return a;
+ }
+
+ public void setA(int a) {
+ this.a = a;
+ }
+
+ public Map getMap() {
+ return map;
+ }
+
+ public void setMap(Map map) {
+ this.map = map;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+}
Added:
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/B.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/B.java
(rev 0)
+++
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/B.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -0,0 +1,32 @@
+package org.hibernate.test.perf.object;
+
+import java.util.List;
+
+public class B {
+ protected long id;
+
+ protected int b;
+
+ protected List list;
+
+ public int getB() {
+ return b;
+ }
+
+ public void setB(int b) {
+ this.b = b;
+ }
+
+ public List getList() {
+ return list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+}
Added:
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/C.java
===================================================================
---
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/C.java
(rev 0)
+++
core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/perf/org/hibernate/test/perf/object/C.java 2010-07-07
13:08:31 UTC (rev 19906)
@@ -0,0 +1,19 @@
+package org.hibernate.test.perf.object;
+
+public class C {
+ protected long id;
+
+ protected int c;
+
+ public int getC() {
+ return c;
+ }
+
+ public void setC(int c) {
+ this.c = c;
+ }
+
+ public long getId() {
+ return id;
+ }
+}