[hibernate-commits] Hibernate SVN: r19470 - in core/trunk: core/src/main/java/org/hibernate/id/enhanced and 2 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue May 11 15:27:21 EDT 2010


Author: steve.ebersole at jboss.com
Date: 2010-05-11 15:27:20 -0400 (Tue, 11 May 2010)
New Revision: 19470

Added:
   core/trunk/core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java
   core/trunk/core/src/main/java/org/hibernate/id/SequenceGenerator.java
   core/trunk/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java
   core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
   core/trunk/parent/pom.xml
Log:
HHH-5042 - TableGenerator does not increment hibernate_sequences.next_hi_value anymore after having exhausted the current lo-range


Modified: core/trunk/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java	2010-05-11 19:24:54 UTC (rev 19469)
+++ core/trunk/core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java	2010-05-11 19:27:20 UTC (rev 19470)
@@ -37,6 +37,8 @@
 import org.hibernate.LockMode;
 import org.hibernate.MappingException;
 import org.hibernate.cfg.ObjectNameNormalizer;
+import org.hibernate.id.enhanced.AccessCallback;
+import org.hibernate.id.enhanced.OptimizerFactory;
 import org.hibernate.jdbc.util.FormatStyle;
 import org.hibernate.dialect.Dialect;
 import org.hibernate.engine.SessionImplementor;
@@ -104,8 +106,7 @@
 	public static final String MAX_LO = "max_lo";
 
 	private int maxLo;
-	private int lo;
-	private IntegralDataTypeHolder value;
+	private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer;
 
 	private Class returnClass;
 	private int keySize;
@@ -149,19 +150,15 @@
 		IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );
 		int rows;
 		do {
-			// The loop ensures atomicity of the
-			// select + update even for no transaction
-			// or read committed isolation level
-
-			//sql = query;
-			SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
-			PreparedStatement qps = conn.prepareStatement(query);
+			SQL_STATEMENT_LOGGER.logStatement( query, FormatStyle.BASIC );
+			PreparedStatement qps = conn.prepareStatement( query );
 			PreparedStatement ips = null;
 			try {
 				ResultSet rs = qps.executeQuery();
 				boolean isInitialized = rs.next();
 				if ( !isInitialized ) {
 					value.initialize( 0 );
+					SQL_STATEMENT_LOGGER.logStatement( insert, FormatStyle.BASIC );
 					ips = conn.prepareStatement( insert );
 					value.bind( ips, 1 );
 					ips.execute();
@@ -182,7 +179,8 @@
 				qps.close();
 			}
 
-			PreparedStatement ups = conn.prepareStatement(update);
+			SQL_STATEMENT_LOGGER.logStatement( update, FormatStyle.BASIC );
+			PreparedStatement ups = conn.prepareStatement( update );
 			try {
 				value.copy().increment().bind( ups, 1 );
 				value.bind( ups, 2 );
@@ -195,12 +193,12 @@
 			finally {
 				ups.close();
 			}
-		}
-		while (rows==0);
+		} while ( rows==0 );
+
 		return value;
 	}
 
-	public synchronized Serializable generate(SessionImplementor session, Object obj)
+	public synchronized Serializable generate(final SessionImplementor session, Object obj)
 		throws HibernateException {
 		// maxLo < 1 indicates a hilo generator with no hilo :?
 		if ( maxLo < 1 ) {
@@ -212,15 +210,13 @@
 			return value.makeValue();
 		}
 
-		if ( lo > maxLo ) {
-			IntegralDataTypeHolder hiVal = (IntegralDataTypeHolder) doWorkInNewTransaction( session );
-			lo = ( hiVal.eq( 0 ) ) ? 1 : 0;
-			value = hiVal.copy().multiplyBy( maxLo+1 ).add( lo );
-			if ( log.isDebugEnabled() ) {
-				log.debug("new hi value: " + hiVal);
-			}
-		}
-		return value.makeValueThenIncrement();
+		return hiloOptimizer.generate(
+				new AccessCallback() {
+					public IntegralDataTypeHolder getNextValue() {
+						return (IntegralDataTypeHolder) doWorkInNewTransaction( session );
+					}
+				}
+		);
 	}
 
 	public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
@@ -281,7 +277,8 @@
 
 		//hilo config
 		maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
-		lo = maxLo + 1; // so we "clock over" on the first invocation
 		returnClass = type.getReturnedClass();
+
+		hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( returnClass, maxLo );
 	}
 }

Modified: core/trunk/core/src/main/java/org/hibernate/id/SequenceGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/SequenceGenerator.java	2010-05-11 19:24:54 UTC (rev 19469)
+++ core/trunk/core/src/main/java/org/hibernate/id/SequenceGenerator.java	2010-05-11 19:27:20 UTC (rev 19470)
@@ -72,6 +72,10 @@
 	private Type identifierType;
 	private String sql;
 
+	protected Type getIdentifierType() {
+		return identifierType;
+	}
+
 	public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
 		ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
 		sequenceName = normalizer.normalizeIdentifierQuoting(
@@ -103,7 +107,7 @@
 
 	protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
 		try {
-			PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
+			PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
 			try {
 				ResultSet rs = st.executeQuery();
 				try {

Modified: core/trunk/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java	2010-05-11 19:24:54 UTC (rev 19469)
+++ core/trunk/core/src/main/java/org/hibernate/id/SequenceHiLoGenerator.java	2010-05-11 19:27:20 UTC (rev 19470)
@@ -26,11 +26,11 @@
 import java.io.Serializable;
 import java.util.Properties;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.hibernate.MappingException;
 import org.hibernate.dialect.Dialect;
 import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.enhanced.AccessCallback;
+import org.hibernate.id.enhanced.OptimizerFactory;
 import org.hibernate.type.Type;
 import org.hibernate.util.PropertiesHelper;
 
@@ -50,23 +50,24 @@
  * @author Gavin King
  */
 public class SequenceHiLoGenerator extends SequenceGenerator {
-
 	public static final String MAX_LO = "max_lo";
 
-	private static final Logger log = LoggerFactory.getLogger(SequenceHiLoGenerator.class);
-
 	private int maxLo;
-	private int lo;
 
-	private IntegralDataTypeHolder value;
+	private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer;
 
 	public void configure(Type type, Properties params, Dialect d) throws MappingException {
 		super.configure(type, params, d);
-		maxLo = PropertiesHelper.getInt(MAX_LO, params, 9);
-		lo = maxLo + 1; // so we "clock over" on the first invocation
+
+		maxLo = PropertiesHelper.getInt( MAX_LO, params, 9 );
+
+		hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer(
+				getIdentifierType().getReturnedClass(),
+				maxLo
+		);
 	}
 
-	public synchronized Serializable generate(SessionImplementor session, Object obj) {
+	public synchronized Serializable generate(final SessionImplementor session, Object obj) {
 		// maxLo < 1 indicates a hilo generator with no hilo :?
 		if ( maxLo < 1 ) {
 			//keep the behavior consistent even for boundary usages
@@ -77,16 +78,21 @@
 			return value.makeValue();
 		}
 
-		if ( lo > maxLo ) {
-			IntegralDataTypeHolder hiVal = generateHolder( session );
-			lo = ( hiVal.eq( 0 ) ) ? 1 : 0;
-			value = hiVal.copy().multiplyBy( maxLo+1 ).add( lo );
-			if ( log.isDebugEnabled() ) {
-				log.debug("new hi value: " + hiVal);
-			}
-		}
+		return hiloOptimizer.generate(
+				new AccessCallback() {
+					public IntegralDataTypeHolder getNextValue() {
+						return generateHolder( session );
+					}
+				}
+		);
+	}
 
-		return value.makeValueThenIncrement();
+	/**
+	 * For testing/assertion purposes
+	 *
+	 * @return The optimizer
+	 */
+	OptimizerFactory.LegacyHiLoAlgorithmOptimizer getHiloOptimizer() {
+		return hiloOptimizer;
 	}
-
 }

Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java	2010-05-11 19:24:54 UTC (rev 19469)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java	2010-05-11 19:27:20 UTC (rev 19470)
@@ -48,6 +48,7 @@
 
 	private static Class[] CTOR_SIG = new Class[] { Class.class, int.class };
 
+	@SuppressWarnings({ "UnnecessaryBoxing" })
 	public static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize) {
 		String optimizerClassName;
 		if ( NONE.equals( type ) ) {
@@ -66,7 +67,7 @@
 		try {
 			Class optimizerClass = ReflectHelper.classForName( optimizerClassName );
 			Constructor ctor = optimizerClass.getConstructor( CTOR_SIG );
-			return ( Optimizer ) ctor.newInstance( new Object[] { returnClass, new Integer( incrementSize ) } );
+			return ( Optimizer ) ctor.newInstance( returnClass, Integer.valueOf( incrementSize ) );
 		}
 		catch( Throwable ignore ) {
 			// intentionally empty
@@ -265,6 +266,67 @@
 		}
 	}
 
+	public static class LegacyHiLoAlgorithmOptimizer extends OptimizerSupport {
+		private long maxLo;
+		private long lo;
+		private IntegralDataTypeHolder hi;
+
+		private IntegralDataTypeHolder lastSourceValue;
+		private IntegralDataTypeHolder value;
+
+
+		public LegacyHiLoAlgorithmOptimizer(Class returnClass, int incrementSize) {
+			super( returnClass, incrementSize );
+			if ( incrementSize < 1 ) {
+				throw new HibernateException( "increment size cannot be less than 1" );
+			}
+			if ( log.isTraceEnabled() ) {
+				log.trace( "creating hilo optimizer (legacy) with [incrementSize=" + incrementSize + "; returnClass="  + returnClass.getName() + "]" );
+			}
+
+			maxLo = incrementSize;
+			lo = maxLo+1;
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public synchronized Serializable generate(AccessCallback callback) {
+			if ( lo > maxLo ) {
+				lastSourceValue = callback.getNextValue();
+				lo = lastSourceValue.eq( 0 ) ? 1 : 0;
+				hi = lastSourceValue.copy().multiplyBy( maxLo+1 );
+			}
+			value = hi.copy().add( lo++ );
+			return value.makeValue();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public IntegralDataTypeHolder getLastSourceValue() {
+			return lastSourceValue.copy();
+		}
+
+		/**
+		 * {@inheritDoc}
+		 */
+		public boolean applyIncrementSizeToSourceValues() {
+			return false;
+		}
+
+		/**
+		 * Getter for property 'lastValue'.
+		 * <p/>
+		 * Exposure intended for testing purposes.
+		 *
+		 * @return Value for property 'lastValue'.
+		 */
+		public IntegralDataTypeHolder getLastValue() {
+			return value;
+		}
+	}
+
 	/**
 	 * Optimizer which uses a pool of values, storing the next low value of the
 	 * range in the database.

Added: core/trunk/core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java
===================================================================
--- core/trunk/core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java	                        (rev 0)
+++ core/trunk/core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java	2010-05-11 19:27:20 UTC (rev 19470)
@@ -0,0 +1,169 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. 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 Inc.
+ *
+ * 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.id;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.cfg.NamingStrategy;
+import org.hibernate.cfg.ObjectNameNormalizer;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.impl.SessionImpl;
+import org.hibernate.jdbc.Work;
+import org.hibernate.mapping.SimpleAuxiliaryDatabaseObject;
+
+/**
+ * I went back to 3.3 source and grabbed the code/logic as it existed back then and crafted this
+ * unit test so that we can make sure the value keep being generated in the expected manner
+ *
+ * @author Steve Ebersole
+ */
+ at SuppressWarnings({ "deprecation" })
+public class SequenceHiLoGeneratorTest extends TestCase {
+	private static final String TEST_SEQUENCE = "test_sequence";
+
+	private Configuration cfg;
+	private SessionFactoryImplementor sessionFactory;
+	private SequenceHiLoGenerator generator;
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		Properties properties = new Properties();
+		properties.setProperty( SequenceGenerator.SEQUENCE, TEST_SEQUENCE );
+		properties.setProperty( SequenceHiLoGenerator.MAX_LO, "3" );
+		properties.put(
+				PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER,
+				new ObjectNameNormalizer() {
+					@Override
+					protected boolean isUseQuotedIdentifiersGlobally() {
+						return false;
+					}
+
+					@Override
+					protected NamingStrategy getNamingStrategy() {
+						return cfg.getNamingStrategy();
+					}
+				}
+		);
+
+		Dialect dialect = new H2Dialect();
+
+		generator = new SequenceHiLoGenerator();
+		generator.configure( Hibernate.LONG, properties, dialect );
+
+		cfg = new Configuration()
+				.setProperty( Environment.DRIVER, "org.h2.Driver" )
+				.setProperty( Environment.URL, "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1" )
+				.setProperty( Environment.USER, "sa" )
+				.setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+		cfg.addAuxiliaryDatabaseObject(
+				new SimpleAuxiliaryDatabaseObject(
+						generator.sqlCreateStrings( dialect )[0],
+						generator.sqlDropStrings( dialect )[0]
+				)
+		);
+
+		sessionFactory = (SessionFactoryImplementor) cfg.buildSessionFactory();
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		if ( sessionFactory != null ) {
+			sessionFactory.close();
+		}
+
+		super.tearDown();
+	}
+
+	public void testHiLoAlgorithm() {
+		SessionImpl session = (SessionImpl) sessionFactory.openSession();
+		session.beginTransaction();
+
+		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		// initially sequence should be uninitialized
+		assertEquals( 0L, extractSequenceValue( session ) );
+
+		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		// historically the hilo generators skipped the initial block of values;
+		// 		so the first generated id value is maxlo + 1, here be 4
+		Long generatedValue = (Long) generator.generate( session, null );
+		assertEquals( 4L, generatedValue.longValue() );
+		// which should also perform the first read on the sequence which should set it to its "start with" value (1)
+		assertEquals( 1L, extractSequenceValue( session ) );
+
+		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		generatedValue = (Long) generator.generate( session, null );
+		assertEquals( 5L, generatedValue.longValue() );
+		assertEquals( 1L, extractSequenceValue( session ) );
+
+		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		generatedValue = (Long) generator.generate( session, null );
+		assertEquals( 6L, generatedValue.longValue() );
+		assertEquals( 1L, extractSequenceValue( session ) );
+
+		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		generatedValue = (Long) generator.generate( session, null );
+		assertEquals( 7L, generatedValue.longValue() );
+		// unlike the newer strategies, the db value will not get update here.  It gets updated on the next invocation
+		// 	after a clock over
+		assertEquals( 1L, extractSequenceValue( session ) );
+
+		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+		generatedValue = (Long) generator.generate( session, null );
+		assertEquals( 8L, generatedValue.longValue() );
+		// this should force an increment in the sequence value
+		assertEquals( 2L, extractSequenceValue( session ) );
+
+		session.getTransaction().commit();
+		session.close();
+	}
+
+	private long extractSequenceValue(Session session) {
+		class WorkImpl implements Work {
+			private long value;
+			public void execute(Connection connection) throws SQLException {
+				PreparedStatement query = connection.prepareStatement( "select currval('" + TEST_SEQUENCE + "');" );
+				ResultSet resultSet = query.executeQuery();
+				resultSet.next();
+				value = resultSet.getLong( 1 );
+			}
+		}
+		WorkImpl work = new WorkImpl();
+		session.doWork( work );
+		return work.value;
+	}
+}

Modified: core/trunk/parent/pom.xml
===================================================================
--- core/trunk/parent/pom.xml	2010-05-11 19:24:54 UTC (rev 19469)
+++ core/trunk/parent/pom.xml	2010-05-11 19:27:20 UTC (rev 19470)
@@ -593,6 +593,7 @@
                     <groupId>com.h2database</groupId>
                     <artifactId>h2</artifactId>
                     <version>1.2.134</version>
+                    <scope>test</scope>
                 </dependency>
             </dependencies>
             <properties>
@@ -738,6 +739,7 @@
                     <groupId>com.ibm</groupId>
                     <artifactId>db2jcc_license_cu</artifactId>
                     <version>3.1.57</version>
+                    <scope>test</scope>
                 </dependency>
             </dependencies>
             <properties>
@@ -764,6 +766,7 @@
                     <groupId>com.ibm</groupId>
                     <artifactId>db2jcc_license_cu</artifactId>
                     <version>3.8.47</version>
+                    <scope>test</scope>
                 </dependency>
             </dependencies>
             <properties>
@@ -790,6 +793,7 @@
                     <groupId>com.ibm</groupId>
                     <artifactId>db2jcc_license_cu</artifactId>
                     <version>3.57.86</version>
+                    <scope>test</scope>
                 </dependency>
             </dependencies>
             <properties>



More information about the hibernate-commits mailing list