/* * 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 java.util.Properties; import org.hibernate.MappingException; import org.hibernate.boot.internal.MetadataBuilderImpl; import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.type.StandardBasicTypes; import org.hibernate.testing.boot.MetadataBuildingContextTestingImpl; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; import static org.hibernate.testing.junit4.ExtraAssertions.assertClassAssignability; import static org.junit.Assert.assertEquals; /** * Tests that SequenceStyleGenerator configures itself as expected in various scenarios * * @author Steve Ebersole */ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { /** * Test all params defaulted with a dialect supporting sequences */ @Test public void testDefaultedSequenceBackedConfiguration() { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, SequenceDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } private Properties buildGeneratorPropertiesBase(StandardServiceRegistry serviceRegistry) { Properties props = new Properties(); props.put( PersistentIdentifierGenerator.IDENTIFIER_NORMALIZER, new MetadataBuildingContextTestingImpl( serviceRegistry ).getObjectNameNormalizer() ); return props; } /** * Test all params defaulted with a dialect which does not support sequences */ @Test public void testDefaultedTableBackedConfiguration() { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, TableDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } /** * Test default optimizer selection for sequence backed generators * based on the configured increment size; both in the case of the * dialect supporting pooled sequences (pooled) and not (hilo) */ @Test public void testDefaultOptimizerBasedOnIncrementBackedBySequence() { // for dialects which do not support pooled sequences, we default to pooled+table StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, SequenceDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "10" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } // for dialects which do support pooled sequences, we default to pooled+sequence serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, PooledSequenceDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "10" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } /** * Test default optimizer selection for table backed generators * based on the configured increment size. Here we always prefer * pooled. */ @Test public void testDefaultOptimizerBasedOnIncrementBackedByTable() { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, TableDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "10" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } /** * Test forcing of table as backing structure with dialect supporting sequences */ @Test public void testForceTableUse() { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, SequenceDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); props.setProperty( SequenceStyleGenerator.FORCE_TBL_PARAM, "true" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( TableStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( NoopOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( SequenceStyleGenerator.DEF_SEQUENCE_NAME, generator.getDatabaseStructure().getName() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } /** * Test explicitly specifying both optimizer and increment */ @Test public void testExplicitOptimizerWithExplicitIncrementSize() { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, SequenceDialect.class.getName() ) .build(); // optimizer=none w/ increment > 1 => should honor optimizer try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.NONE.getExternalName() ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().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( serviceRegistry ); props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.HILO.getExternalName() ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().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( serviceRegistry ); props.setProperty( SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName() ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); // 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( PooledOptimizer.class, generator.getOptimizer().getClass() ); assertEquals( 20, generator.getOptimizer().getIncrementSize() ); assertEquals( 20, generator.getDatabaseStructure().getIncrementSize() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } @Test public void testPreferredPooledOptimizerSetting() { StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .applySetting( AvailableSettings.DIALECT, PooledSequenceDialect.class.getName() ) .build(); try { Properties props = buildGeneratorPropertiesBase( serviceRegistry ); props.setProperty( SequenceStyleGenerator.INCREMENT_PARAM, "20" ); SequenceStyleGenerator generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( PooledOptimizer.class, generator.getOptimizer().getClass() ); props.setProperty( Environment.PREFER_POOLED_VALUES_LO, "true" ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( PooledLoOptimizer.class, generator.getOptimizer().getClass() ); props.setProperty( Environment.PREFERRED_POOLED_OPTIMIZER, StandardOptimizerDescriptor.POOLED_LOTL.getExternalName() ); generator = new SequenceStyleGenerator(); generator.configure( StandardBasicTypes.LONG, props, serviceRegistry ); generator.registerExportables( new Database( new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry ) ) ); assertClassAssignability( SequenceStructure.class, generator.getDatabaseStructure().getClass() ); assertClassAssignability( PooledLoThreadLocalOptimizer.class, generator.getOptimizer().getClass() ); } finally { StandardServiceRegistryBuilder.destroy( serviceRegistry ); } } public static class TableDialect extends Dialect { public boolean supportsSequences() { return false; } } public static class SequenceDialect extends Dialect { public boolean supportsSequences() { return true; } public boolean supportsPooledSequences() { return false; } public String getSequenceNextValString(String sequenceName) throws MappingException { return ""; } } public static class PooledSequenceDialect extends SequenceDialect { public boolean supportsPooledSequences() { return true; } } }