diff --git a/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java b/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java
index a0133b2f0e..8f03a62291 100644
--- a/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java
+++ b/core/src/main/java/org/hibernate/id/TableHiLoGenerator.java
@@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * 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 Middleware LLC.
+ * 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
@@ -20,17 +20,16 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.id;
import java.io.Serializable;
import java.util.Properties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
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;
@@ -38,7 +37,7 @@ import org.hibernate.util.PropertiesHelper;
* hilo
*
* An IdentifierGenerator that returns a Long, constructed using
- * a hi/lo algorithm. The hi value MUST be fetched in a seperate transaction
+ * a hi/lo algorithm. The hi value MUST be fetched in a separate transaction
* to the Session transaction so the generator must be able to obtain
* a new connection and commit it. Hence this implementation may not
* be used when the user is supplying connections. In this
@@ -51,26 +50,25 @@ import org.hibernate.util.PropertiesHelper;
* @author Gavin King
*/
public class TableHiLoGenerator extends TableGenerator {
-
/**
* The max_lo parameter
*/
public static final String MAX_LO = "max_lo";
+ private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer;
+
private int maxLo;
- private int lo;
-
- private IntegralDataTypeHolder value;
-
- private static final Logger log = LoggerFactory.getLogger(TableHiLoGenerator.class);
public void configure(Type type, Properties params, Dialect d) {
super.configure(type, params, d);
maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
- lo = maxLo + 1; // so we "clock over" on the first invocation
+
+ if ( maxLo >= 1 ) {
+ hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( type.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
@@ -81,16 +79,13 @@ public class TableHiLoGenerator extends TableGenerator {
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 value.makeValueThenIncrement();
+ return hiloOptimizer.generate(
+ new AccessCallback() {
+ public IntegralDataTypeHolder getNextValue() {
+ return generateHolder( session );
+ }
+ }
+ );
}
}
diff --git a/core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java b/core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java
new file mode 100644
index 0000000000..7a18842231
--- /dev/null
+++ b/core/src/test/java/org/hibernate/id/TableHiLoGeneratorTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.TestingDatabaseInfo;
+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
+ */
+@SuppressWarnings({ "deprecation" })
+public class TableHiLoGeneratorTest extends TestCase {
+ private static final String GEN_TABLE = "generator_table";
+ private static final String GEN_COLUMN = TableHiLoGenerator.DEFAULT_COLUMN_NAME;
+
+ private Configuration cfg;
+ private SessionFactoryImplementor sessionFactory;
+ private TableHiLoGenerator generator;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ Properties properties = new Properties();
+ properties.setProperty( TableHiLoGenerator.TABLE, GEN_TABLE );
+ properties.setProperty( TableHiLoGenerator.COLUMN, GEN_COLUMN );
+ properties.setProperty( TableHiLoGenerator.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 TableHiLoGenerator();
+ generator.configure( Hibernate.LONG, properties, dialect );
+
+ cfg = TestingDatabaseInfo.buildBaseConfiguration()
+ .setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+ cfg.addAuxiliaryDatabaseObject(
+ new SimpleAuxiliaryDatabaseObject(
+ generator.sqlCreateStrings( dialect )[0],
+ generator.sqlDropStrings( dialect )[0]
+ )
+ );
+
+ cfg.addAuxiliaryDatabaseObject(
+ new SimpleAuxiliaryDatabaseObject(
+ generator.sqlCreateStrings( dialect )[1],
+ null
+ )
+ );
+
+ 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, extractInDatabaseValue( session ) );
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Long generatedValue = (Long) generator.generate( session, null );
+ assertEquals( 1L, generatedValue.longValue() );
+ assertEquals( 1L, extractInDatabaseValue( session ) );
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ generatedValue = (Long) generator.generate( session, null );
+ assertEquals( 2L, generatedValue.longValue() );
+ assertEquals( 1L, extractInDatabaseValue( session ) );
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ generatedValue = (Long) generator.generate( session, null );
+ assertEquals( 3L, generatedValue.longValue() );
+ assertEquals( 1L, extractInDatabaseValue( session ) );
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ generatedValue = (Long) generator.generate( session, null );
+ assertEquals( 4L, generatedValue.longValue() );
+ assertEquals( 2L, extractInDatabaseValue( session ) );
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ generatedValue = (Long) generator.generate( session, null );
+ assertEquals( 5L, generatedValue.longValue() );
+ assertEquals( 2L, extractInDatabaseValue( session ) );
+
+ session.getTransaction().commit();
+ session.close();
+ }
+
+ private long extractInDatabaseValue(Session session) {
+ class WorkImpl implements Work {
+ private long value;
+ public void execute(Connection connection) throws SQLException {
+ PreparedStatement query = connection.prepareStatement( "select " + GEN_COLUMN + " from " + GEN_TABLE );
+ ResultSet resultSet = query.executeQuery();
+ resultSet.next();
+ value = resultSet.getLong( 1 );
+ }
+ }
+ WorkImpl work = new WorkImpl();
+ session.doWork( work );
+ return work.value;
+ }
+}
\ No newline at end of file