HHH-7582 - TableGenerator does not distinguish between different tenants (MultiTenant Schema based)

This commit is contained in:
Steve Ebersole 2013-05-20 15:17:09 -05:00
parent 45d46b619b
commit 91758f74ff
32 changed files with 1407 additions and 613 deletions

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008, 2013, 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,9 +20,9 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
import java.io.Serializable;
import org.hibernate.HibernateException;
@ -40,33 +40,33 @@ import org.hibernate.engine.spi.SessionImplementor;
* Implementations that accept configuration parameters should
* also implement <tt>Configurable</tt>.
* <br>
* Implementors <em>must</em> be threadsafe
* Implementors <em>must</em> be thread-safe
*
* @author Gavin King
*
* @see PersistentIdentifierGenerator
* @see Configurable
*/
public interface IdentifierGenerator {
/**
* The configuration parameter holding the entity name
*/
public static final String ENTITY_NAME = "entity_name";
/**
* The configuration parameter holding the entity name
*/
public static final String ENTITY_NAME = "entity_name";
/**
* The configuration parameter holding the JPA entity name
*/
public static final String JPA_ENTITY_NAME = "jpa_entity_name";
/**
* The configuration parameter holding the JPA entity name
*/
public static final String JPA_ENTITY_NAME = "jpa_entity_name";
/**
* Generate a new identifier.
* @param session
* @param object the entity or toplevel collection for which the id is being generated
*
* @param session The session from which the request originates
* @param object the entity or collection (idbag) for which the id is being generated
*
* @return a new identifier
* @throws HibernateException
*
* @throws HibernateException Indicates trouble generating the identifier
*/
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException;
public Serializable generate(SessionImplementor session, Object object) throws HibernateException;
}

View File

@ -43,7 +43,7 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.AbstractReturningWork;
@ -109,7 +109,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
public static final String MAX_LO = "max_lo";
private int maxLo;
private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer;
private LegacyHiLoAlgorithmOptimizer hiloOptimizer;
private Class returnClass;
private int keySize;
@ -215,7 +215,15 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
return hiloOptimizer.generate(
new AccessCallback() {
public IntegralDataTypeHolder getNextValue() {
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( work, true );
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
work,
true
);
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
}
);
@ -282,7 +290,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
returnClass = type.getReturnedClass();
if ( maxLo >= 1 ) {
hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( returnClass, maxLo );
hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( returnClass, maxLo );
}
}
}

View File

@ -29,7 +29,7 @@ import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.type.Type;
@ -49,7 +49,7 @@ public class SequenceHiLoGenerator extends SequenceGenerator {
private int maxLo;
private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer;
private LegacyHiLoAlgorithmOptimizer hiloOptimizer;
public void configure(Type type, Properties params, Dialect d) throws MappingException {
super.configure(type, params, d);
@ -57,7 +57,7 @@ public class SequenceHiLoGenerator extends SequenceGenerator {
maxLo = ConfigurationHelper.getInt( MAX_LO, params, 9 );
if ( maxLo >= 1 ) {
hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer(
hiloOptimizer = new LegacyHiLoAlgorithmOptimizer(
getIdentifierType().getReturnedClass(),
maxLo
);
@ -80,6 +80,11 @@ public class SequenceHiLoGenerator extends SequenceGenerator {
public IntegralDataTypeHolder getNextValue() {
return generateHolder( session );
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
}
);
}
@ -89,7 +94,7 @@ public class SequenceHiLoGenerator extends SequenceGenerator {
*
* @return The optimizer
*/
OptimizerFactory.LegacyHiLoAlgorithmOptimizer getHiloOptimizer() {
LegacyHiLoAlgorithmOptimizer getHiloOptimizer() {
return hiloOptimizer;
}
}

View File

@ -28,7 +28,7 @@ import java.util.Properties;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.type.Type;
@ -58,22 +58,24 @@ public class TableHiLoGenerator extends TableGenerator {
*/
public static final String MAX_LO = "max_lo";
private OptimizerFactory.LegacyHiLoAlgorithmOptimizer hiloOptimizer;
private LegacyHiLoAlgorithmOptimizer hiloOptimizer;
private int maxLo;
@Override
public void configure(Type type, Properties params, Dialect d) {
super.configure(type, params, d);
maxLo = ConfigurationHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
super.configure( type, params, d );
maxLo = ConfigurationHelper.getInt( MAX_LO, params, Short.MAX_VALUE );
if ( maxLo >= 1 ) {
hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer( type.getReturnedClass(), maxLo );
hiloOptimizer = new LegacyHiLoAlgorithmOptimizer( type.getReturnedClass(), maxLo );
}
}
@Override
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
// maxLo < 1 indicates a hilo generator with no hilo :?
if ( maxLo < 1 ) {
if ( maxLo < 1 ) {
//keep the behavior consistent even for boundary usages
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 0 ) ) {
@ -87,6 +89,11 @@ public class TableHiLoGenerator extends TableGenerator {
public IntegralDataTypeHolder getNextValue() {
return generateHolder( session );
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
}
);
}

View File

@ -0,0 +1,66 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.hibernate.HibernateException;
/**
* Common support for optimizer implementations.
*
* @author Steve Ebersole
*/
public abstract class AbstractOptimizer implements Optimizer {
protected final Class returnClass;
protected final int incrementSize;
/**
* Construct an optimizer
*
* @param returnClass The expected id class.
* @param incrementSize The increment size
*/
AbstractOptimizer(Class returnClass, int incrementSize) {
if ( returnClass == null ) {
throw new HibernateException( "return class is required" );
}
this.returnClass = returnClass;
this.incrementSize = incrementSize;
}
/**
* Getter for property 'returnClass'. This is the Java
* class which is used to represent the id (e.g. {@link Long}).
*
* @return Value for property 'returnClass'.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public final Class getReturnClass() {
return returnClass;
}
@Override
public final int getIncrementSize() {
return incrementSize;
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008, 2013, 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,9 +20,9 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id.enhanced;
import org.hibernate.id.IntegralDataTypeHolder;
/**
@ -38,4 +38,11 @@ public interface AccessCallback {
* @return The next value.
*/
public IntegralDataTypeHolder getNextValue();
/**
* Obtain the tenant identifier (multi-tenancy), if one, associated with this callback.
*
* @return The tenant identifier
*/
public String getTenantIdentifier();
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008, 2013, 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,9 +20,9 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id.enhanced;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionImplementor;

View File

@ -0,0 +1,185 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Optimizer which applies a 'hilo' algorithm in memory to achieve
* optimization.
* <p/>
* A 'hilo' algorithm is simply a means for a single value stored in the
* database to represent a "bucket" of possible, contiguous values. The
* database value identifies which particular bucket we are on.
* <p/>
* This database value must be paired with another value that defines the
* size of the bucket; the number of possible values available.
* The {@link #getIncrementSize() incrementSize} serves this purpose. The
* naming here is meant more for consistency in that this value serves the
* same purpose as the increment supplied to the {@link PooledOptimizer}.
* <p/>
* The general algorithms used to determine the bucket are:<ol>
* <li>{@code upperLimit = (databaseValue * incrementSize) + 1}</li>
* <li>{@code lowerLimit = upperLimit - incrementSize}</li>
* </ol>
* As an example, consider a case with incrementSize of 20. Initially the
* database holds 1:<ol>
* <li>{@code upperLimit = (1 * 20) + 1 = 21}</li>
* <li>{@code lowerLimit = 21 - 20 = 1}</li>
* </ol>
* From there we increment the value from lowerLimit until we reach the
* upperLimit, at which point we would define a new bucket. The database
* now contains 2, though incrementSize remains unchanged:<ol>
* <li>{@code upperLimit = (2 * 20) + 1 = 41}</li>
* <li>{@code lowerLimit = 41 - 20 = 21}</li>
* </ol>
* And so on...
* <p/>
* Note, 'value' always (after init) holds the next value to return
*
* @author Steve Ebersole
*/
public class HiLoOptimizer extends AbstractOptimizer {
private static final Logger log = Logger.getLogger( HiLoOptimizer.class );
private static class GenerationState {
private IntegralDataTypeHolder lastSourceValue;
private IntegralDataTypeHolder upperLimit;
private IntegralDataTypeHolder value;
}
/**
* Constructs a HiLoOptimizer
*
* @param returnClass The Java type of the values to be generated
* @param incrementSize The increment size.
*/
public HiLoOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
if ( incrementSize < 1 ) {
throw new HibernateException( "increment size cannot be less than 1" );
}
if ( log.isTraceEnabled() ) {
log.tracev( "Creating hilo optimizer with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() );
}
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
if ( generationState.lastSourceValue == null ) {
// first call, so initialize ourselves. we need to read the database
// value and set up the 'bucket' boundaries
generationState.lastSourceValue = callback.getNextValue();
while ( generationState.lastSourceValue.lt( 1 ) ) {
generationState.lastSourceValue = callback.getNextValue();
}
// upperLimit defines the upper end of the bucket values
generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment();
// initialize value to the low end of the bucket
generationState.value = generationState.upperLimit.copy().subtract( incrementSize );
}
else if ( ! generationState.upperLimit.gt( generationState.value ) ) {
generationState.lastSourceValue = callback.getNextValue();
generationState.upperLimit = generationState.lastSourceValue.copy().multiplyBy( incrementSize ).increment();
}
return generationState.value.makeValueThenIncrement();
}
private GenerationState noTenantState;
private Map<String,GenerationState> tenantSpecificState;
private GenerationState locateGenerationState(String tenantIdentifier) {
if ( tenantIdentifier == null ) {
if ( noTenantState == null ) {
noTenantState = new GenerationState();
}
return noTenantState;
}
else {
GenerationState state;
if ( tenantSpecificState == null ) {
tenantSpecificState = new ConcurrentHashMap<String, GenerationState>();
state = new GenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
else {
state = tenantSpecificState.get( tenantIdentifier );
if ( state == null ) {
state = new GenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
}
return state;
}
}
private GenerationState noTenantGenerationState() {
if ( noTenantState == null ) {
throw new IllegalStateException( "Could not locate previous generation state for no-tenant" );
}
return noTenantState;
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return noTenantGenerationState().lastSourceValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
public IntegralDataTypeHolder getLastValue() {
return noTenantGenerationState().value.copy().decrement();
}
/**
* Getter for property 'upperLimit'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'upperLimit'.
*/
public IntegralDataTypeHolder getHiValue() {
return noTenantGenerationState().upperLimit;
}
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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;
/**
* Marker interface for optimizer which wish to know the user-specified initial value.
* <p/>
* Used instead of constructor injection since that is already a public understanding and
* because not all optimizers care.
*
* @author Steve Ebersole
*/
public interface InitialValueAwareOptimizer {
/**
* Reports the user specified initial value to the optimizer.
* <p/>
* <tt>-1</tt> is used to indicate that the user did not specify.
*
* @param initialValue The initial value specified by the user, or <tt>-1</tt> to indicate that the
* user did not specify.
*/
public void injectInitialValue(long initialValue);
}

View File

@ -0,0 +1,148 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Slight variation from {@link HiLoOptimizer}, maintaining compatibility with the values generated by the
* legacy Hibernate hilo based generators.
*
* @author Steve Ebersole
*/
public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer {
private static final Logger log = Logger.getLogger( LegacyHiLoAlgorithmOptimizer.class );
private final long initialMaxLo;
private static class GenerationState {
private long maxLo;
private long lo;
private IntegralDataTypeHolder hi;
private IntegralDataTypeHolder lastSourceValue;
private IntegralDataTypeHolder value;
}
/**
* Constructs a LegacyHiLoAlgorithmOptimizer
*
* @param returnClass The Java type of the values to be generated
* @param incrementSize The increment size.
*/
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.tracev( "Creating hilo optimizer (legacy) with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() );
}
initialMaxLo = incrementSize;
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
if ( generationState.lo > generationState.maxLo ) {
generationState.lastSourceValue = callback.getNextValue();
generationState.lo = generationState.lastSourceValue.eq( 0 ) ? 1 : 0;
generationState.hi = generationState.lastSourceValue.copy().multiplyBy( generationState.maxLo + 1 );
}
generationState.value = generationState.hi.copy().add( generationState.lo++ );
return generationState.value.makeValue();
}
private GenerationState noTenantState;
private Map<String,GenerationState> tenantSpecificState;
private GenerationState locateGenerationState(String tenantIdentifier) {
if ( tenantIdentifier == null ) {
if ( noTenantState == null ) {
noTenantState = createGenerationState();
}
return noTenantState;
}
else {
GenerationState state;
if ( tenantSpecificState == null ) {
tenantSpecificState = new ConcurrentHashMap<String, GenerationState>();
state = createGenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
else {
state = tenantSpecificState.get( tenantIdentifier );
if ( state == null ) {
state = createGenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
}
return state;
}
}
private GenerationState createGenerationState() {
final GenerationState state = new GenerationState();
state.maxLo = initialMaxLo;
state.lo = initialMaxLo + 1;
return state;
}
private GenerationState noTenantGenerationState() {
if ( noTenantState == null ) {
throw new IllegalStateException( "Could not locate previous generation state for no-tenant" );
}
return noTenantState;
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return noTenantGenerationState().lastSourceValue.copy();
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public IntegralDataTypeHolder getLastValue() {
return noTenantGenerationState().value;
}
}

View File

@ -0,0 +1,68 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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;
/**
* An optimizer that performs no optimization. The database is hit for
* every request.
*/
public class NoopOptimizer extends AbstractOptimizer {
private IntegralDataTypeHolder lastSourceValue;
/**
* Constructs a NoopOptimizer
*
* @param returnClass The Java type of the values to be generated
* @param incrementSize The increment size.
*/
public NoopOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
}
@Override
public Serializable generate(AccessCallback callback) {
// IMPL NOTE : it is incredibly important that the method-local variable be used here to
// avoid concurrency issues.
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 1 ) ) {
value = callback.getNextValue();
}
lastSourceValue = value;
return value.makeValue();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
}

View File

@ -23,16 +23,12 @@
*/
package org.hibernate.id.enhanced;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import org.jboss.logging.Logger;
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.
@ -40,95 +36,24 @@ import org.hibernate.internal.util.StringHelper;
* @author Steve Ebersole
*/
public class OptimizerFactory {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
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 );
private final String externalName;
private final Class<? extends Optimizer> optimizerClass;
private final boolean isPooled;
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", externalName );
return null;
}
}
}
/**
* Marker interface for optimizer which wish to know the user-specified initial value.
* <p/>
* Used instead of constructor injection since that is already a public understanding and
* because not all optimizers care.
* Does the given optimizer name represent a pooled strategy?
*
* @param optimizerName The name of the optimizer
*
* @return {@code true} indicates the optimizer is a pooled strategy.
*/
public static interface InitialValueAwareOptimizer {
/**
* Reports the user specified initial value to the optimizer.
* <p/>
* <tt>-1</tt> is used to indicate that the user did not specify.
*
* @param initialValue The initial value specified by the user, or <tt>-1</tt> to indicate that the
* user did not specify.
*/
public void injectInitialValue(long initialValue);
}
public static boolean isPooledOptimizer(String type) {
final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( type );
public static boolean isPooledOptimizer(String optimizerName) {
final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( optimizerName );
return standardDescriptor != null && standardDescriptor.isPooled();
}
private static Class[] CTOR_SIG = new Class[] { Class.class, int.class };
private static final Class[] CTOR_SIG = new Class[] { Class.class, int.class };
/**
* Builds an optimizer
@ -161,11 +86,11 @@ public class OptimizerFactory {
}
try {
Constructor ctor = optimizerClass.getConstructor( CTOR_SIG );
return ( Optimizer ) ctor.newInstance( returnClass, Integer.valueOf( incrementSize ) );
final Constructor ctor = optimizerClass.getConstructor( CTOR_SIG );
return (Optimizer) ctor.newInstance( returnClass, Integer.valueOf( incrementSize ) );
}
catch( Throwable ignore ) {
LOG.unableToInstantiateOptimizer( type );
LOG.unableToInstantiateOptimizer( type );
}
return buildFallbackOptimizer( returnClass, incrementSize );
@ -195,349 +120,8 @@ public class OptimizerFactory {
}
/**
* Common support for optimizer implementations.
*/
public static abstract class OptimizerSupport implements Optimizer {
protected final Class returnClass;
protected final int incrementSize;
/**
* Construct an optimizer
*
* @param returnClass The expected id class.
* @param incrementSize The increment size
*/
protected OptimizerSupport(Class returnClass, int incrementSize) {
if ( returnClass == null ) {
throw new HibernateException( "return class is required" );
}
this.returnClass = returnClass;
this.incrementSize = incrementSize;
}
/**
* Getter for property 'returnClass'. This is the Java
* class which is used to represent the id (e.g. {@link java.lang.Long}).
*
* @return Value for property 'returnClass'.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public final Class getReturnClass() {
return returnClass;
}
@Override
public final int getIncrementSize() {
return incrementSize;
}
}
/**
* An optimizer that performs no optimization. The database is hit for
* every request.
*/
public static class NoopOptimizer extends OptimizerSupport {
private IntegralDataTypeHolder lastSourceValue;
public NoopOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
}
@Override
public Serializable generate(AccessCallback callback) {
// IMPL NOTE : it is incredibly important that the method-local variable be used here to
// avoid concurrency issues.
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 1 ) ) {
value = callback.getNextValue();
}
lastSourceValue = value;
return value.makeValue();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
}
/**
* Optimizer which applies a 'hilo' algorithm in memory to achieve
* optimization.
* <p/>
* A 'hilo' algorithm is simply a means for a single value stored in the
* database to represent a "bucket" of possible, contiguous values. The
* database value identifies which particular bucket we are on.
* <p/>
* This database value must be paired with another value that defines the
* size of the bucket; the number of possible values available.
* The {@link #getIncrementSize() incrementSize} serves this purpose. The
* naming here is meant more for consistency in that this value serves the
* same purpose as the increment supplied to the {@link PooledOptimizer}.
* <p/>
* The general algorithms used to determine the bucket are:<ol>
* <li>{@code upperLimit = (databaseValue * incrementSize) + 1}</li>
* <li>{@code lowerLimit = upperLimit - incrementSize}</li>
* </ol>
* As an example, consider a case with incrementSize of 20. Initially the
* database holds 1:<ol>
* <li>{@code upperLimit = (1 * 20) + 1 = 21}</li>
* <li>{@code lowerLimit = 21 - 20 = 1}</li>
* </ol>
* From there we increment the value from lowerLimit until we reach the
* upperLimit, at which point we would define a new bucket. The database
* now contains 2, though incrementSize remains unchanged:<ol>
* <li>{@code upperLimit = (2 * 20) + 1 = 41}</li>
* <li>{@code lowerLimit = 41 - 20 = 21}</li>
* </ol>
* And so on...
* <p/>
* Note, 'value' always (after init) holds the next value to return
*/
public static class HiLoOptimizer extends OptimizerSupport {
private IntegralDataTypeHolder lastSourceValue;
private IntegralDataTypeHolder upperLimit;
private IntegralDataTypeHolder value;
public HiLoOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
if ( incrementSize < 1 )
throw new HibernateException( "increment size cannot be less than 1" );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Creating hilo optimizer with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() );
}
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue == null ) {
// first call, so initialize ourselves. we need to read the database
// value and set up the 'bucket' boundaries
lastSourceValue = callback.getNextValue();
while ( lastSourceValue.lt( 1 ) ) {
lastSourceValue = callback.getNextValue();
}
// upperLimit defines the upper end of the bucket values
upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment();
// initialize value to the low end of the bucket
value = upperLimit.copy().subtract( incrementSize );
}
else if ( ! upperLimit.gt( value ) ) {
lastSourceValue = callback.getNextValue();
upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment();
}
return value.makeValueThenIncrement();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@Override
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.copy().decrement();
}
/**
* Getter for property 'upperLimit'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'upperLimit'.
*/
public IntegralDataTypeHolder getHiValue() {
return upperLimit;
}
}
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.tracev( "Creating hilo optimizer (legacy) with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() );
}
maxLo = incrementSize;
lo = maxLo+1;
}
@Override
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();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue.copy();
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return false;
}
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
@SuppressWarnings( {"UnusedDeclaration"})
public IntegralDataTypeHolder getLastValue() {
return value;
}
}
/**
* Optimizer which uses a pool of values, storing the next low value of the
* range in the database.
* <p/>
* Note that this optimizer works essentially the same as the
* {@link HiLoOptimizer} except that here the bucket ranges are actually
* encoded into the database structures.
* <p/>
* Note if you prefer that the database value be interpreted as the bottom end of our current range,
* then use the {@link PooledLoOptimizer} strategy
*/
public static class PooledOptimizer extends OptimizerSupport implements InitialValueAwareOptimizer {
private IntegralDataTypeHolder hiValue;
private IntegralDataTypeHolder value;
private long initialValue = -1;
public PooledOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
if ( incrementSize < 1 ) {
throw new HibernateException( "increment size cannot be less than 1" );
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Creating pooled optimizer with [incrementSize={0}; returnClass={1}]", incrementSize, returnClass.getName() );
}
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue == null ) {
value = callback.getNextValue();
// unfortunately not really safe to normalize this
// to 1 as an initial value like we do the others
// because we would not be able to control this if
// we are using a sequence...
if (value.lt(1)) LOG.pooledOptimizerReportedInitialValue(value);
// the call to obtain next-value just gave us the initialValue
if ( ( initialValue == -1 && value.lt( incrementSize ) ) || value.eq( initialValue ) ) {
hiValue = callback.getNextValue();
}
else {
hiValue = value;
value = hiValue.copy().subtract( incrementSize );
}
}
else if ( ! hiValue.gt( value ) ) {
hiValue = callback.getNextValue();
value = hiValue.copy().subtract( incrementSize );
}
return value.makeValueThenIncrement();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return hiValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return true;
}
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
public IntegralDataTypeHolder getLastValue() {
return value.copy().decrement();
}
@Override
public void injectInitialValue(long initialValue) {
this.initialValue = initialValue;
}
}
public static class PooledLoOptimizer extends OptimizerSupport {
private IntegralDataTypeHolder lastSourceValue; // last value read from db source
private IntegralDataTypeHolder value; // the current generator value
public PooledLoOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
if ( incrementSize < 1 ) {
throw new HibernateException( "increment size cannot be less than 1" );
}
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Creating pooled optimizer (lo) with [incrementSize={0}; returnClass=]", incrementSize, returnClass.getName() );
}
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue == null || ! value.lt( lastSourceValue.copy().add( incrementSize ) ) ) {
lastSourceValue = callback.getNextValue();
value = lastSourceValue.copy();
// handle cases where initial-value is less that one (hsqldb for instance).
while ( value.lt( 1 ) ) {
value.increment();
}
}
return value.makeValueThenIncrement();
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return true;
}
}
/**
* Deprecated!
*
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#NONE}
*/
@Deprecated
@ -545,6 +129,8 @@ public class OptimizerFactory {
public static final String NONE = StandardOptimizerDescriptor.NONE.getExternalName();
/**
* Deprecated!
*
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#HILO}
*/
@Deprecated
@ -552,6 +138,8 @@ public class OptimizerFactory {
public static final String HILO = StandardOptimizerDescriptor.HILO.getExternalName();
/**
* Deprecated!
*
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#LEGACY_HILO}
*/
@Deprecated
@ -559,6 +147,8 @@ public class OptimizerFactory {
public static final String LEGACY_HILO = "legacy-hilo";
/**
* Deprecated!
*
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#POOLED}
*/
@Deprecated
@ -566,9 +156,14 @@ public class OptimizerFactory {
public static final String POOL = "pooled";
/**
* Deprecated!
*
* @deprecated Use {@link StandardOptimizerDescriptor#getExternalName()} via {@link StandardOptimizerDescriptor#POOLED_LO}
*/
@Deprecated
@SuppressWarnings( {"UnusedDeclaration"})
public static final String POOL_LO = "pooled-lo";
private OptimizerFactory() {
}
}

View File

@ -0,0 +1,129 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Variation of {@link PooledOptimizer} which interprets the incoming database value as the lo value, rather than
* the hi value.
*
* @author Steve Ebersole
*
* @see PooledOptimizer
*/
public class PooledLoOptimizer extends AbstractOptimizer {
private static final Logger log = Logger.getLogger( PooledLoOptimizer.class );
private static class GenerationState {
// last value read from db source
private IntegralDataTypeHolder lastSourceValue;
// the current generator value
private IntegralDataTypeHolder value;
}
/**
* Constructs a PooledLoOptimizer.
*
* @param returnClass The Java type of the values to be generated
* @param incrementSize The increment size.
*/
public PooledLoOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
if ( incrementSize < 1 ) {
throw new HibernateException( "increment size cannot be less than 1" );
}
if ( log.isTraceEnabled() ) {
log.tracev( "Creating pooled optimizer (lo) with [incrementSize={0}; returnClass=]", incrementSize, returnClass.getName() );
}
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
if ( generationState.lastSourceValue == null
|| ! generationState.value.lt( generationState.lastSourceValue.copy().add( incrementSize ) ) ) {
generationState.lastSourceValue = callback.getNextValue();
generationState.value = generationState.lastSourceValue.copy();
// handle cases where initial-value is less that one (hsqldb for instance).
while ( generationState.value.lt( 1 ) ) {
generationState.value.increment();
}
}
return generationState.value.makeValueThenIncrement();
}
private GenerationState noTenantState;
private Map<String,GenerationState> tenantSpecificState;
private GenerationState locateGenerationState(String tenantIdentifier) {
if ( tenantIdentifier == null ) {
if ( noTenantState == null ) {
noTenantState = new GenerationState();
}
return noTenantState;
}
else {
GenerationState state;
if ( tenantSpecificState == null ) {
tenantSpecificState = new ConcurrentHashMap<String, GenerationState>();
state = new GenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
else {
state = tenantSpecificState.get( tenantIdentifier );
if ( state == null ) {
state = new GenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
}
return state;
}
}
private GenerationState noTenantGenerationState() {
if ( noTenantState == null ) {
throw new IllegalStateException( "Could not locate previous generation state for no-tenant" );
}
return noTenantState;
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return noTenantGenerationState().lastSourceValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return true;
}
}

View File

@ -0,0 +1,177 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.internal.CoreMessageLogger;
/**
* Optimizer which uses a pool of values, storing the next low value of the
* range in the database.
* <p/>
* Note that this optimizer works essentially the same as the
* {@link org.hibernate.id.enhanced.HiLoOptimizer} except that here the bucket ranges are actually
* encoded into the database structures.
* <p/>
* Note if you prefer that the database value be interpreted as the bottom end of our current range,
* then use the {@link PooledLoOptimizer} strategy
*
* @author Steve Ebersole
*
* @see PooledLoOptimizer
*/
public class PooledOptimizer extends AbstractOptimizer implements InitialValueAwareOptimizer {
private static final CoreMessageLogger log = Logger.getMessageLogger(
CoreMessageLogger.class,
PooledOptimizer.class.getName()
);
private static class GenerationState {
private IntegralDataTypeHolder hiValue;
private IntegralDataTypeHolder value;
}
private long initialValue = -1;
/**
* Constructs a PooledOptimizer
*
* @param returnClass The Java type of the values to be generated
* @param incrementSize The increment size.
*/
public PooledOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
if ( incrementSize < 1 ) {
throw new HibernateException( "increment size cannot be less than 1" );
}
if ( log.isTraceEnabled() ) {
log.tracev(
"Creating pooled optimizer with [incrementSize={0}; returnClass={1}]",
incrementSize,
returnClass.getName()
);
}
}
@Override
public synchronized Serializable generate(AccessCallback callback) {
final GenerationState generationState = locateGenerationState( callback.getTenantIdentifier() );
if ( generationState.hiValue == null ) {
generationState.value = callback.getNextValue();
// unfortunately not really safe to normalize this
// to 1 as an initial value like we do the others
// because we would not be able to control this if
// we are using a sequence...
if ( generationState.value.lt( 1 ) ) {
log.pooledOptimizerReportedInitialValue( generationState.value );
}
// the call to obtain next-value just gave us the initialValue
if ( ( initialValue == -1
&& generationState.value.lt( incrementSize ) )
|| generationState.value.eq( initialValue ) ) {
generationState.hiValue = callback.getNextValue();
}
else {
generationState.hiValue = generationState.value;
generationState.value = generationState.hiValue.copy().subtract( incrementSize );
}
}
else if ( ! generationState.hiValue.gt( generationState.value ) ) {
generationState.hiValue = callback.getNextValue();
generationState.value = generationState.hiValue.copy().subtract( incrementSize );
}
return generationState.value.makeValueThenIncrement();
}
private GenerationState noTenantState;
private Map<String,GenerationState> tenantSpecificState;
private GenerationState locateGenerationState(String tenantIdentifier) {
if ( tenantIdentifier == null ) {
if ( noTenantState == null ) {
noTenantState = new GenerationState();
}
return noTenantState;
}
else {
GenerationState state;
if ( tenantSpecificState == null ) {
tenantSpecificState = new ConcurrentHashMap<String, GenerationState>();
state = new GenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
else {
state = tenantSpecificState.get( tenantIdentifier );
if ( state == null ) {
state = new GenerationState();
tenantSpecificState.put( tenantIdentifier, state );
}
}
return state;
}
}
private GenerationState noTenantGenerationState() {
if ( noTenantState == null ) {
throw new IllegalStateException( "Could not locate previous generation state for no-tenant" );
}
return noTenantState;
}
@Override
public IntegralDataTypeHolder getLastSourceValue() {
return noTenantGenerationState().hiValue;
}
@Override
public boolean applyIncrementSizeToSourceValues() {
return true;
}
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
public IntegralDataTypeHolder getLastValue() {
return noTenantGenerationState().value.copy().decrement();
}
@Override
public void injectInitialValue(long initialValue) {
this.initialValue = initialValue;
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008, 2013, 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,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id.enhanced;
import java.sql.PreparedStatement;
@ -42,8 +41,10 @@ import org.hibernate.internal.CoreMessageLogger;
* @author Steve Ebersole
*/
public class SequenceStructure implements DatabaseStructure {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SequenceStructure.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
SequenceStructure.class.getName()
);
private final String sequenceName;
private final int initialValue;
@ -93,12 +94,12 @@ public class SequenceStructure implements DatabaseStructure {
public IntegralDataTypeHolder getNextValue() {
accessCounter++;
try {
PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql );
final PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql );
try {
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
final ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
try {
rs.next();
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
value.initialize( rs, 1 );
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Sequence value obtained: %s", value.makeValue() );
@ -127,6 +128,11 @@ public class SequenceStructure implements DatabaseStructure {
);
}
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
};
}
@ -137,7 +143,7 @@ public class SequenceStructure implements DatabaseStructure {
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
final int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize );
}

View File

@ -81,8 +81,9 @@ import org.hibernate.type.Type;
* <td><i>depends on defined increment size</i></td>
* <td>Allows explicit definition of which optimization strategy to use</td>
* </tr>
* <tr>
* <td>{@link #FORCE_TBL_PARAM}</td>
* <td><b><i>false<i/></b></td>
* <td><b><i>false</i></b></td>
* <td>Allows explicit definition of which optimization strategy to use</td>
* </tr>
* </table>
@ -107,32 +108,85 @@ import org.hibernate.type.Type;
public class SequenceStyleGenerator
implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, Configurable {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
SequenceStyleGenerator.class.getName()
);
// general purpose parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Indicates the name of the sequence (or table) to use. The default value is {@link #DEF_SEQUENCE_NAME},
* although {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY} effects the default as well.
*/
public static final String SEQUENCE_PARAM = "sequence_name";
/**
* The default value for {@link #SEQUENCE_PARAM}, in the absence of any {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY}
* setting.
*/
public static final String DEF_SEQUENCE_NAME = "hibernate_sequence";
/**
* Indicates the initial value to use. The default value is {@link #DEFAULT_INITIAL_VALUE}
*/
public static final String INITIAL_PARAM = "initial_value";
/**
* The default value for {@link #INITIAL_PARAM}
*/
public static final int DEFAULT_INITIAL_VALUE = 1;
/**
* Indicates the increment size to use. The default value is {@link #DEFAULT_INCREMENT_SIZE}
*/
public static final String INCREMENT_PARAM = "increment_size";
/**
* The default value for {@link #INCREMENT_PARAM}
*/
public static final int DEFAULT_INCREMENT_SIZE = 1;
/**
* Used to create dedicated sequence for each entity based on the entity name. Sequence suffix can be
* controlled with {@link #CONFIG_SEQUENCE_PER_ENTITY_SUFFIX} option.
*/
public static final String CONFIG_PREFER_SEQUENCE_PER_ENTITY = "prefer_sequence_per_entity";
/**
* Indicates the suffix to use in naming the identifier sequence/table name, by appending the suffix to
* the name of the entity. Used in conjunction with {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY}.
*/
public static final String CONFIG_SEQUENCE_PER_ENTITY_SUFFIX = "sequence_per_entity_suffix";
/**
* The default value for {@link #CONFIG_SEQUENCE_PER_ENTITY_SUFFIX}
*/
public static final String DEF_SEQUENCE_SUFFIX = "_SEQ";
/**
* Indicates the optimizer to use, either naming a {@link Optimizer} implementation class or naming
* a {@link StandardOptimizerDescriptor} by name
*/
public static final String OPT_PARAM = "optimizer";
/**
* A flag to force using a table as the underlying structure rather than a sequence.
*/
public static final String FORCE_TBL_PARAM = "force_table_use";
public static final String CONFIG_PREFER_SEQUENCE_PER_ENTITY = "prefer_sequence_per_entity";
public static final String CONFIG_SEQUENCE_PER_ENTITY_SUFFIX = "sequence_per_entity_suffix";
public static final String DEF_SEQUENCE_SUFFIX = "_SEQ";
// table-specific parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Indicates the name of the column holding the identifier values. The default value is {@link #DEF_VALUE_COLUMN}
*/
public static final String VALUE_COLUMN_PARAM = "value_column";
/**
* The default value for {@link #VALUE_COLUMN_PARAM}
*/
public static final String DEF_VALUE_COLUMN = "next_val";
@ -169,6 +223,7 @@ public class SequenceStyleGenerator
}
// Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
@ -187,7 +242,7 @@ public class SequenceStyleGenerator
if ( dialect.supportsSequences() && !forceTableUse ) {
if ( !dialect.supportsPooledSequences() && OptimizerFactory.isPooledOptimizer( optimizationStrategy ) ) {
forceTableUse = true;
LOG.forcingTableUse();
LOG.forcingTableUse();
}
}
@ -220,27 +275,26 @@ public class SequenceStyleGenerator
* @return The sequence name
*/
protected String determineSequenceName(Properties params, Dialect dialect) {
String sequencePerEntitySuffix = ConfigurationHelper.getString( CONFIG_SEQUENCE_PER_ENTITY_SUFFIX, params, DEF_SEQUENCE_SUFFIX );
final String sequencePerEntitySuffix = ConfigurationHelper.getString( CONFIG_SEQUENCE_PER_ENTITY_SUFFIX, params, DEF_SEQUENCE_SUFFIX );
// JPA_ENTITY_NAME value honors <class ... entity-name="..."> (HBM) and @Entity#name (JPA) overrides.
String sequenceName = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEQUENCE_PER_ENTITY, params, false )
? params.getProperty( JPA_ENTITY_NAME ) + sequencePerEntitySuffix
: DEF_SEQUENCE_NAME;
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
sequenceName = ConfigurationHelper.getString( SEQUENCE_PARAM, params, sequenceName );
if ( sequenceName.indexOf( '.' ) < 0 ) {
sequenceName = normalizer.normalizeIdentifierQuoting( sequenceName );
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
final String schemaName = params.getProperty( SCHEMA );
final String catalogName = params.getProperty( CATALOG );
sequenceName = Table.qualify(
dialect.quote( catalogName ),
dialect.quote( schemaName ),
dialect.quote( sequenceName )
);
}
else {
// if already qualified there is not much we can do in a portable manner so we pass it
// through and assume the user has set up the name correctly.
}
// if already qualified there is not much we can do in a portable manner so we pass it
// through and assume the user has set up the name correctly.
return sequenceName;
}
@ -256,8 +310,8 @@ public class SequenceStyleGenerator
* @return The value column name
*/
protected String determineValueColumnName(Properties params, Dialect dialect) {
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
return dialect.quote( normalizer.normalizeIdentifierQuoting( name ) );
}
@ -300,11 +354,11 @@ public class SequenceStyleGenerator
protected String determineOptimizationStrategy(Properties params, int incrementSize) {
// 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.StandardOptimizerDescriptor.POOLED_LO.getExternalName()
: OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName();
String defaultOptimizerStrategy = incrementSize <= 1
? OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName()
final String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false )
? StandardOptimizerDescriptor.POOLED_LO.getExternalName()
: StandardOptimizerDescriptor.POOLED.getExternalName();
final String defaultOptimizerStrategy = incrementSize <= 1
? StandardOptimizerDescriptor.NONE.getExternalName()
: defaultPooledOptimizerStrategy;
return ConfigurationHelper.getString( OPT_PARAM, params, defaultOptimizerStrategy );
}
@ -318,10 +372,9 @@ public class SequenceStyleGenerator
* @return The adjusted increment size.
*/
protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
if ( incrementSize > 1
&& OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName().equals( optimizationStrategy ) ) {
LOG.honoringOptimizerSetting(
OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName(),
if ( incrementSize > 1 && StandardOptimizerDescriptor.NONE.getExternalName().equals( optimizationStrategy ) ) {
LOG.honoringOptimizerSetting(
StandardOptimizerDescriptor.NONE.getExternalName(),
INCREMENT_PARAM,
incrementSize
);
@ -351,12 +404,12 @@ public class SequenceStyleGenerator
String sequenceName,
int initialValue,
int incrementSize) {
boolean useSequence = dialect.supportsSequences() && !forceTableUse;
final boolean useSequence = dialect.supportsSequences() && !forceTableUse;
if ( useSequence ) {
return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize, type.getReturnedClass() );
}
else {
String valueColumnName = determineValueColumnName( params, dialect );
final String valueColumnName = determineValueColumnName( params, dialect );
return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize, type.getReturnedClass() );
}
}
@ -395,7 +448,7 @@ public class SequenceStyleGenerator
// it does, as long as
// 1) there is no (non-noop) optimizer in use
// 2) the underlying structure is a sequence
return OptimizerFactory.NoopOptimizer.class.isInstance( getOptimizer() )
return NoopOptimizer.class.isInstance( getOptimizer() )
&& getDatabaseStructure().isPhysicalSequence();
}

View File

@ -0,0 +1,121 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.jboss.logging.Logger;
import org.hibernate.internal.util.StringHelper;
/**
* Enumeration of the standard Hibernate id generation optimizers.
*
* @author Steve Ebersole
*/
public enum StandardOptimizerDescriptor {
/**
* Describes the optimizer for no optimization
*/
NONE( "none", NoopOptimizer.class ),
/**
* Describes the optimizer for using a custom "hilo" algorithm optimization
*/
HILO( "hilo", HiLoOptimizer.class ),
/**
* Describes the optimizer for using a custom "hilo" algorithm optimization, following the legacy
* Hibernate hilo algorithm
*/
LEGACY_HILO( "legacy-hilo", LegacyHiLoAlgorithmOptimizer.class ),
/**
* Describes the optimizer for use with tables/sequences that store the chunk information. Here, specifically the
* hi value is stored in the database.
*/
POOLED( "pooled", PooledOptimizer.class, true ),
/**
* Describes the optimizer for use with tables/sequences that store the chunk information. Here, specifically the
* lo value is stored in the database.
*/
POOLED_LO( "pooled-lo", PooledLoOptimizer.class, true );
private static final Logger log = Logger.getLogger( StandardOptimizerDescriptor.class );
private final String externalName;
private final Class<? extends Optimizer> optimizerClass;
private final boolean isPooled;
private StandardOptimizerDescriptor(String externalName, Class<? extends Optimizer> optimizerClass) {
this( externalName, optimizerClass, false );
}
private 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;
}
/**
* Interpret the incoming external name into the appropriate enum value
*
* @param externalName The external name
*
* @return The corresponding enum value; if no external name is supplied, {@link #NONE} is returned; if an
* unrecognized external name is supplied, {@code null} is returned
*/
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", externalName );
return null;
}
}
}

View File

@ -131,32 +131,95 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public class TableGenerator implements PersistentIdentifierGenerator, Configurable {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
TableGenerator.class.getName()
);
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, TableGenerator.class.getName());
/**
* By default (in the absence of a {@link #SEGMENT_VALUE_PARAM} setting) we use a single row for all
* generators. This setting can be used to change that to instead default to using a row for each entity name.
*/
public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "prefer_entity_table_as_segment_value";
/**
* Configures the name of the table to use. The default value is {@link #DEF_TABLE}
*/
public static final String TABLE_PARAM = "table_name";
/**
* The default {@link #TABLE_PARAM} value
*/
public static final String DEF_TABLE = "hibernate_sequences";
/**
* The name of column which holds the sequence value. The default value is {@link #DEF_VALUE_COLUMN}
*/
public static final String VALUE_COLUMN_PARAM = "value_column_name";
/**
* The default {@link #VALUE_COLUMN_PARAM} value
*/
public static final String DEF_VALUE_COLUMN = "next_val";
/**
* The name of the column which holds the segment key. The segment defines the different buckets (segments)
* of values currently tracked in the table. The default value is {@link #DEF_SEGMENT_COLUMN}
*/
public static final String SEGMENT_COLUMN_PARAM = "segment_column_name";
/**
* The default {@link #SEGMENT_COLUMN_PARAM} value
*/
public static final String DEF_SEGMENT_COLUMN = "sequence_name";
/**
* The value indicating which segment is used by this generator, as indicated by the actual value stored in the
* column indicated by {@link #SEGMENT_COLUMN_PARAM}. The default value for setting is {@link #DEF_SEGMENT_VALUE},
* although {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} effects the default as well.
*/
public static final String SEGMENT_VALUE_PARAM = "segment_value";
/**
* The default {@link #SEGMENT_VALUE_PARAM} value, unless {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} is specified
*/
public static final String DEF_SEGMENT_VALUE = "default";
/**
* Indicates the length of the column defined by {@link #SEGMENT_COLUMN_PARAM}. Used in schema export. The
* default value is {@link #DEF_SEGMENT_LENGTH}
*/
public static final String SEGMENT_LENGTH_PARAM = "segment_value_length";
/**
* The default {@link #SEGMENT_LENGTH_PARAM} value
*/
public static final int DEF_SEGMENT_LENGTH = 255;
/**
* Indicates the initial value to use. The default value is {@link #DEFAULT_INITIAL_VALUE}
*/
public static final String INITIAL_PARAM = "initial_value";
/**
* The default {@link #INITIAL_PARAM} value
*/
public static final int DEFAULT_INITIAL_VALUE = 1;
/**
* Indicates the increment size to use. The default value is {@link #DEFAULT_INCREMENT_SIZE}
*/
public static final String INCREMENT_PARAM = "increment_size";
/**
* The default {@link #INCREMENT_PARAM} value
*/
public static final int DEFAULT_INCREMENT_SIZE = 1;
/**
* Indicates the optimizer to use, either naming a {@link Optimizer} implementation class or by naming
* a {@link StandardOptimizerDescriptor} by name
*/
public static final String OPT_PARAM = "optimizer";
@ -177,7 +240,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
private String updateQuery;
private Optimizer optimizer;
private long accessCount = 0;
private long accessCount;
@Override
public Object generatorKey() {
@ -304,11 +367,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.StandardOptimizerDescriptor.POOLED_LO.getExternalName()
: OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName();
final String defaultPooledOptimizerStrategy = ConfigurationHelper.getBoolean( Environment.PREFER_POOLED_VALUES_LO, params, false )
? StandardOptimizerDescriptor.POOLED_LO.getExternalName()
: StandardOptimizerDescriptor.POOLED.getExternalName();
final String defaultOptimizerStrategy = incrementSize <= 1
? OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName()
? StandardOptimizerDescriptor.NONE.getExternalName()
: defaultPooledOptimizerStrategy;
final String optimizationStrategy = ConfigurationHelper.getString( OPT_PARAM, params, defaultOptimizerStrategy );
optimizer = OptimizerFactory.buildOptimizer(
@ -331,23 +394,22 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
*/
protected String determineGeneratorTableName(Properties params, Dialect dialect) {
String name = ConfigurationHelper.getString( TABLE_PARAM, params, DEF_TABLE );
boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0;
final boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0;
if ( isGivenNameUnqualified ) {
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
name = normalizer.normalizeIdentifierQuoting( name );
// if the given name is un-qualified we may neen to qualify it
String schemaName = normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) );
String catalogName = normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) );
final String schemaName = normalizer.normalizeIdentifierQuoting( params.getProperty( SCHEMA ) );
final String catalogName = normalizer.normalizeIdentifierQuoting( params.getProperty( CATALOG ) );
name = Table.qualify(
dialect.quote( catalogName ),
dialect.quote( schemaName ),
dialect.quote( name)
);
}
else {
// if already qualified there is not much we can do in a portable manner so we pass it
// through and assume the user has set up the name correctly.
}
// if already qualified there is not much we can do in a portable manner so we pass it
// through and assume the user has set up the name correctly.
return name;
}
@ -363,8 +425,8 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @return The name of the segment column
*/
protected String determineSegmentColumnName(Properties params, Dialect dialect) {
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
final String name = ConfigurationHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
return dialect.quote( normalizer.normalizeIdentifierQuoting( name ) );
}
@ -379,8 +441,8 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @return The name of the value column
*/
protected String determineValueColumnName(Properties params, Dialect dialect) {
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
final ObjectNameNormalizer normalizer = (ObjectNameNormalizer) params.get( IDENTIFIER_NORMALIZER );
final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
return dialect.quote( normalizer.normalizeIdentifierQuoting( name ) );
}
@ -409,9 +471,9 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
* @return The default segment value to use.
*/
protected String determineDefaultSegmentValue(Properties params) {
boolean preferSegmentPerEntity = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false );
String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE;
LOG.usingDefaultIdGeneratorSegmentValue(tableName, segmentColumnName, defaultToUse);
final boolean preferSegmentPerEntity = ConfigurationHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false );
final String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE;
LOG.usingDefaultIdGeneratorSegmentValue( tableName, segmentColumnName, defaultToUse );
return defaultToUse;
}
@ -438,12 +500,12 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
protected String buildSelectQuery(Dialect dialect) {
final String alias = "tbl";
String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
final String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
" from " + tableName + ' ' + alias +
" where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
final LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
lockOptions.setAliasSpecificLockMode( alias, LockMode.PESSIMISTIC_WRITE );
Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
final Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
return dialect.applyLocksToSql( query, lockOptions, updateTargetColumnsMap );
}
@ -472,19 +534,27 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
new AbstractReturningWork<IntegralDataTypeHolder>() {
@Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder(
identifierType.getReturnedClass()
);
int rows;
do {
statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() );
statementLogger.logStatement(
selectQuery,
FormatStyle.BASIC.getFormatter()
);
PreparedStatement selectPS = connection.prepareStatement( selectQuery );
try {
selectPS.setString( 1, segmentValue );
ResultSet selectRS = selectPS.executeQuery();
final ResultSet selectRS = selectPS.executeQuery();
if ( !selectRS.next() ) {
value.initialize( initialValue );
PreparedStatement insertPS = null;
try {
statementLogger.logStatement( insertQuery, FormatStyle.BASIC.getFormatter() );
statementLogger.logStatement(
insertQuery,
FormatStyle.BASIC.getFormatter()
);
insertPS = connection.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
value.bind( insertPS, 2 );
@ -501,16 +571,19 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
}
selectRS.close();
}
catch ( SQLException e ) {
LOG.unableToReadOrInitHiValue(e);
catch (SQLException e) {
LOG.unableToReadOrInitHiValue( e );
throw e;
}
finally {
selectPS.close();
}
statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement updatePS = connection.prepareStatement( updateQuery );
statementLogger.logStatement(
updateQuery,
FormatStyle.BASIC.getFormatter()
);
final PreparedStatement updatePS = connection.prepareStatement( updateQuery );
try {
final IntegralDataTypeHolder updateValue = value.copy();
if ( optimizer.applyIncrementSizeToSourceValues() ) {
@ -524,8 +597,8 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
updatePS.setString( 3, segmentValue );
rows = updatePS.executeUpdate();
}
catch ( SQLException e ) {
LOG.unableToUpdateQueryHiValue(tableName, e);
catch (SQLException e) {
LOG.unableToUpdateQueryHiValue( tableName, e );
throw e;
}
finally {
@ -542,6 +615,11 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
true
);
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
}
);
}
@ -549,23 +627,10 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
new StringBuilder()
.append( dialect.getCreateTableString() )
.append( ' ' )
.append( tableName )
.append( " ( " )
.append( segmentColumnName )
.append( ' ' )
.append( dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) )
.append( " not null " )
.append( ", " )
.append( valueColumnName )
.append( ' ' )
.append( dialect.getTypeName( Types.BIGINT ) )
.append( ", primary key ( " )
.append( segmentColumnName )
.append( " ) ) " )
.toString()
dialect.getCreateTableString() + ' ' + tableName + " ( "
+ segmentColumnName + ' ' + dialect.getTypeName( Types.VARCHAR, segmentValueLength, 0, 0 ) + " not null "
+ ", " + valueColumnName + ' ' + dialect.getTypeName( Types.BIGINT )
+ ", primary key ( " + segmentColumnName + " ) ) "
};
}

View File

@ -50,8 +50,10 @@ import org.hibernate.jdbc.AbstractReturningWork;
* @author Steve Ebersole
*/
public class TableStructure implements DatabaseStructure {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, TableStructure.class.getName());
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
TableStructure.class.getName()
);
private final String tableName;
private final String valueColumnName;
@ -125,22 +127,24 @@ public class TableStructure implements DatabaseStructure {
.getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder(
numberType
);
int rows;
do {
statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement selectStatement = connection.prepareStatement( selectQuery );
try {
ResultSet selectRS = selectStatement.executeQuery();
final ResultSet selectRS = selectStatement.executeQuery();
if ( !selectRS.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName;
final String err = "could not read a hi value - you need to populate the table: " + tableName;
LOG.error( err );
throw new IdentifierGenerationException( err );
}
value.initialize( selectRS, 1 );
selectRS.close();
}
catch ( SQLException sqle ) {
catch (SQLException sqle) {
LOG.error( "could not read a hi value", sqle );
throw sqle;
}
@ -149,7 +153,7 @@ public class TableStructure implements DatabaseStructure {
}
statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement updatePS = connection.prepareStatement( updateQuery );
final PreparedStatement updatePS = connection.prepareStatement( updateQuery );
try {
final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
final IntegralDataTypeHolder updateValue = value.copy().add( increment );
@ -157,8 +161,8 @@ public class TableStructure implements DatabaseStructure {
value.bind( updatePS, 2 );
rows = updatePS.executeUpdate();
}
catch ( SQLException e ) {
LOG.unableToUpdateQueryHiValue(tableName, e);
catch (SQLException e) {
LOG.unableToUpdateQueryHiValue( tableName, e );
throw e;
}
finally {
@ -174,6 +178,11 @@ public class TableStructure implements DatabaseStructure {
true
);
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
};
}

View File

@ -0,0 +1,5 @@
/**
* Enhanced/improved versions of table and sequence based identifier generators targeting portability and unified
* configuration
*/
package org.hibernate.id.enhanced;

View File

@ -255,23 +255,23 @@ public class OptimizerUnitTest extends BaseUnitTestCase {
}
private static Optimizer buildNoneOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.NONE, initial, increment );
return buildOptimizer( StandardOptimizerDescriptor.NONE, initial, increment );
}
private static Optimizer buildHiloOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.HILO, initial, increment );
return buildOptimizer( StandardOptimizerDescriptor.HILO, initial, increment );
}
private static Optimizer buildPooledOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.POOLED, initial, increment );
return buildOptimizer( StandardOptimizerDescriptor.POOLED, initial, increment );
}
private static Optimizer buildPooledLoOptimizer(long initial, int increment) {
return buildOptimizer( OptimizerFactory.StandardOptimizerDescriptor.POOLED_LO, initial, increment );
return buildOptimizer( StandardOptimizerDescriptor.POOLED_LO, initial, increment );
}
private static Optimizer buildOptimizer(
OptimizerFactory.StandardOptimizerDescriptor descriptor,
StandardOptimizerDescriptor descriptor,
long initial,
int increment) {
return OptimizerFactory.buildOptimizer( descriptor.getExternalName(), Long.class, increment, initial );
@ -319,6 +319,11 @@ public class OptimizerUnitTest extends BaseUnitTestCase {
}
}
@Override
public String getTenantIdentifier() {
return null;
}
private void initValue() {
this.value.initialize( initialValue );
}

View File

@ -63,7 +63,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
}
@ -95,7 +95,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
}
@ -114,7 +114,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
SequenceStyleGenerator generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
// for dialects which do support pooled sequences, we default to pooled+sequence
@ -122,7 +122,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
}
@ -139,7 +139,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
SequenceStyleGenerator generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
}
@ -154,7 +154,7 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
SequenceStyleGenerator generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() );
}
@ -168,36 +168,36 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
// optimizer=none w/ increment > 1 => should honor optimizer
Properties props = buildGeneratorPropertiesBase();
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.NONE.getExternalName() );
props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.NONE.getExternalName() );
props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
SequenceStyleGenerator generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.NoopOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( 1, generator.getOptimizer().getIncrementSize() );
assertEquals( 1, generator.getDatabaseStructure().getIncrementSize() );
// optimizer=hilo w/ increment > 1 => hilo
props = buildGeneratorPropertiesBase();
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.HILO.getExternalName() );
props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.HILO.getExternalName() );
props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( HiLoOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( 20, generator.getOptimizer().getIncrementSize() );
assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() );
// optimizer=pooled w/ increment > 1 => hilo
props = buildGeneratorPropertiesBase();
props.setProperty( SequenceStyleGenerator.OPT_PARAM, OptimizerFactory.StandardOptimizerDescriptor.POOLED.getExternalName() );
props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName() );
props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" );
generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
// because the dialect reports to not support pooled seqyences, the expectation is that we will
// use a table for the backing structure...
assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
assertEquals( 20, generator.getOptimizer().getIncrementSize() );
assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() );
}
@ -211,13 +211,13 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase {
SequenceStyleGenerator generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
props.setProperty( Environment.PREFER_POOLED_VALUES_LO, "true" );
generator = new SequenceStyleGenerator();
generator.configure( StandardBasicTypes.LONG, props, dialect );
assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() );
assertClassAssignability( OptimizerFactory.PooledLoOptimizer.class, generator.getOptimizer().getClass() );
assertClassAssignability( PooledLoOptimizer.class, generator.getOptimizer().getClass() );
}
private static class TableDialect extends Dialect {

View File

@ -28,7 +28,8 @@ import org.junit.Test;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.NoopOptimizer;
import org.hibernate.id.enhanced.PooledOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.internal.util.StringHelper;
@ -87,7 +88,7 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase {
assertEquals( 1, seqGenerator.getDatabaseStructure().getInitialValue() );
// 50 is the annotation default
assertEquals( 50, seqGenerator.getDatabaseStructure().getIncrementSize() );
assertFalse( OptimizerFactory.NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) );
assertFalse( NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) );
}
@Test
@ -99,7 +100,7 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase {
assertEquals( "my_catalog.my_schema."+CompleteSequenceEntity.SEQ_NAME, seqGenerator.getDatabaseStructure().getName() );
assertEquals( 1000, seqGenerator.getDatabaseStructure().getInitialValue() );
assertEquals( 52, seqGenerator.getDatabaseStructure().getIncrementSize() );
assertFalse( OptimizerFactory.NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) );
assertFalse( NoopOptimizer.class.isInstance( seqGenerator.getOptimizer() ) );
}
@Test
@ -127,7 +128,7 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase {
assertEquals( 1, tabGenerator.getInitialValue() );
// 50 is the annotation default
assertEquals( 50, tabGenerator.getIncrementSize() );
assertTrue( OptimizerFactory.PooledOptimizer.class.isInstance( tabGenerator.getOptimizer() ) );
assertTrue( PooledOptimizer.class.isInstance( tabGenerator.getOptimizer() ) );
}
@Test

View File

@ -27,7 +27,7 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.NoopOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.persister.entity.EntityPersister;
@ -59,7 +59,7 @@ public class BasicForcedTableSequenceTest extends BaseCoreFunctionalTestCase {
);
assertTrue(
"no-op optimizer was not used",
OptimizerFactory.NoopOptimizer.class.isInstance( generator.getOptimizer() )
NoopOptimizer.class.isInstance( generator.getOptimizer() )
);
int count = 5;

View File

@ -27,7 +27,7 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.HiLoOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.persister.entity.EntityPersister;
@ -60,9 +60,9 @@ public class HiLoForcedTableSequenceTest extends BaseCoreFunctionalTestCase {
);
assertTrue(
"hilo optimizer was not used",
OptimizerFactory.HiLoOptimizer.class.isInstance( generator.getOptimizer() )
HiLoOptimizer.class.isInstance( generator.getOptimizer() )
);
OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer();
HiLoOptimizer optimizer = (HiLoOptimizer) generator.getOptimizer();
int increment = optimizer.getIncrementSize();
Entity[] entities = new Entity[ increment + 1 ];

View File

@ -27,7 +27,7 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.PooledOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.persister.entity.EntityPersister;
@ -58,9 +58,9 @@ public class PooledForcedTableSequenceTest extends BaseCoreFunctionalTestCase {
);
assertTrue(
"pooled optimizer was not used",
OptimizerFactory.PooledOptimizer.class.isInstance( generator.getOptimizer() )
PooledOptimizer.class.isInstance( generator.getOptimizer() )
);
OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer();
PooledOptimizer optimizer = (PooledOptimizer) generator.getOptimizer();
int increment = optimizer.getIncrementSize();
Entity[] entities = new Entity[ increment + 1 ];

View File

@ -28,7 +28,7 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.HiLoOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -49,8 +49,8 @@ public class HiLoSequenceTest extends BaseCoreFunctionalTestCase {
EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() );
assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer();
assertClassAssignability( HiLoOptimizer.class, generator.getOptimizer().getClass() );
HiLoOptimizer optimizer = (HiLoOptimizer) generator.getOptimizer();
int increment = optimizer.getIncrementSize();
Entity[] entities = new Entity[ increment + 1 ];

View File

@ -26,7 +26,7 @@ package org.hibernate.test.idgen.enhanced.sequence;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.PooledOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -49,8 +49,8 @@ public class PooledSequenceTest extends BaseCoreFunctionalTestCase {
EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() );
assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
SequenceStyleGenerator generator = ( SequenceStyleGenerator ) persister.getIdentifierGenerator();
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer();
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
PooledOptimizer optimizer = (PooledOptimizer) generator.getOptimizer();
int increment = optimizer.getIncrementSize();
Entity[] entities = new Entity[ increment + 1 ];

View File

@ -26,7 +26,7 @@ package org.hibernate.test.idgen.enhanced.table;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.HiLoOptimizer;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -49,8 +49,8 @@ public class HiLoTableTest extends BaseCoreFunctionalTestCase {
EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() );
assertClassAssignability( TableGenerator.class, persister.getIdentifierGenerator().getClass() );
TableGenerator generator = ( TableGenerator ) persister.getIdentifierGenerator();
assertClassAssignability( OptimizerFactory.HiLoOptimizer.class, generator.getOptimizer().getClass() );
OptimizerFactory.HiLoOptimizer optimizer = ( OptimizerFactory.HiLoOptimizer ) generator.getOptimizer();
assertClassAssignability( HiLoOptimizer.class, generator.getOptimizer().getClass() );
HiLoOptimizer optimizer = (HiLoOptimizer) generator.getOptimizer();
int increment = optimizer.getIncrementSize();
Entity[] entities = new Entity[ increment + 1 ];

View File

@ -26,7 +26,7 @@ package org.hibernate.test.idgen.enhanced.table;
import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.PooledOptimizer;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
@ -49,8 +49,8 @@ public class PooledTableTest extends BaseCoreFunctionalTestCase {
EntityPersister persister = sessionFactory().getEntityPersister( Entity.class.getName() );
assertClassAssignability( TableGenerator.class, persister.getIdentifierGenerator().getClass() );
TableGenerator generator = ( TableGenerator ) persister.getIdentifierGenerator();
assertClassAssignability( OptimizerFactory.PooledOptimizer.class, generator.getOptimizer().getClass() );
OptimizerFactory.PooledOptimizer optimizer = ( OptimizerFactory.PooledOptimizer ) generator.getOptimizer();
assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() );
PooledOptimizer optimizer = (PooledOptimizer) generator.getOptimizer();
int increment = optimizer.getIncrementSize();
Entity[] entities = new Entity[ increment + 1 ];

View File

@ -0,0 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, 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.test.multitenancy.schema;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.id.enhanced.TableGenerator;
/**
* @author Steve Ebersole
*/
@Entity
public class Invoice {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "number_sequence")
@GenericGenerator(
name = "number_sequence",
strategy = "org.hibernate.id.enhanced.TableGenerator",
parameters = {
@Parameter(name = TableGenerator.SEGMENT_VALUE_PARAM, value = "customer"),
@Parameter(name = TableGenerator.INCREMENT_PARAM, value = "5"),
@Parameter(name = TableGenerator.OPT_PARAM, value = "pooled")
}
)
private Long id;
public Long getId() {
return id;
}
}

View File

@ -80,6 +80,7 @@ public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase {
cfg.setProperty( Environment.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName() );
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
cfg.addAnnotatedClass( Customer.class );
cfg.addAnnotatedClass( Invoice.class );
cfg.buildMappings();
RootClass meta = (RootClass) cfg.getClassMapping( Customer.class.getName() );
@ -293,6 +294,39 @@ public class SchemaBasedMultiTenancyTest extends BaseUnitTestCase {
session.close();
}
@Test
public void testTableIdentifiers() {
Session session = getNewSession( "jboss" );
session.beginTransaction();
Invoice orderJboss = new Invoice();
session.save( orderJboss );
Assert.assertEquals( Long.valueOf( 1 ), orderJboss.getId() );
session.getTransaction().commit();
session.close();
session = getNewSession( "acme" );
session.beginTransaction();
Invoice orderAcme = new Invoice();
session.save( orderAcme );
Assert.assertEquals( Long.valueOf( 1 ), orderAcme.getId() );
session.getTransaction().commit();
session.close();
session = getNewSession( "jboss" );
session.beginTransaction();
session.delete( orderJboss );
session.getTransaction().commit();
session.close();
session = getNewSession( "acme" );
session.beginTransaction();
session.delete( orderAcme );
session.getTransaction().commit();
session.close();
sessionFactory.getStatisticsImplementor().clear();
}
protected Session getNewSession(String tenant) {
return sessionFactory.withOptions().tenantIdentifier( tenant ).openSession();
}