HHH-7582 - TableGenerator does not distinguish between different tenants (MultiTenant Schema based)
This commit is contained in:
parent
45d46b619b
commit
91758f74ff
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -94,4 +94,4 @@ public interface DatabaseStructure {
|
|||
* @return {@code true} if the actual database structure is a sequence; {@code false} otherwise.
|
||||
*/
|
||||
public boolean isPhysicalSequence();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 + " ) ) "
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* Enhanced/improved versions of table and sequence based identifier generators targeting portability and unified
|
||||
* configuration
|
||||
*/
|
||||
package org.hibernate.id.enhanced;
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ];
|
||||
|
|
|
@ -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 ];
|
||||
|
|
|
@ -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 ];
|
||||
|
|
|
@ -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 ];
|
||||
|
|
|
@ -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 ];
|
||||
|
|
|
@ -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 ];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue