[hibernate-commits] Hibernate SVN: r16997 - in core/branches/Branch_3_2: src/org/hibernate/loader/collection and 3 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Sun Jul 5 23:01:31 EDT 2009


Author: steve.ebersole at jboss.com
Date: 2009-07-05 23:01:30 -0400 (Sun, 05 Jul 2009)
New Revision: 16997

Added:
   core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/
   core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java
   core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/Group.java
   core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/User.java
   core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml
Modified:
   core/branches/Branch_3_2/src/org/hibernate/loader/JoinWalker.java
   core/branches/Branch_3_2/src/org/hibernate/loader/collection/BatchingCollectionInitializer.java
   core/branches/Branch_3_2/src/org/hibernate/persister/collection/AbstractCollectionPersister.java
Log:
HHH-4003 - many-to-many loaders should use inner joins from the collection table to element table


Modified: core/branches/Branch_3_2/src/org/hibernate/loader/JoinWalker.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/loader/JoinWalker.java	2009-07-06 03:00:36 UTC (rev 16996)
+++ core/branches/Branch_3_2/src/org/hibernate/loader/JoinWalker.java	2009-07-06 03:01:30 UTC (rev 16997)
@@ -579,11 +579,14 @@
 	 * is the "first" join in a series
 	 */
 	protected int getJoinType(boolean nullable, int currentDepth) {
-		//TODO: this is too conservative; if all preceding joins were 
+		//TODO: this is too conservative; if all preceding joins were
 		//      also inner joins, we could use an inner join here
-		return !nullable && currentDepth==0 ? 
-					JoinFragment.INNER_JOIN : 
-					JoinFragment.LEFT_OUTER_JOIN;
+		//
+		// IMPL NOTE : currentDepth might be less-than zero if this is the
+		// 		root of a many-to-many collection initializer
+		return !nullable && currentDepth <= 0
+				? JoinFragment.INNER_JOIN
+				: JoinFragment.LEFT_OUTER_JOIN;
 	}
 
 	protected boolean isTooDeep(int currentDepth) {

Modified: core/branches/Branch_3_2/src/org/hibernate/loader/collection/BatchingCollectionInitializer.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/loader/collection/BatchingCollectionInitializer.java	2009-07-06 03:00:36 UTC (rev 16996)
+++ core/branches/Branch_3_2/src/org/hibernate/loader/collection/BatchingCollectionInitializer.java	2009-07-06 03:01:30 UTC (rev 16997)
@@ -22,7 +22,6 @@
  * @author Gavin King
  */
 public class BatchingCollectionInitializer implements CollectionInitializer {
-
 	private final Loader[] loaders;
 	private final int[] batchSizes;
 	private final CollectionPersister collectionPersister;
@@ -33,6 +32,18 @@
 		this.collectionPersister = collPersister;
 	}
 
+	public CollectionPersister getCollectionPersister() {
+		return collectionPersister;
+	}
+
+	public Loader[] getLoaders() {
+		return loaders;
+	}
+
+	public int[] getBatchSizes() {
+		return batchSizes;
+	}
+
 	public void initialize(Serializable id, SessionImplementor session)
 	throws HibernateException {
 		

Modified: core/branches/Branch_3_2/src/org/hibernate/persister/collection/AbstractCollectionPersister.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/persister/collection/AbstractCollectionPersister.java	2009-07-06 03:00:36 UTC (rev 16996)
+++ core/branches/Branch_3_2/src/org/hibernate/persister/collection/AbstractCollectionPersister.java	2009-07-06 03:01:30 UTC (rev 16997)
@@ -1782,4 +1782,14 @@
 	protected Dialect getDialect() {
 		return dialect;
 	}
+
+	/**
+	 * Intended for internal use only.  In fact really only currently used from
+	 * test suite for assertion purposes.
+	 *
+	 * @return The default collection initializer for this persister/collection.
+	 */
+	public CollectionInitializer getInitializer() {
+		return initializer;
+	}
 }

Added: core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java	                        (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java	2009-07-06 03:01:30 UTC (rev 16997)
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.manytomany.batchload;
+
+import java.util.List;
+import java.util.Iterator;
+
+import junit.framework.Test;
+import junit.framework.Assert;
+
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.Session;
+import org.hibernate.Hibernate;
+import org.hibernate.Interceptor;
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.jdbc.BatcherFactory;
+import org.hibernate.jdbc.NonBatchingBatcher;
+import org.hibernate.jdbc.Batcher;
+import org.hibernate.jdbc.ConnectionManager;
+import org.hibernate.stat.CollectionStatistics;
+import org.hibernate.loader.collection.BatchingCollectionInitializer;
+import org.hibernate.persister.collection.AbstractCollectionPersister;
+
+/**
+ * Tests loading of many-to-many collection which should trigger
+ * a batch load.
+ *
+ * @author Steve Ebersole
+ */
+public class BatchedManyToManyTest extends FunctionalTestCase {
+	public BatchedManyToManyTest(String string) {
+		super( string );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( BatchedManyToManyTest.class );
+	}
+
+	public String[] getMappings() {
+		return new String[] { "manytomany/batchload/UserGroupBatchLoad.hbm.xml" };
+	}
+
+	public void configure(Configuration cfg) {
+		cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" );
+		cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+		cfg.setProperty( Environment.BATCH_STRATEGY, TestingBatcherFactory.class.getName() );
+	}
+
+	public static class TestingBatcherFactory implements BatcherFactory {
+		public Batcher createBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+			return new TestingBatcher( connectionManager, interceptor );
+		}
+	}
+
+	public static class TestingBatcher extends NonBatchingBatcher {
+		public TestingBatcher(ConnectionManager connectionManager, Interceptor interceptor) {
+			super( connectionManager, interceptor );
+		}
+
+	}
+
+	public void testProperLoaderSetup() {
+		AbstractCollectionPersister cp = ( AbstractCollectionPersister )
+				sfi().getCollectionPersister( User.class.getName() + ".groups" );
+		assertClassAssignability( BatchingCollectionInitializer.class, cp.getInitializer().getClass() );
+		BatchingCollectionInitializer initializer = ( BatchingCollectionInitializer ) cp.getInitializer();
+		assertEquals( 50, findMaxBatchSize( initializer.getBatchSizes() ) );
+	}
+
+	private int findMaxBatchSize(int[] batchSizes) {
+		int max = 0;
+		for ( int i = 0; i < batchSizes.length; i++ ) {
+			int size = batchSizes[i];
+			if ( size > max ) {
+				max = size;
+			}
+		}
+		return max;
+	}
+
+	public void testLoadingNonInverseSide() {
+		prepareTestData();
+
+		sfi().getStatistics().clear();
+		CollectionStatistics userGroupStats = sfi().getStatistics()
+				.getCollectionStatistics( User.class.getName() + ".groups" );
+		CollectionStatistics groupUserStats = sfi().getStatistics()
+				.getCollectionStatistics( Group.class.getName() + ".users" );
+
+		Interceptor testingInterceptor = new EmptyInterceptor() {
+			public String onPrepareStatement(String sql) {
+				// ugh, this is the best way I could come up with to assert this.
+				// unfortunately, this is highly dependent on the dialect and its
+				// outer join fragment.  But at least this wil fail on the majority
+				// of dialects...
+				Assert.assertFalse(
+						"batch load of many-to-many should use inner join",
+						sql.toLowerCase().contains( "left outer join" )
+				);
+				return super.onPrepareStatement( sql );
+			}
+		};
+
+		Session s = openSession( testingInterceptor );
+		s.beginTransaction();
+		List users = s.createQuery( "from User u" ).list();
+		User user = ( User ) users.get( 0 );
+		assertTrue( Hibernate.isInitialized( user ) );
+		assertTrue( Hibernate.isInitialized( user.getGroups() ) );
+		user = ( User ) users.get( 1 );
+		assertTrue( Hibernate.isInitialized( user ) );
+		assertTrue( Hibernate.isInitialized( user.getGroups() ) );
+		assertEquals( 1, userGroupStats.getFetchCount() ); // should have been just one fetch (the batch fetch)
+		assertEquals( 1, groupUserStats.getFetchCount() ); // should have been just one fetch (the batch fetch)
+		s.getTransaction().commit();
+		s.close();
+
+		cleanupTestData();
+	}
+
+	protected void prepareTestData() {
+		// set up the test data
+		User me = new User( "steve" );
+		User you = new User( "not steve" );
+		Group developers = new Group( "developers" );
+		Group translators = new Group( "translators" );
+		Group contributors = new Group( "contributors" );
+		me.getGroups().add( developers );
+		developers.getUsers().add( me );
+		you.getGroups().add( translators );
+		translators.getUsers().add( you );
+		you.getGroups().add( contributors );
+		contributors.getUsers().add( you );
+		Session s = openSession();
+		s.beginTransaction();
+		s.save( me );
+		s.save( you );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	protected void cleanupTestData() {
+		// clean up the test data
+		Session s = openSession();
+		s.beginTransaction();
+		// User is the non-inverse side...
+		List users = s.createQuery( "from User" ).list();
+		Iterator itr = users.iterator();
+		while ( itr.hasNext() ) {
+			User user = ( User ) itr.next();
+			s.delete( user );
+		}
+		s.flush();
+		s.createQuery( "delete Group" ).executeUpdate();
+		s.getTransaction().commit();
+		s.close();
+	}
+}

Added: core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/Group.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/Group.java	                        (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/Group.java	2009-07-06 03:01:30 UTC (rev 16997)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.manytomany.batchload;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class Group {
+	private Long id;
+	private String name;
+	private Set users = new HashSet();
+
+	public Group() {
+	}
+
+	public Group(String name) {
+		this.name = name;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Set getUsers() {
+		return users;
+	}
+
+	public void setUsers(Set users) {
+		this.users = users;
+	}
+}

Added: core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/User.java
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/User.java	                        (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/User.java	2009-07-06 03:01:30 UTC (rev 16997)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.test.manytomany.batchload;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class User {
+	private Long id;
+	private String name;
+	private Set groups = new HashSet();
+
+	public User() {
+	}
+
+	public User(String name) {
+		this.name = name;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Set getGroups() {
+		return groups;
+	}
+
+	public void setGroups(Set groups) {
+		this.groups = groups;
+	}
+}

Added: core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml
===================================================================
--- core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml	                        (rev 0)
+++ core/branches/Branch_3_2/test/org/hibernate/test/manytomany/batchload/UserGroupBatchLoad.hbm.xml	2009-07-06 03:01:30 UTC (rev 16997)
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
+  ~ indicated by the @author tags or express copyright attribution
+  ~ statements applied by the authors.  All third-party contributions are
+  ~ distributed under license by Red Hat Middleware LLC.
+  ~
+  ~ This copyrighted material is made available to anyone wishing to use, modify,
+  ~ copy, or redistribute it subject to the terms and conditions of the GNU
+  ~ Lesser General Public License, as published by the Free Software Foundation.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+  ~ for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public License
+  ~ along with this distribution; if not, write to:
+  ~ Free Software Foundation, Inc.
+  ~ 51 Franklin Street, Fifth Floor
+  ~ Boston, MA  02110-1301  USA
+  -->
+<!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.manytomany.batchload">
+	
+	<class name="User" table="M2N_BATCHED_USER">
+        <id name="id" type="long">
+            <generator class="increment"/>
+        </id>
+        <property name="name" type="string"/>
+		<set name="groups" table="M2N_BATCHED_GROUPUSER" inverse="false" cascade="all" lazy="false" batch-size="50" >
+            <key column="USER_ID"/>
+			<many-to-many class="Group" column="GROUP_ID"/>
+		</set>
+	</class>
+	
+	<class name="Group" table="M2N_BATCHED_GROUP">
+        <id name="id" type="long">
+            <generator class="increment"/>
+        </id>
+		<property name="name" type="string"/>
+		<set name="users" table="M2N_BATCHED_GROUPUSER" inverse="true" cascade="all" lazy="false" batch-size="50">
+            <key column="GROUP_ID"/>
+			<many-to-many class="User" column="USER_ID"/>
+		</set>
+	</class>
+	
+</hibernate-mapping>




More information about the hibernate-commits mailing list