HHH-6855 - SequenceStyleGenerator should force use of TableStructure when the optimizer is PooledLo

This commit is contained in:
Steve Ebersole 2011-12-30 21:05:03 -06:00
parent 5071a82458
commit b2f24c6987
7 changed files with 580 additions and 476 deletions

View File

@ -1,78 +1,78 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, 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.id.enhanced;
import java.io.Serializable;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Performs optimization on an optimizable identifier generator. Typically
* this optimization takes the form of trying to ensure we do not have to
* hit the database on each and every request to get an identifier value.
* <p/>
* Optimizers work on constructor injection. They should provide
* a constructor with the following arguments <ol>
* <li>java.lang.Class - The return type for the generated values</li>
* <li>int - The increment size</li>
* </ol>
*
* @author Steve Ebersole
*/
public interface Optimizer {
/**
* Generate an identifier value accounting for this specific optimization.
*
* @param callback Callback to access the underlying value source.
* @return The generated identifier value.
*/
public Serializable generate(AccessCallback callback);
/**
* A common means to access the last value obtained from the underlying
* source. This is intended for testing purposes, since accessing the
* underlying database source directly is much more difficult.
*
* @return The last value we obtained from the underlying source;
* null indicates we have not yet consulted with the source.
*/
public IntegralDataTypeHolder getLastSourceValue();
/**
* Retrieves the defined increment size.
*
* @return The increment size.
*/
public int getIncrementSize();
/**
* Are increments to be applied to the values stored in the underlying
* value source?
*
* @return True if the values in the source are to be incremented
* according to the defined increment size; false otherwise, in which
* case the increment is totally an in memory construct.
*/
public boolean applyIncrementSizeToSourceValues();
}
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008-2011, 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.enhanced;
import java.io.Serializable;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Performs optimization on an optimizable identifier generator. Typically
* this optimization takes the form of trying to ensure we do not have to
* hit the database on each and every request to get an identifier value.
* <p/>
* Optimizers work on constructor injection. They should provide
* a constructor with the following arguments <ol>
* <li>java.lang.Class - The return type for the generated values</li>
* <li>int - The increment size</li>
* </ol>
*
* @author Steve Ebersole
*/
public interface Optimizer {
/**
* Generate an identifier value accounting for this specific optimization.
*
* @param callback Callback to access the underlying value source.
* @return The generated identifier value.
*/
public Serializable generate(AccessCallback callback);
/**
* A common means to access the last value obtained from the underlying
* source. This is intended for testing purposes, since accessing the
* underlying database source directly is much more difficult.
*
* @return The last value we obtained from the underlying source;
* null indicates we have not yet consulted with the source.
*/
public IntegralDataTypeHolder getLastSourceValue();
/**
* Retrieves the defined increment size.
*
* @return The increment size.
*/
public int getIncrementSize();
/**
* Are increments to be applied to the values stored in the underlying
* value source?
*
* @return True if the values in the source are to be incremented
* according to the defined increment size; false otherwise, in which
* case the increment is totally an in memory construct.
*/
public boolean applyIncrementSizeToSourceValues();
}

View File

@ -32,6 +32,7 @@ import org.hibernate.HibernateException;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
/**
* Factory for {@link Optimizer} instances.
@ -39,16 +40,70 @@ import org.hibernate.internal.util.ReflectHelper;
* @author Steve Ebersole
*/
public class OptimizerFactory {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
OptimizerFactory.class.getName()
);
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, OptimizerFactory.class.getName());
public static enum StandardOptimizerDescriptor {
NONE( "none", NoopOptimizer.class ),
HILO( "hilo", HiLoOptimizer.class ),
LEGACY_HILO( "legacy-hilo", LegacyHiLoAlgorithmOptimizer.class ),
POOLED( "pooled", PooledOptimizer.class, true ),
POOLED_LO( "pooled-lo", PooledLoOptimizer.class, true );
public static final String NONE = "none";
public static final String HILO = "hilo";
public static final String LEGACY_HILO = "legacy-hilo";
public static final String POOL = "pooled";
public static final String POOL_LO = "pooled-lo";
private final String externalName;
private final Class<? extends Optimizer> optimizerClass;
private final boolean isPooled;
private static Class[] CTOR_SIG = new Class[] { Class.class, int.class };
StandardOptimizerDescriptor(String externalName, Class<? extends Optimizer> optimizerClass) {
this( externalName, optimizerClass, false );
}
StandardOptimizerDescriptor(String externalName, Class<? extends Optimizer> optimizerClass, boolean pooled) {
this.externalName = externalName;
this.optimizerClass = optimizerClass;
this.isPooled = pooled;
}
public String getExternalName() {
return externalName;
}
public Class<? extends Optimizer> getOptimizerClass() {
return optimizerClass;
}
public boolean isPooled() {
return isPooled;
}
public static StandardOptimizerDescriptor fromExternalName(String externalName) {
if ( StringHelper.isEmpty( externalName ) ) {
LOG.debug( "No optimizer specified, using NONE as default" );
return NONE;
}
else if ( NONE.externalName.equals( externalName ) ) {
return NONE;
}
else if ( HILO.externalName.equals( externalName ) ) {
return HILO;
}
else if ( LEGACY_HILO.externalName.equals( externalName ) ) {
return LEGACY_HILO;
}
else if ( POOLED.externalName.equals( externalName ) ) {
return POOLED;
}
else if ( POOLED_LO.externalName.equals( externalName ) ) {
return POOLED_LO;
}
else {
LOG.debugf( "Unknown optimizer key [%s]; returning null assuming Optimizer impl class name" );
return null;
}
}
}
/**
* Marker interface for optimizer which wish to know the user-specified initial value.
@ -68,6 +123,13 @@ public class OptimizerFactory {
public void injectInitialValue(long initialValue);
}
public static boolean isPooledOptimizer(String type) {
final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( type );
return standardDescriptor != null && standardDescriptor.isPooled();
}
private static Class[] CTOR_SIG = new Class[] { Class.class, int.class };
/**
* Builds an optimizer
*
@ -80,41 +142,38 @@ public class OptimizerFactory {
* @deprecated Use {@link #buildOptimizer(String, Class, int, long)} instead
*/
@Deprecated
@SuppressWarnings({ "UnnecessaryBoxing" })
@SuppressWarnings( {"UnnecessaryBoxing", "unchecked"})
public static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize) {
String optimizerClassName;
if ( NONE.equals( type ) ) {
optimizerClassName = NoopOptimizer.class.getName();
}
else if ( HILO.equals( type ) ) {
optimizerClassName = HiLoOptimizer.class.getName();
}
else if ( LEGACY_HILO.equals( type ) ) {
optimizerClassName = LegacyHiLoAlgorithmOptimizer.class.getName();
}
else if ( POOL.equals( type ) ) {
optimizerClassName = PooledOptimizer.class.getName();
}
else if ( POOL_LO.equals( type ) ) {
optimizerClassName = PooledLoOptimizer.class.getName();
final Class<? extends Optimizer> optimizerClass;
final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( type );
if ( standardDescriptor != null ) {
optimizerClass = standardDescriptor.getOptimizerClass();
}
else {
optimizerClassName = type;
try {
optimizerClass = ReflectHelper.classForName( type );
}
catch( Throwable ignore ) {
LOG.unableToLocateCustomOptimizerClass( type );
return buildFallbackOptimizer( returnClass, incrementSize );
}
}
try {
Class optimizerClass = ReflectHelper.classForName( optimizerClassName );
Constructor ctor = optimizerClass.getConstructor( CTOR_SIG );
return ( Optimizer ) ctor.newInstance( returnClass, Integer.valueOf( incrementSize ) );
}
catch( Throwable ignore ) {
LOG.unableToInstantiateOptimizer(type);
LOG.unableToInstantiateOptimizer( type );
}
// the default...
return new NoopOptimizer( returnClass, incrementSize );
return buildFallbackOptimizer( returnClass, incrementSize );
}
private static Optimizer buildFallbackOptimizer(Class returnClass, int incrementSize) {
return new NoopOptimizer( returnClass, incrementSize );
}
/**
* Builds an optimizer
@ -162,13 +221,12 @@ public class OptimizerFactory {
*
* @return Value for property 'returnClass'.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public final Class getReturnClass() {
return returnClass;
}
/**
* {@inheritDoc}
*/
@Override
public final int getIncrementSize() {
return incrementSize;
}
@ -185,9 +243,7 @@ public class OptimizerFactory {
super( returnClass, incrementSize );
}
/**
* {@inheritDoc}
*/
@Override
public Serializable generate(AccessCallback callback) {
// IMPL NOTE : it is incredibly important that the method-local variable be used here to
// avoid concurrency issues.
@ -199,16 +255,12 @@ public class OptimizerFactory {
return value.makeValue();
}
/**
* {@inheritDoc}
*/
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
/**
* {@inheritDoc}
*/
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
@ -261,9 +313,7 @@ public class OptimizerFactory {
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue == null ) {
// first call, so initialize ourselves. we need to read the database
@ -284,17 +334,12 @@ public class OptimizerFactory {
return value.makeValueThenIncrement();
}
/**
* {@inheritDoc}
*/
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
/**
* {@inheritDoc}
*/
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
@ -342,9 +387,7 @@ public class OptimizerFactory {
lo = maxLo+1;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized Serializable generate(AccessCallback callback) {
if ( lo > maxLo ) {
lastSourceValue = callback.getNextValue();
@ -355,16 +398,12 @@ public class OptimizerFactory {
return value.makeValue();
}
/**
* {@inheritDoc}
*/
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue.copy();
}
/**
* {@inheritDoc}
*/
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
@ -376,6 +415,7 @@ public class OptimizerFactory {
*
* @return Value for property 'lastValue'.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public IntegralDataTypeHolder getLastValue() {
return value;
}
@ -407,9 +447,7 @@ public class OptimizerFactory {
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue == null ) {
value = callback.getNextValue();
@ -432,16 +470,12 @@ public class OptimizerFactory {
return value.makeValueThenIncrement();
}
/**
* {@inheritDoc}
*/
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return hiValue;
}
/**
* {@inheritDoc}
*/
@Override
public boolean applyIncrementSizeToSourceValues() {
return true;
}
@ -457,9 +491,7 @@ public class OptimizerFactory {
return value.copy().decrement();
}
/**
* {@inheritDoc}
*/
@Override
public void injectInitialValue(long initialValue) {
this.initialValue = initialValue;
}
@ -479,6 +511,7 @@ public class OptimizerFactory {
}
}
@Override
public Serializable generate(AccessCallback callback) {
if ( lastSourceValue == null || ! value.lt( lastSourceValue.copy().add( incrementSize ) ) ) {
lastSourceValue = callback.getNextValue();
@ -491,12 +524,49 @@ public class OptimizerFactory {
return value.makeValueThenIncrement();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return true;
}
}
/**
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#NONE}
*/
@Deprecated
@SuppressWarnings( {"UnusedDeclaration"})
public static final String NONE = StandardOptimizerDescriptor.NONE.getExternalName();
/**
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#HILO}
*/
@Deprecated
@SuppressWarnings( {"UnusedDeclaration"})
public static final String HILO = StandardOptimizerDescriptor.HILO.getExternalName();
/**
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#LEGACY_HILO}
*/
@Deprecated
@SuppressWarnings( {"UnusedDeclaration"})
public static final String LEGACY_HILO = "legacy-hilo";
/**
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#POOLED}
*/
@Deprecated
@SuppressWarnings( {"UnusedDeclaration"})
public static final String POOL = "pooled";
/**
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#POOLED_LO}
*/
@Deprecated
@SuppressWarnings( {"UnusedDeclaration"})
public static final String POOL_LO = "pooled-lo";
}

View File

@ -174,7 +174,7 @@ public class SequenceStyleGenerator
incrementSize = determineAdjustedIncrementSize( optimizationStrategy, incrementSize );
if ( dialect.supportsSequences() && !forceTableUse ) {
if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
if ( !dialect.supportsPooledSequences() && OptimizerFactory.isPooledOptimizer( optimizationStrategy ) ) {
forceTableUse = true;
LOG.forcingTableUse();
}
@ -282,12 +282,14 @@ public class SequenceStyleGenerator
* @return The optimizer strategy (name)
*/
protected String determineOptimizationStrategy(Properties params, int incrementSize) {
// if the increment size is greater than one, we prefer pooled optimization; but we
// if the increment size is greater than one, we prefer pooled optimization; but we first
// need to see if the user prefers POOL or POOL_LO...
String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false )
? OptimizerFactory.POOL_LO
: OptimizerFactory.POOL;
String defaultOptimizerStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : defaultPooledOptimizerStrategy;
? OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO.getExternalName()
: OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName();
String defaultOptimizerStrategy = incrementSize <= 1
? OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName()
: defaultPooledOptimizerStrategy;
return ConfigurationHelper.getString( OPT_PARAM, params, defaultOptimizerStrategy );
}
@ -300,8 +302,13 @@ public class SequenceStyleGenerator
* @return The adjusted increment size.
*/
protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
if ( OptimizerFactory.NONE.equals( optimizationStrategy ) && incrementSize > 1 ) {
LOG.honoringOptimizerSetting(OptimizerFactory.NONE, INCREMENT_PARAM, incrementSize);
if ( incrementSize > 1
&& OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName().equals( optimizationStrategy ) ) {
LOG.honoringOptimizerSetting(
OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName(),
INCREMENT_PARAM,
incrementSize
);
incrementSize = 1;
}
return incrementSize;

View File

@ -305,9 +305,11 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
// if the increment size is greater than one, we prefer pooled optimization; but we
// need to see if the user prefers POOL or POOL_LO...
String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false )
? OptimizerFactory.POOL_LO
: OptimizerFactory.POOL;
final String defaultOptimizerStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : defaultPooledOptimizerStrategy;
? OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO.getExternalName()
: OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName();
final String defaultOptimizerStrategy = incrementSize <= 1
? OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName()
: defaultPooledOptimizerStrategy;
final String optimizationStrategy = ConfigurationHelper.getString( OPT_PARAM, params, defaultOptimizerStrategy );
optimizer = OptimizerFactory.buildOptimizer(
optimizationStrategy,
@ -569,7 +571,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
StringBuilder sqlDropString = new StringBuilder().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
sqlDropString.append( "if exists " );
}

View File

@ -1095,6 +1095,10 @@ public interface CoreMessageLogger extends BasicLogger {
void unableToInstantiateConfiguredSchemaNameResolver(String resolverClassName,
String message);
@LogMessage(level = WARN)
@Message(value = "Unable to interpret specified optimizer [%s], falling back to noop", id = 321)
void unableToLocateCustomOptimizerClass(String type);
@LogMessage(level = WARN)
@Message(value = "Unable to instantiate specified optimizer [%s], falling back to noop", id = 322)
void unableToInstantiateOptimizer(String type);

View File

@ -1,312 +1,334 @@
/*
* 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.enhanced;
import org.junit.Test;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertEquals;
/**
* {@inheritDoc}
*
* @author Steve Ebersole
*/
@SuppressWarnings({ "deprecation" })
public class OptimizerUnitTest extends BaseUnitTestCase {
@Test
public void testBasicNoOptimizerUsage() {
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1 );
Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
for ( int i = 1; i < 11; i++ ) {
final Long next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 10, sequence.getTimesCalled() );
assertEquals( 10, sequence.getCurrentValue() );
// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
sequence = new SourceMock( 0 );
optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.NONE, Long.class, 1 );
for ( int i = 1; i < 11; i++ ) {
final Long next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 11, sequence.getTimesCalled() ); // an extra time to get to 1 initially
assertEquals( 10, sequence.getCurrentValue() );
}
@Test
public void testBasicHiLoOptimizerUsage() {
int increment = 10;
Long next;
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1 );
Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
for ( int i = 1; i <= increment; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 1, sequence.getTimesCalled() ); // once to initialze state
assertEquals( 1, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 2, sequence.getCurrentValue() );
// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
sequence = new SourceMock( 0 );
optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.HILO, Long.class, increment );
for ( int i = 1; i <= increment; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // here have have an extra call to get to 1 initially
assertEquals( 1, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 2, sequence.getCurrentValue() );
}
@Test
public void testBasicPooledOptimizerUsage() {
Long next;
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1, 10 );
Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 10 );
for ( int i = 1; i < 11; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // twice to initialize state
assertEquals( 11, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 21, sequence.getCurrentValue() );
}
@Test
public void testSubsequentPooledOptimizerUsage() {
// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
// cheat by telling the sequence to start with 1000
final SourceMock sequence = new SourceMock( 1001, 3, 5 );
// but tell the optimizer the start-with is 1
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
assertEquals( 5, sequence.getTimesCalled() );
assertEquals( 1001, sequence.getCurrentValue() );
Long next = (Long) optimizer.generate( sequence );
assertEquals( 1001, next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+1), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+2), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
// force a "clock over"
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+3), next.intValue() );
assertEquals( (5+2), sequence.getTimesCalled() );
assertEquals( (1001+6), sequence.getCurrentValue() );
}
@Test
public void testBasicPooledLoOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( 2, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( 3, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
// // force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( (1+3), sequence.getCurrentValue() );
}
@Test
public void testSubsequentPooledLoOptimizerUsage() {
// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
// cheat by telling the sequence to start with 1000
final SourceMock sequence = new SourceMock( 1001, 3, 5 );
// but tell the optimizer the start-with is 1
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
assertEquals( 5, sequence.getTimesCalled() );
assertEquals( 1001, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+1), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+2), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
// // force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+3), next.intValue() );
assertEquals( (5+2), sequence.getTimesCalled() );
assertEquals( (1001+6), sequence.getCurrentValue() );
}
@Test
public void testRecoveredPooledOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 4, sequence.getCurrentValue() );
// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
final Optimizer optimizer2 = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL, Long.class, 3, 1 );
next = ( Long ) optimizer2.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 7, sequence.getCurrentValue() );
}
@Test
public void testRecoveredPooledLoOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3, 1 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
final Optimizer optimizer2 = OptimizerFactory.buildOptimizer( OptimizerFactory.POOL_LO, Long.class, 3, 1 );
next = ( Long ) optimizer2.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 4, sequence.getCurrentValue() );
}
private static class SourceMock implements AccessCallback {
private IdentifierGeneratorHelper.BasicHolder value = new IdentifierGeneratorHelper.BasicHolder( Long.class );
private long initialValue;
private int increment;
private int timesCalled = 0;
public SourceMock(long initialValue) {
this( initialValue, 1 );
}
public SourceMock(long initialValue, int increment) {
this( initialValue, increment, 0 );
}
public SourceMock(long initialValue, int increment, int timesCalled) {
this.increment = increment;
this.timesCalled = timesCalled;
if ( timesCalled != 0 ) {
this.value.initialize( initialValue );
this.initialValue = 1;
}
else {
this.value.initialize( -1 );
this.initialValue = initialValue;
}
}
public IntegralDataTypeHolder getNextValue() {
try {
if ( timesCalled == 0 ) {
initValue();
return value.copy();
}
else {
return value.add( increment ).copy();
}
}
finally {
timesCalled++;
}
}
private void initValue() {
this.value.initialize( initialValue );
}
public int getTimesCalled() {
return timesCalled;
}
public long getCurrentValue() {
return value == null ? -1 : value.getActualLongValue();
}
}
}
/*
* 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.enhanced;
import org.junit.Test;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertEquals;
/**
* {@inheritDoc}
*
* @author Steve Ebersole
*/
public class OptimizerUnitTest extends BaseUnitTestCase {
@Test
public void testBasicNoOptimizerUsage() {
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1 );
Optimizer optimizer = buildNoneOptimizer( -1, 1 );
for ( int i = 1; i < 11; i++ ) {
final Long next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 10, sequence.getTimesCalled() );
assertEquals( 10, sequence.getCurrentValue() );
// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
sequence = new SourceMock( 0 );
optimizer = buildNoneOptimizer( -1, 1 );
for ( int i = 1; i < 11; i++ ) {
final Long next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 11, sequence.getTimesCalled() ); // an extra time to get to 1 initially
assertEquals( 10, sequence.getCurrentValue() );
}
@Test
public void testBasicHiLoOptimizerUsage() {
int increment = 10;
Long next;
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1 );
Optimizer optimizer = buildHiloOptimizer( -1, increment );
for ( int i = 1; i <= increment; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 1, sequence.getTimesCalled() ); // once to initialze state
assertEquals( 1, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 2, sequence.getCurrentValue() );
// test historic table behavior, where the initial values started at 0 (we now force 1 to be the first used id value)
sequence = new SourceMock( 0 );
optimizer = buildHiloOptimizer( -1, increment );
for ( int i = 1; i <= increment; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // here have have an extra call to get to 1 initially
assertEquals( 1, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 2, sequence.getCurrentValue() );
}
@Test
public void testBasicPooledOptimizerUsage() {
Long next;
// test historic sequence behavior, where the initial values start at 1...
SourceMock sequence = new SourceMock( 1, 10 );
Optimizer optimizer = buildPooledOptimizer( -1, 10 );
for ( int i = 1; i < 11; i++ ) {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // twice to initialize state
assertEquals( 11, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 11, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 21, sequence.getCurrentValue() );
}
@Test
public void testSubsequentPooledOptimizerUsage() {
// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
// cheat by telling the sequence to start with 1000
final SourceMock sequence = new SourceMock( 1001, 3, 5 );
// but tell the optimizer the start-with is 1
final Optimizer optimizer = buildPooledOptimizer( 1, 3 );
assertEquals( 5, sequence.getTimesCalled() );
assertEquals( 1001, sequence.getCurrentValue() );
Long next = (Long) optimizer.generate( sequence );
assertEquals( 1001, next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+1), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+2), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
// force a "clock over"
next = (Long) optimizer.generate( sequence );
assertEquals( (1001+3), next.intValue() );
assertEquals( (5+2), sequence.getTimesCalled() );
assertEquals( (1001+6), sequence.getCurrentValue() );
}
@Test
public void testBasicPooledLoOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = buildPooledLoOptimizer( 1, 3 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( 2, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( 3, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
// // force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( (1+3), sequence.getCurrentValue() );
}
@Test
public void testSubsequentPooledLoOptimizerUsage() {
// test the pooled optimizer in situation where the sequence is already beyond its initial value on init.
// cheat by telling the sequence to start with 1000
final SourceMock sequence = new SourceMock( 1001, 3, 5 );
// but tell the optimizer the start-with is 1
final Optimizer optimizer = buildPooledOptimizer( 1, 3 );
assertEquals( 5, sequence.getTimesCalled() );
assertEquals( 1001, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+1), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+2), next.intValue() );
assertEquals( (5+1), sequence.getTimesCalled() );
assertEquals( (1001+3), sequence.getCurrentValue() );
// // force a "clock over"
next = ( Long ) optimizer.generate( sequence );
assertEquals( (1001+3), next.intValue() );
assertEquals( (5+2), sequence.getTimesCalled() );
assertEquals( (1001+6), sequence.getCurrentValue() );
}
@Test
public void testRecoveredPooledOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = buildPooledOptimizer( 1, 3 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 4, sequence.getCurrentValue() );
// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
final Optimizer optimizer2 = buildPooledOptimizer( 1, 3 );
next = ( Long ) optimizer2.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 3, sequence.getTimesCalled() );
assertEquals( 7, sequence.getCurrentValue() );
}
@Test
public void testRecoveredPooledLoOptimizerUsage() {
final SourceMock sequence = new SourceMock( 1, 3 );
final Optimizer optimizer = buildPooledLoOptimizer( 1, 3 );
assertEquals( 0, sequence.getTimesCalled() );
assertEquals( -1, sequence.getCurrentValue() );
Long next = ( Long ) optimizer.generate( sequence );
assertEquals( 1, next.intValue() );
assertEquals( 1, sequence.getTimesCalled() );
assertEquals( 1, sequence.getCurrentValue() );
// app ends, and starts back up (we should "lose" only 2 and 3 as id values)
final Optimizer optimizer2 = buildPooledLoOptimizer( 1, 3 );
next = ( Long ) optimizer2.generate( sequence );
assertEquals( 4, next.intValue() );
assertEquals( 2, sequence.getTimesCalled() );
assertEquals( 4, sequence.getCurrentValue() );
}
private static Optimizer buildNoneOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.NONE, initial, increment );
}
private static Optimizer buildHiloOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.HILO, initial, increment );
}
private static Optimizer buildPooledOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.POOLED, initial, increment );
}
private static Optimizer buildPooledLoOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO, initial, increment );
}
private static Optimizer buildOptimizer(
OptimizerFactory.StandardOptimizerDescriptor descriptor,
long initial,
int increment) {
return OptimizerFactory.buildOptimizer( descriptor.getExternalName(), Long.class, increment, initial );
}
private static class SourceMock implements AccessCallback {
private IdentifierGeneratorHelper.BasicHolder value = new IdentifierGeneratorHelper.BasicHolder( Long.class );
private long initialValue;
private int increment;
private int timesCalled = 0;
public SourceMock(long initialValue) {
this( initialValue, 1 );
}
public SourceMock(long initialValue, int increment) {
this( initialValue, increment, 0 );
}
public SourceMock(long initialValue, int increment, int timesCalled) {
this.increment = increment;
this.timesCalled = timesCalled;
if ( timesCalled != 0 ) {
this.value.initialize( initialValue );
this.initialValue = 1;
}
else {
this.value.initialize( -1 );
this.initialValue = initialValue;
}
}
public IntegralDataTypeHolder getNextValue() {
try {
if ( timesCalled == 0 ) {
initValue();
return value.copy();
}
else {
return value.add( increment ).copy();
}
}
finally {
timesCalled++;
}
}
private void initValue() {
this.value.initialize( initialValue );
}
public int getTimesCalled() {
return timesCalled;
}
public long getCurrentValue() {
return value == null ? -1 : value.getActualLongValue();
}
}
}

View File

@ -44,7 +44,6 @@ import static org.junit.Assert.fail;
*
* @author Steve Ebersole
*/
@SuppressWarnings({ "deprecation" })
public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
private void assertClassAssignability(Class expected, Class actual) {
if ( ! expected.isAssignableFrom( actual ) ) {
@ -169,7 +168,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
// optimizer=none w/ increment > 1 => should honor optimizer
Properties props = buildGeneratorPropertiesBase();
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.NONE );
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName() );
props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
SequenceStyleGenerator generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
@ -180,7 +179,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
// optimizer=hilo w/ increment > 1 => hilo
props = buildGeneratorPropertiesBase();
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.HILO );
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.HILO.getExternalName() );
props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
@ -191,7 +190,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
// optimizer=pooled w/ increment > 1 => hilo
props = buildGeneratorPropertiesBase();
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.POOL );
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName() );
props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );