clean up SequenceStyleGenerator + StandardOptimizerDescriptor
This commit is contained in:
parent
b682a1036c
commit
91eb9e1f20
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.id.enhanced;
|
||||||
|
|
||||||
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class CustomOptimizerDescriptor implements OptimizerDescriptor {
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
CustomOptimizerDescriptor(String className) {
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isPooled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExternalName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Optimizer> getOptimizerClass() throws ClassNotFoundException {
|
||||||
|
return ReflectHelper.classForName( className );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.id.enhanced;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public interface OptimizerDescriptor {
|
||||||
|
boolean isPooled();
|
||||||
|
String getExternalName();
|
||||||
|
Class<? extends Optimizer> getOptimizerClass()
|
||||||
|
throws ClassNotFoundException;
|
||||||
|
}
|
|
@ -11,12 +11,11 @@ import java.util.Properties;
|
||||||
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for {@link Optimizer} instances.
|
* Factory for {@link Optimizer} instances.
|
||||||
*
|
*
|
||||||
|
@ -34,59 +33,54 @@ public class OptimizerFactory {
|
||||||
* @param optimizerName The name of the optimizer
|
* @param optimizerName The name of the optimizer
|
||||||
*
|
*
|
||||||
* @return {@code true} indicates the optimizer is a pooled strategy.
|
* @return {@code true} indicates the optimizer is a pooled strategy.
|
||||||
|
*
|
||||||
|
* @deprecated No longer used
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "6.3")
|
||||||
public static boolean isPooledOptimizer(String optimizerName) {
|
public static boolean isPooledOptimizer(String optimizerName) {
|
||||||
final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( optimizerName );
|
return StandardOptimizerDescriptor.fromExternalName( optimizerName ).isPooled();
|
||||||
return standardDescriptor != null && standardDescriptor.isPooled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Class[] CTOR_SIG = new Class[] { Class.class, int.class };
|
private static final Class<?>[] CTOR_SIG = new Class[] { Class.class, int.class };
|
||||||
|
|
||||||
private static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize) {
|
private static Optimizer buildOptimizer(OptimizerDescriptor descriptor, Class<?> returnClass, int incrementSize) {
|
||||||
final Class<? extends Optimizer> optimizerClass;
|
final Class<? extends Optimizer> optimizerClass;
|
||||||
|
|
||||||
final StandardOptimizerDescriptor standardDescriptor = StandardOptimizerDescriptor.fromExternalName( type );
|
|
||||||
if ( standardDescriptor != null ) {
|
|
||||||
optimizerClass = standardDescriptor.getOptimizerClass();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
try {
|
||||||
optimizerClass = ReflectHelper.classForName( type );
|
optimizerClass = descriptor.getOptimizerClass();
|
||||||
}
|
}
|
||||||
catch ( Throwable ignore ) {
|
catch ( Throwable ignore ) {
|
||||||
LOG.unableToLocateCustomOptimizerClass( type );
|
LOG.unableToLocateCustomOptimizerClass( descriptor.getExternalName() );
|
||||||
return buildFallbackOptimizer( returnClass, incrementSize );
|
return buildFallbackOptimizer( returnClass, incrementSize );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Constructor ctor = optimizerClass.getConstructor( CTOR_SIG );
|
final Constructor<? extends Optimizer> ctor = optimizerClass.getConstructor( CTOR_SIG );
|
||||||
return (Optimizer) ctor.newInstance( returnClass, incrementSize );
|
return ctor.newInstance( returnClass, incrementSize );
|
||||||
}
|
}
|
||||||
catch ( Throwable ignore ) {
|
catch ( Throwable ignore ) {
|
||||||
LOG.unableToInstantiateOptimizer( type );
|
LOG.unableToInstantiateOptimizer( descriptor.getExternalName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildFallbackOptimizer( returnClass, incrementSize );
|
return buildFallbackOptimizer( returnClass, incrementSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optimizer buildFallbackOptimizer(Class returnClass, int incrementSize) {
|
private static Optimizer buildFallbackOptimizer(Class<?> returnClass, int incrementSize) {
|
||||||
return new NoopOptimizer( returnClass, incrementSize );
|
return new NoopOptimizer( returnClass, incrementSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds an optimizer
|
* Builds an optimizer
|
||||||
*
|
*
|
||||||
* @param type The optimizer type, either a short-hand name or the {@link Optimizer} class name.
|
* @param type The optimizer type, either a shorthand name or the {@link Optimizer} class name.
|
||||||
* @param returnClass The generated value java type
|
* @param returnClass The generated value java type
|
||||||
* @param incrementSize The increment size.
|
* @param incrementSize The increment size.
|
||||||
* @param explicitInitialValue The user supplied initial-value (-1 indicates the user did not specify).
|
* @param explicitInitialValue The user supplied initial-value (-1 indicates the user did not specify).
|
||||||
*
|
*
|
||||||
* @return The built optimizer
|
* @return The built optimizer
|
||||||
*/
|
*/
|
||||||
public static Optimizer buildOptimizer(String type, Class returnClass, int incrementSize, long explicitInitialValue) {
|
public static Optimizer buildOptimizer(OptimizerDescriptor type, Class<?> returnClass, int incrementSize, long explicitInitialValue) {
|
||||||
final Optimizer optimizer = buildOptimizer( type, returnClass, incrementSize );
|
final Optimizer optimizer = buildOptimizer( type, returnClass, incrementSize );
|
||||||
if ( InitialValueAwareOptimizer.class.isInstance( optimizer ) ) {
|
if ( optimizer instanceof InitialValueAwareOptimizer ) {
|
||||||
( (InitialValueAwareOptimizer) optimizer ).injectInitialValue( explicitInitialValue );
|
( (InitialValueAwareOptimizer) optimizer ).injectInitialValue( explicitInitialValue );
|
||||||
}
|
}
|
||||||
return optimizer;
|
return optimizer;
|
||||||
|
@ -99,16 +93,19 @@ public class OptimizerFactory {
|
||||||
if ( incrementSize <= 1 ) {
|
if ( incrementSize <= 1 ) {
|
||||||
return StandardOptimizerDescriptor.NONE.getExternalName();
|
return StandardOptimizerDescriptor.NONE.getExternalName();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// see if the user defined a preferred pooled optimizer...
|
// see if the user defined a preferred pooled optimizer...
|
||||||
final String preferredPooledOptimizerStrategy = configSettings.getProperty( AvailableSettings.PREFERRED_POOLED_OPTIMIZER );
|
final String preferredPooledOptimizerStrategy =
|
||||||
if ( StringHelper.isNotEmpty( preferredPooledOptimizerStrategy ) ) {
|
configSettings.getProperty( AvailableSettings.PREFERRED_POOLED_OPTIMIZER );
|
||||||
|
if ( isNotEmpty( preferredPooledOptimizerStrategy ) ) {
|
||||||
return preferredPooledOptimizerStrategy;
|
return preferredPooledOptimizerStrategy;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// otherwise fallback to the fallback strategy
|
// otherwise fallback to the fallback strategy
|
||||||
return StandardOptimizerDescriptor.POOLED.getExternalName();
|
return StandardOptimizerDescriptor.POOLED.getExternalName();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private OptimizerFactory() {
|
private OptimizerFactory() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.boot.model.relational.Database;
|
import org.hibernate.boot.model.relational.Database;
|
||||||
import org.hibernate.boot.model.relational.QualifiedName;
|
import org.hibernate.boot.model.relational.QualifiedName;
|
||||||
import org.hibernate.boot.model.relational.QualifiedNameParser;
|
import org.hibernate.boot.model.relational.QualifiedNameParser;
|
||||||
|
import org.hibernate.boot.model.relational.QualifiedSequenceName;
|
||||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||||
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
import org.hibernate.boot.registry.selector.spi.StrategySelector;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
@ -28,7 +29,6 @@ import org.hibernate.id.PersistentIdentifierGenerator;
|
||||||
import org.hibernate.id.SequenceMismatchStrategy;
|
import org.hibernate.id.SequenceMismatchStrategy;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
|
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
@ -36,8 +36,12 @@ import org.hibernate.type.Type;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import static org.hibernate.cfg.AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY;
|
import static org.hibernate.cfg.AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY;
|
||||||
|
import static org.hibernate.id.enhanced.OptimizerFactory.determineImplicitOptimizerName;
|
||||||
import static org.hibernate.internal.log.IncubationLogger.INCUBATION_LOGGER;
|
import static org.hibernate.internal.log.IncubationLogger.INCUBATION_LOGGER;
|
||||||
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
|
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
|
||||||
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
|
||||||
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getInt;
|
||||||
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates identifier values based on a sequence-style database structure.
|
* Generates identifier values based on a sequence-style database structure.
|
||||||
|
@ -188,65 +192,33 @@ public class SequenceStyleGenerator
|
||||||
@Override
|
@Override
|
||||||
public void configure(Type type, Properties parameters, ServiceRegistry serviceRegistry) throws MappingException {
|
public void configure(Type type, Properties parameters, ServiceRegistry serviceRegistry) throws MappingException {
|
||||||
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
|
final JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class );
|
||||||
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
|
|
||||||
|
|
||||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||||
|
|
||||||
this.identifierType = type;
|
this.identifierType = type;
|
||||||
boolean forceTableUse = ConfigurationHelper.getBoolean( FORCE_TBL_PARAM, parameters, false );
|
|
||||||
|
|
||||||
final QualifiedName sequenceName = determineSequenceName( parameters, dialect, jdbcEnvironment, serviceRegistry );
|
final QualifiedName sequenceName = determineSequenceName( parameters, dialect, jdbcEnvironment, serviceRegistry );
|
||||||
|
|
||||||
final int initialValue = determineInitialValue( parameters );
|
final int initialValue = determineInitialValue( parameters );
|
||||||
int incrementSize = determineIncrementSize( parameters );
|
int incrementSize = determineIncrementSize( parameters );
|
||||||
|
final OptimizerDescriptor optimizationStrategy = determineOptimizationStrategy( parameters, incrementSize );
|
||||||
|
|
||||||
final String optimizationStrategy = determineOptimizationStrategy(parameters, incrementSize );
|
boolean forceTableUse = getBoolean( FORCE_TBL_PARAM, parameters, false );
|
||||||
|
final boolean physicalSequence = isPhysicalSequence( jdbcEnvironment, forceTableUse );
|
||||||
|
|
||||||
final boolean isPooledOptimizer = OptimizerFactory.isPooledOptimizer( optimizationStrategy );
|
incrementSize = adjustIncrementSize(
|
||||||
|
jdbcEnvironment,
|
||||||
|
sequenceName,
|
||||||
SequenceMismatchStrategy sequenceMismatchStrategy = configurationService.getSetting(
|
incrementSize,
|
||||||
AvailableSettings.SEQUENCE_INCREMENT_SIZE_MISMATCH_STRATEGY,
|
physicalSequence,
|
||||||
SequenceMismatchStrategy::interpret,
|
optimizationStrategy,
|
||||||
SequenceMismatchStrategy.EXCEPTION
|
serviceRegistry
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( sequenceMismatchStrategy != SequenceMismatchStrategy.NONE
|
if ( physicalSequence
|
||||||
&& isPooledOptimizer
|
&& optimizationStrategy.isPooled()
|
||||||
&& isPhysicalSequence( jdbcEnvironment, forceTableUse ) ) {
|
&& !dialect.getSequenceSupport().supportsPooledSequences() ) {
|
||||||
String databaseSequenceName = sequenceName.getObjectName().getText();
|
|
||||||
Number databaseIncrementValue = getSequenceIncrementValue( jdbcEnvironment, databaseSequenceName );
|
|
||||||
|
|
||||||
if ( databaseIncrementValue != null && databaseIncrementValue.intValue() != incrementSize ) {
|
|
||||||
int dbIncrementValue = databaseIncrementValue.intValue();
|
|
||||||
|
|
||||||
switch ( sequenceMismatchStrategy ) {
|
|
||||||
case EXCEPTION:
|
|
||||||
throw new MappingException(
|
|
||||||
String.format(
|
|
||||||
"The increment size of the [%s] sequence is set to [%d] in the entity mapping " +
|
|
||||||
"while the associated database sequence increment size is [%d].",
|
|
||||||
databaseSequenceName, incrementSize, dbIncrementValue
|
|
||||||
)
|
|
||||||
);
|
|
||||||
case FIX:
|
|
||||||
incrementSize = dbIncrementValue;
|
|
||||||
case LOG:
|
|
||||||
LOG.sequenceIncrementSizeMismatch( databaseSequenceName, incrementSize, dbIncrementValue );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
incrementSize = determineAdjustedIncrementSize( optimizationStrategy, incrementSize );
|
|
||||||
|
|
||||||
if ( dialect.getSequenceSupport().supportsSequences() && !forceTableUse ) {
|
|
||||||
if ( !dialect.getSequenceSupport().supportsPooledSequences()
|
|
||||||
&& OptimizerFactory.isPooledOptimizer( optimizationStrategy ) ) {
|
|
||||||
forceTableUse = true;
|
forceTableUse = true;
|
||||||
LOG.forcingTableUse();
|
LOG.forcingTableUse();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.databaseStructure = buildDatabaseStructure(
|
this.databaseStructure = buildDatabaseStructure(
|
||||||
type,
|
type,
|
||||||
|
@ -261,11 +233,53 @@ public class SequenceStyleGenerator
|
||||||
optimizationStrategy,
|
optimizationStrategy,
|
||||||
identifierType.getReturnedClass(),
|
identifierType.getReturnedClass(),
|
||||||
incrementSize,
|
incrementSize,
|
||||||
ConfigurationHelper.getInt( INITIAL_PARAM, parameters, -1 )
|
getInt( INITIAL_PARAM, parameters, -1 )
|
||||||
);
|
);
|
||||||
this.databaseStructure.configure( optimizer );
|
this.databaseStructure.configure( optimizer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int adjustIncrementSize(
|
||||||
|
JdbcEnvironment jdbcEnvironment,
|
||||||
|
QualifiedName sequenceName,
|
||||||
|
int incrementSize,
|
||||||
|
boolean physicalSequence,
|
||||||
|
OptimizerDescriptor optimizationStrategy,
|
||||||
|
ServiceRegistry serviceRegistry) {
|
||||||
|
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
|
||||||
|
final SequenceMismatchStrategy sequenceMismatchStrategy = configurationService.getSetting(
|
||||||
|
AvailableSettings.SEQUENCE_INCREMENT_SIZE_MISMATCH_STRATEGY,
|
||||||
|
SequenceMismatchStrategy::interpret,
|
||||||
|
SequenceMismatchStrategy.EXCEPTION
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( sequenceMismatchStrategy != SequenceMismatchStrategy.NONE
|
||||||
|
&& optimizationStrategy.isPooled()
|
||||||
|
&& physicalSequence ) {
|
||||||
|
final String databaseSequenceName = sequenceName.getObjectName().getText();
|
||||||
|
final Number databaseIncrementValue = getSequenceIncrementValue( jdbcEnvironment, databaseSequenceName );
|
||||||
|
if ( databaseIncrementValue != null && databaseIncrementValue.intValue() != incrementSize) {
|
||||||
|
final int dbIncrementValue = databaseIncrementValue.intValue();
|
||||||
|
switch ( sequenceMismatchStrategy ) {
|
||||||
|
case EXCEPTION:
|
||||||
|
throw new MappingException(
|
||||||
|
String.format(
|
||||||
|
"The increment size of the [%s] sequence is set to [%d] in the entity mapping "
|
||||||
|
+ "while the associated database sequence increment size is [%d].",
|
||||||
|
databaseSequenceName, incrementSize, dbIncrementValue
|
||||||
|
)
|
||||||
|
);
|
||||||
|
case FIX:
|
||||||
|
incrementSize = dbIncrementValue;
|
||||||
|
case LOG:
|
||||||
|
//TODO: the log message is correct for the case of FIX, but wrong for LOG
|
||||||
|
LOG.sequenceIncrementSizeMismatch( databaseSequenceName, incrementSize, dbIncrementValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return determineAdjustedIncrementSize( optimizationStrategy, incrementSize );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerExportables(Database database) {
|
public void registerExportables(Database database) {
|
||||||
databaseStructure.registerExportables( database );
|
databaseStructure.registerExportables( database );
|
||||||
|
@ -294,16 +308,16 @@ public class SequenceStyleGenerator
|
||||||
JdbcEnvironment jdbcEnv,
|
JdbcEnvironment jdbcEnv,
|
||||||
ServiceRegistry serviceRegistry) {
|
ServiceRegistry serviceRegistry) {
|
||||||
final Identifier catalog = jdbcEnv.getIdentifierHelper().toIdentifier(
|
final Identifier catalog = jdbcEnv.getIdentifierHelper().toIdentifier(
|
||||||
ConfigurationHelper.getString( CATALOG, params )
|
getString( CATALOG, params )
|
||||||
);
|
);
|
||||||
final Identifier schema = jdbcEnv.getIdentifierHelper().toIdentifier(
|
final Identifier schema = jdbcEnv.getIdentifierHelper().toIdentifier(
|
||||||
ConfigurationHelper.getString( SCHEMA, params )
|
getString( SCHEMA, params )
|
||||||
);
|
);
|
||||||
|
|
||||||
final String sequenceName = ConfigurationHelper.getString(
|
final String sequenceName = getString(
|
||||||
SEQUENCE_PARAM,
|
SEQUENCE_PARAM,
|
||||||
params,
|
params,
|
||||||
() -> ConfigurationHelper.getString( ALT_SEQUENCE_PARAM, params )
|
() -> getString( ALT_SEQUENCE_PARAM, params )
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( StringHelper.isNotEmpty( sequenceName ) ) {
|
if ( StringHelper.isNotEmpty( sequenceName ) ) {
|
||||||
|
@ -333,7 +347,7 @@ public class SequenceStyleGenerator
|
||||||
|
|
||||||
final String namingStrategySetting = coalesceSuppliedValues(
|
final String namingStrategySetting = coalesceSuppliedValues(
|
||||||
() -> {
|
() -> {
|
||||||
final String localSetting = ConfigurationHelper.getString( ID_DB_STRUCTURE_NAMING_STRATEGY, params );
|
final String localSetting = getString( ID_DB_STRUCTURE_NAMING_STRATEGY, params );
|
||||||
if ( localSetting != null ) {
|
if ( localSetting != null ) {
|
||||||
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );
|
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );
|
||||||
}
|
}
|
||||||
|
@ -341,7 +355,7 @@ public class SequenceStyleGenerator
|
||||||
},
|
},
|
||||||
() -> {
|
() -> {
|
||||||
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
|
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
|
||||||
final String globalSetting = ConfigurationHelper.getString( ID_DB_STRUCTURE_NAMING_STRATEGY, configurationService.getSettings() );
|
final String globalSetting = getString( ID_DB_STRUCTURE_NAMING_STRATEGY, configurationService.getSettings() );
|
||||||
if ( globalSetting != null ) {
|
if ( globalSetting != null ) {
|
||||||
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );
|
INCUBATION_LOGGER.incubatingSetting( ID_DB_STRUCTURE_NAMING_STRATEGY );
|
||||||
}
|
}
|
||||||
|
@ -370,7 +384,7 @@ public class SequenceStyleGenerator
|
||||||
* @return The value column name
|
* @return The value column name
|
||||||
*/
|
*/
|
||||||
protected Identifier determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
|
protected Identifier determineValueColumnName(Properties params, JdbcEnvironment jdbcEnvironment) {
|
||||||
final String name = ConfigurationHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
|
final String name = getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
|
||||||
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name );
|
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +399,7 @@ public class SequenceStyleGenerator
|
||||||
* @return The initial value
|
* @return The initial value
|
||||||
*/
|
*/
|
||||||
protected int determineInitialValue(Properties params) {
|
protected int determineInitialValue(Properties params) {
|
||||||
return ConfigurationHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
|
return getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -398,7 +412,7 @@ public class SequenceStyleGenerator
|
||||||
* @return The increment size
|
* @return The increment size
|
||||||
*/
|
*/
|
||||||
protected int determineIncrementSize(Properties params) {
|
protected int determineIncrementSize(Properties params) {
|
||||||
return ConfigurationHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
|
return getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -410,11 +424,9 @@ public class SequenceStyleGenerator
|
||||||
* @param incrementSize The {@link #determineIncrementSize determined increment size}
|
* @param incrementSize The {@link #determineIncrementSize determined increment size}
|
||||||
* @return The optimizer strategy (name)
|
* @return The optimizer strategy (name)
|
||||||
*/
|
*/
|
||||||
protected String determineOptimizationStrategy(Properties params, int incrementSize) {
|
protected OptimizerDescriptor determineOptimizationStrategy(Properties params, int incrementSize) {
|
||||||
return ConfigurationHelper.getString(
|
return StandardOptimizerDescriptor.fromExternalName(
|
||||||
OPT_PARAM,
|
getString( OPT_PARAM, params, determineImplicitOptimizerName( incrementSize, params ) )
|
||||||
params,
|
|
||||||
OptimizerFactory.determineImplicitOptimizerName( incrementSize, params )
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,36 +438,35 @@ public class SequenceStyleGenerator
|
||||||
* @param incrementSize The {@link #determineIncrementSize determined increment size}
|
* @param incrementSize The {@link #determineIncrementSize determined increment size}
|
||||||
* @return The adjusted increment size.
|
* @return The adjusted increment size.
|
||||||
*/
|
*/
|
||||||
protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
|
protected int determineAdjustedIncrementSize(OptimizerDescriptor optimizationStrategy, int incrementSize) {
|
||||||
final int resolvedIncrementSize;
|
if ( optimizationStrategy == StandardOptimizerDescriptor.NONE ) {
|
||||||
if ( Math.abs( incrementSize ) > 1 &&
|
|
||||||
StandardOptimizerDescriptor.NONE.getExternalName().equals( optimizationStrategy ) ) {
|
|
||||||
if ( incrementSize < -1 ) {
|
if ( incrementSize < -1 ) {
|
||||||
resolvedIncrementSize = -1;
|
|
||||||
LOG.honoringOptimizerSetting(
|
LOG.honoringOptimizerSetting(
|
||||||
StandardOptimizerDescriptor.NONE.getExternalName(),
|
StandardOptimizerDescriptor.NONE.getExternalName(),
|
||||||
INCREMENT_PARAM,
|
INCREMENT_PARAM,
|
||||||
incrementSize,
|
incrementSize,
|
||||||
"negative",
|
"negative",
|
||||||
resolvedIncrementSize
|
-1
|
||||||
);
|
);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
else {
|
else if ( incrementSize > 1 ) {
|
||||||
// incrementSize > 1
|
|
||||||
resolvedIncrementSize = 1;
|
|
||||||
LOG.honoringOptimizerSetting(
|
LOG.honoringOptimizerSetting(
|
||||||
StandardOptimizerDescriptor.NONE.getExternalName(),
|
StandardOptimizerDescriptor.NONE.getExternalName(),
|
||||||
INCREMENT_PARAM,
|
INCREMENT_PARAM,
|
||||||
incrementSize,
|
incrementSize,
|
||||||
"positive",
|
"positive",
|
||||||
resolvedIncrementSize
|
1
|
||||||
);
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return incrementSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolvedIncrementSize = incrementSize;
|
return incrementSize;
|
||||||
}
|
}
|
||||||
return resolvedIncrementSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -571,11 +582,12 @@ public class SequenceStyleGenerator
|
||||||
.stream()
|
.stream()
|
||||||
.filter(
|
.filter(
|
||||||
sequenceInformation -> {
|
sequenceInformation -> {
|
||||||
Identifier catalog = sequenceInformation.getSequenceName().getCatalogName();
|
QualifiedSequenceName name = sequenceInformation.getSequenceName();
|
||||||
Identifier schema = sequenceInformation.getSequenceName().getSchemaName();
|
Identifier catalog = name.getCatalogName();
|
||||||
return sequenceName.equalsIgnoreCase( sequenceInformation.getSequenceName().getSequenceName().getText() ) &&
|
Identifier schema = name.getSchemaName();
|
||||||
( catalog == null || catalog.equals( jdbcEnvironment.getCurrentCatalog() ) ) &&
|
return sequenceName.equalsIgnoreCase( name.getSequenceName().getText() )
|
||||||
( schema == null || schema.equals( jdbcEnvironment.getCurrentSchema() ) );
|
&& ( catalog == null || catalog.equals( jdbcEnvironment.getCurrentCatalog() ) )
|
||||||
|
&& ( schema == null || schema.equals( jdbcEnvironment.getCurrentSchema() ) );
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.map( SequenceInformation::getIncrementValue )
|
.map( SequenceInformation::getIncrementValue )
|
||||||
|
|
|
@ -6,72 +6,97 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.id.enhanced;
|
package org.hibernate.id.enhanced;
|
||||||
|
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.AssertionFailure;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of the standard Hibernate id generation optimizers.
|
* Enumeration of the standard Hibernate id generation optimizers.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public enum StandardOptimizerDescriptor {
|
public enum StandardOptimizerDescriptor implements OptimizerDescriptor {
|
||||||
/**
|
/**
|
||||||
* Describes the optimizer for no optimization.
|
* Describes the optimizer for no optimization.
|
||||||
*/
|
*/
|
||||||
NONE( "none", NoopOptimizer.class ),
|
NONE,
|
||||||
/**
|
/**
|
||||||
* Describes the optimizer for using a custom "hilo" algorithm optimization.
|
* Describes the optimizer for using a custom "hilo" algorithm optimization.
|
||||||
*/
|
*/
|
||||||
HILO( "hilo", HiLoOptimizer.class ),
|
HILO,
|
||||||
/**
|
/**
|
||||||
* Describes the optimizer for using a custom "hilo" algorithm optimization, following the
|
* Describes the optimizer for using a custom "hilo" algorithm optimization, following the
|
||||||
* legacy Hibernate hilo algorithm.
|
* legacy Hibernate hilo algorithm.
|
||||||
*/
|
*/
|
||||||
LEGACY_HILO( "legacy-hilo", LegacyHiLoAlgorithmOptimizer.class ),
|
LEGACY_HILO,
|
||||||
/**
|
/**
|
||||||
* Describes the optimizer for use with tables/sequences that store the chunk information.
|
* Describes the optimizer for use with tables/sequences that store the chunk information.
|
||||||
* Here, specifically the hi value is stored in the database.
|
* Here, specifically the hi value is stored in the database.
|
||||||
*/
|
*/
|
||||||
POOLED( "pooled", PooledOptimizer.class, true ),
|
POOLED,
|
||||||
/**
|
/**
|
||||||
* Describes the optimizer for use with tables/sequences that store the chunk information.
|
* Describes the optimizer for use with tables/sequences that store the chunk information.
|
||||||
* Here, specifically the lo value is stored in the database.
|
* Here, specifically the lo value is stored in the database.
|
||||||
*/
|
*/
|
||||||
POOLED_LO( "pooled-lo", PooledLoOptimizer.class, true ),
|
POOLED_LO,
|
||||||
/**
|
/**
|
||||||
* Describes the optimizer for use with tables/sequences that store the chunk information.
|
* Describes the optimizer for use with tables/sequences that store the chunk information.
|
||||||
* Here, specifically the lo value is stored in the database and ThreadLocal used to cache
|
* Here, specifically the lo value is stored in the database and ThreadLocal used to cache
|
||||||
* the generation state.
|
* the generation state.
|
||||||
*/
|
*/
|
||||||
POOLED_LOTL( "pooled-lotl", PooledLoThreadLocalOptimizer.class, true );
|
POOLED_LOTL;
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger( StandardOptimizerDescriptor.class );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getExternalName() {
|
public String getExternalName() {
|
||||||
return externalName;
|
switch ( this ) {
|
||||||
|
case NONE:
|
||||||
|
return "none";
|
||||||
|
case HILO:
|
||||||
|
return "hilo";
|
||||||
|
case LEGACY_HILO:
|
||||||
|
return "legacy-hilo";
|
||||||
|
case POOLED:
|
||||||
|
return "pooled";
|
||||||
|
case POOLED_LO:
|
||||||
|
return "pooled-lo";
|
||||||
|
case POOLED_LOTL:
|
||||||
|
return "pooled-lotl";
|
||||||
|
}
|
||||||
|
throw new AssertionFailure( "unknown StandardOptimizerDescriptor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Class<? extends Optimizer> getOptimizerClass() {
|
public Class<? extends Optimizer> getOptimizerClass() {
|
||||||
return optimizerClass;
|
switch ( this ) {
|
||||||
|
case NONE:
|
||||||
|
return NoopOptimizer.class;
|
||||||
|
case HILO:
|
||||||
|
return HiLoOptimizer.class;
|
||||||
|
case LEGACY_HILO:
|
||||||
|
return LegacyHiLoAlgorithmOptimizer.class;
|
||||||
|
case POOLED:
|
||||||
|
return PooledOptimizer.class;
|
||||||
|
case POOLED_LO:
|
||||||
|
return PooledLoOptimizer.class;
|
||||||
|
case POOLED_LOTL:
|
||||||
|
return PooledLoThreadLocalOptimizer.class;
|
||||||
|
}
|
||||||
|
throw new AssertionFailure( "unknown StandardOptimizerDescriptor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isPooled() {
|
public boolean isPooled() {
|
||||||
return isPooled;
|
switch ( this ) {
|
||||||
|
case NONE:
|
||||||
|
case HILO:
|
||||||
|
case LEGACY_HILO:
|
||||||
|
return false;
|
||||||
|
case POOLED:
|
||||||
|
case POOLED_LO:
|
||||||
|
case POOLED_LOTL:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new AssertionFailure( "unknown StandardOptimizerDescriptor" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,32 +108,17 @@ public enum StandardOptimizerDescriptor {
|
||||||
* {@link #NONE} is returned; if an unrecognized external name is supplied,
|
* {@link #NONE} is returned; if an unrecognized external name is supplied,
|
||||||
* {@code null} is returned
|
* {@code null} is returned
|
||||||
*/
|
*/
|
||||||
public static StandardOptimizerDescriptor fromExternalName(String externalName) {
|
public static OptimizerDescriptor fromExternalName(String externalName) {
|
||||||
if ( StringHelper.isEmpty( externalName ) ) {
|
if ( isEmpty( externalName ) ) {
|
||||||
log.debug( "No optimizer specified, using NONE as default" );
|
|
||||||
return NONE;
|
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 if ( POOLED_LOTL.externalName.equals( externalName ) ) {
|
|
||||||
return POOLED_LOTL;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
log.debugf( "Unknown optimizer key [%s]; returning null assuming Optimizer impl class name", externalName );
|
for ( StandardOptimizerDescriptor value: values() ) {
|
||||||
return null;
|
if ( value.getExternalName().equals( externalName ) ) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new CustomOptimizerDescriptor( externalName );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
@ -53,7 +52,9 @@ import org.hibernate.type.Type;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.hibernate.cfg.AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY;
|
import static org.hibernate.cfg.AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY;
|
||||||
|
import static org.hibernate.id.enhanced.OptimizerFactory.determineImplicitOptimizerName;
|
||||||
import static org.hibernate.internal.log.IncubationLogger.INCUBATION_LOGGER;
|
import static org.hibernate.internal.log.IncubationLogger.INCUBATION_LOGGER;
|
||||||
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
|
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
|
||||||
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
|
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
|
||||||
|
@ -346,17 +347,11 @@ public class TableGenerator implements PersistentIdentifierGenerator {
|
||||||
initialValue = determineInitialValue( parameters );
|
initialValue = determineInitialValue( parameters );
|
||||||
incrementSize = determineIncrementSize( parameters );
|
incrementSize = determineIncrementSize( parameters );
|
||||||
|
|
||||||
final String optimizationStrategy = getString(
|
|
||||||
OPT_PARAM,
|
|
||||||
parameters,
|
|
||||||
OptimizerFactory.determineImplicitOptimizerName( incrementSize, parameters)
|
|
||||||
);
|
|
||||||
int optimizerInitialValue = getInt( INITIAL_PARAM, parameters, -1 );
|
|
||||||
optimizer = OptimizerFactory.buildOptimizer(
|
optimizer = OptimizerFactory.buildOptimizer(
|
||||||
optimizationStrategy,
|
determineOptimizationStrategy( parameters, incrementSize ),
|
||||||
identifierType.getReturnedClass(),
|
identifierType.getReturnedClass(),
|
||||||
incrementSize,
|
incrementSize,
|
||||||
optimizerInitialValue
|
getInt( INITIAL_PARAM, parameters, -1 )
|
||||||
);
|
);
|
||||||
|
|
||||||
contributor = parameters.getProperty( CONTRIBUTOR_NAME );
|
contributor = parameters.getProperty( CONTRIBUTOR_NAME );
|
||||||
|
@ -365,6 +360,12 @@ public class TableGenerator implements PersistentIdentifierGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static OptimizerDescriptor determineOptimizationStrategy(Properties parameters, int incrementSize) {
|
||||||
|
return StandardOptimizerDescriptor.fromExternalName(
|
||||||
|
getString( OPT_PARAM, parameters, determineImplicitOptimizerName( incrementSize, parameters ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the table name to use for the generator values.
|
* Determine the table name to use for the generator values.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -515,7 +516,6 @@ public class TableGenerator implements PersistentIdentifierGenerator {
|
||||||
return getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
|
return getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected String buildSelectQuery(String formattedPhysicalTableName, SqlStringGenerationContext context) {
|
protected String buildSelectQuery(String formattedPhysicalTableName, SqlStringGenerationContext context) {
|
||||||
final String alias = "tbl";
|
final String alias = "tbl";
|
||||||
final String query = "select " + StringHelper.qualify( alias, valueColumnName )
|
final String query = "select " + StringHelper.qualify( alias, valueColumnName )
|
||||||
|
@ -523,7 +523,7 @@ public class TableGenerator implements PersistentIdentifierGenerator {
|
||||||
+ " where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
|
+ " where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
|
||||||
final LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
|
final LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
|
||||||
lockOptions.setAliasSpecificLockMode( alias, LockMode.PESSIMISTIC_WRITE );
|
lockOptions.setAliasSpecificLockMode( alias, LockMode.PESSIMISTIC_WRITE );
|
||||||
final Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
|
final Map<String,String[]> updateTargetColumnsMap = singletonMap( alias, new String[] { valueColumnName } );
|
||||||
return context.getDialect().applyLocksToSql( query, lockOptions, updateTargetColumnsMap );
|
return context.getDialect().applyLocksToSql( query, lockOptions, updateTargetColumnsMap );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,7 @@ public class OptimizerConcurrencyUnitTest extends BaseUnitTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optimizer buildOptimizer(long initial, int increment) {
|
private Optimizer buildOptimizer(long initial, int increment) {
|
||||||
return OptimizerFactory.buildOptimizer( optimizerDescriptor.getExternalName(), Long.class, increment, initial );
|
return OptimizerFactory.buildOptimizer( optimizerDescriptor, Long.class, increment, initial );
|
||||||
}
|
}
|
||||||
|
|
||||||
private <R> R getDoneFutureValue(Future<R> future) {
|
private <R> R getDoneFutureValue(Future<R> future) {
|
||||||
|
|
|
@ -328,7 +328,7 @@ public class OptimizerUnitTest {
|
||||||
StandardOptimizerDescriptor descriptor,
|
StandardOptimizerDescriptor descriptor,
|
||||||
long initial,
|
long initial,
|
||||||
int increment) {
|
int increment) {
|
||||||
return OptimizerFactory.buildOptimizer( descriptor.getExternalName(), Long.class, increment, initial );
|
return OptimizerFactory.buildOptimizer( descriptor, Long.class, increment, initial );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SourceMock implements AccessCallback {
|
private static class SourceMock implements AccessCallback {
|
||||||
|
|
Loading…
Reference in New Issue