HHH-6790 - Sequence per entity for enhanced SequenceStyleGenerator

This commit is contained in:
Lukasz Antoniak 2012-04-21 12:32:42 +02:00
parent fc068be040
commit cfa729d8b3
11 changed files with 211 additions and 6 deletions

View File

@ -1883,6 +1883,19 @@ class Person {
used to hold the value.</para>
</listitem>
<listitem>
<para><literal>prefer_sequence_per_entity</literal> (optional -
defaults to <literal>false</literal>): should we create
separate sequence for each entity that share current generator
based on its name?</para>
</listitem>
<listitem>
<para><literal>sequence_per_entity_suffix</literal> (optional -
defaults to <literal>_SEQ</literal>): suffix added to the name
of a dedicated sequence.</para>
</listitem>
<listitem>
<para><literal>optimizer</literal> (optional - defaults to
<literal>none</literal>): See <xref

View File

@ -52,7 +52,12 @@ public interface IdentifierGenerator {
* 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";
/**
* Generate a new identifier.
* @param session

View File

@ -48,6 +48,12 @@ import org.hibernate.type.Type;
* a sequence. These variations are encapsulated by the {@link DatabaseStructure}
* interface internally.
* <p/>
* <b>NOTE</b> that by default we utilize a single database sequence for all
* generators. The configuration parameter {@link #CONFIG_PREFER_SEQUENCE_PER_ENTITY}
* can be used to create dedicated sequence for each entity based on its name.
* Sequence suffix can be controlled with {@link #CONFIG_SEQUENCE_PER_ENTITY_SUFFIX}
* option.
* <p/>
* General configuration parameters:
* <table>
* <tr>
@ -96,6 +102,7 @@ import org.hibernate.type.Type;
* </table>
*
* @author Steve Ebersole
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class SequenceStyleGenerator
implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, Configurable {
@ -119,6 +126,10 @@ public class SequenceStyleGenerator
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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public static final String VALUE_COLUMN_PARAM = "value_column";
@ -209,8 +220,13 @@ 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 );
// 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 );
String sequenceName = ConfigurationHelper.getString( SEQUENCE_PARAM, params, DEF_SEQUENCE_NAME );
sequenceName = ConfigurationHelper.getString( SEQUENCE_PARAM, params, sequenceName );
if ( sequenceName.indexOf( '.' ) < 0 ) {
sequenceName = normalizer.normalizeIdentifierQuoting( sequenceName );
String schemaName = params.getProperty( SCHEMA );

View File

@ -151,6 +151,7 @@ public class SimpleValue implements KeyValue {
//pass the entity-name, if not a collection-id
if (rootClass!=null) {
params.setProperty( IdentifierGenerator.ENTITY_NAME, rootClass.getEntityName() );
params.setProperty( IdentifierGenerator.JPA_ENTITY_NAME, rootClass.getJpaEntityName() );
}
//init the table here instead of earlier, so that we can get a quoted table name

View File

@ -0,0 +1,28 @@
package org.hibernate.test.annotations.id.generationmappings;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity
@Table(name = "DEDICATED_SEQ_TBL1")
public class DedicatedSequenceEntity1 implements Serializable {
public static final String SEQUENCE_SUFFIX = "_GEN";
private Long id;
@Id
@GeneratedValue(generator = "SequencePerEntityGenerator")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -0,0 +1,28 @@
package org.hibernate.test.annotations.id.generationmappings;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Entity(name = DedicatedSequenceEntity2.ENTITY_NAME)
@Table(name = "DEDICATED_SEQ_TBL2")
public class DedicatedSequenceEntity2 implements Serializable {
public static final String ENTITY_NAME = "DEDICATED2";
private Long id;
@Id
@GeneratedValue(generator = "SequencePerEntityGenerator")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -31,7 +31,9 @@ import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
@ -43,6 +45,7 @@ import static org.junit.Assert.assertTrue;
* hibernate generators using the new scheme
*
* @author Steve Ebersole
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase {
@Override
@ -51,10 +54,17 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase {
MinimalSequenceEntity.class,
CompleteSequenceEntity.class,
AutoEntity.class,
MinimalTableEntity.class
MinimalTableEntity.class,
DedicatedSequenceEntity1.class,
DedicatedSequenceEntity2.class
};
}
@Override
protected String[] getAnnotatedPackages() {
return new String[] { this.getClass().getPackage().getName() };
}
@Override
protected void configure(Configuration cfg) {
super.configure( cfg );
@ -119,4 +129,28 @@ public class NewGeneratorMappingsTest extends BaseCoreFunctionalTestCase {
assertEquals( 50, tabGenerator.getIncrementSize() );
assertTrue( OptimizerFactory.PooledOptimizer.class.isInstance( tabGenerator.getOptimizer() ) );
}
@Test
@TestForIssue(jiraKey = "HHH-6790")
public void testSequencePerEntity() {
// Checking first entity.
EntityPersister persister = sessionFactory().getEntityPersister( DedicatedSequenceEntity1.class.getName() );
IdentifierGenerator generator = persister.getIdentifierGenerator();
assertTrue( SequenceStyleGenerator.class.isInstance( generator ) );
SequenceStyleGenerator seqGenerator = (SequenceStyleGenerator) generator;
assertEquals(
StringHelper.unqualifyEntityName( DedicatedSequenceEntity1.class.getName() ) + DedicatedSequenceEntity1.SEQUENCE_SUFFIX,
seqGenerator.getDatabaseStructure().getName()
);
// Checking second entity.
persister = sessionFactory().getEntityPersister( DedicatedSequenceEntity2.class.getName() );
generator = persister.getIdentifierGenerator();
assertTrue( SequenceStyleGenerator.class.isInstance( generator ) );
seqGenerator = (SequenceStyleGenerator) generator;
assertEquals(
DedicatedSequenceEntity2.ENTITY_NAME + DedicatedSequenceEntity1.SEQUENCE_SUFFIX,
seqGenerator.getDatabaseStructure().getName()
);
}
}

View File

@ -0,0 +1,12 @@
@GenericGenerator(name = "SequencePerEntityGenerator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "prefer_sequence_per_entity", value = "true"),
@Parameter(name = "sequence_per_entity_suffix", value = DedicatedSequenceEntity1.SEQUENCE_SUFFIX)
}
)
package org.hibernate.test.annotations.id.generationmappings;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

View File

@ -28,6 +28,8 @@ import org.junit.Test;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.mapping.Column;
import org.hibernate.test.annotations.id.generationmappings.DedicatedSequenceEntity1;
import org.hibernate.test.annotations.id.generationmappings.DedicatedSequenceEntity2;
import org.hibernate.test.annotations.id.sequences.entities.Ball;
import org.hibernate.test.annotations.id.sequences.entities.BreakDance;
import org.hibernate.test.annotations.id.sequences.entities.Computer;
@ -47,6 +49,7 @@ import org.hibernate.test.annotations.id.sequences.entities.Store;
import org.hibernate.test.annotations.id.sequences.entities.Tree;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static junit.framework.Assert.assertEquals;
@ -54,6 +57,7 @@ import static junit.framework.Assert.assertNotNull;
/**
* @author Emmanuel Bernard
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@SuppressWarnings("unchecked")
@RequiresDialectFeature(DialectChecks.SupportsSequences.class)
@ -291,6 +295,23 @@ public class IdTest extends BaseCoreFunctionalTestCase {
s.close();
}
@Test
@TestForIssue(jiraKey = "HHH-6790")
public void testSequencePerEntity() {
Session session = openSession();
session.beginTransaction();
DedicatedSequenceEntity1 entity1 = new DedicatedSequenceEntity1();
DedicatedSequenceEntity2 entity2 = new DedicatedSequenceEntity2();
session.persist( entity1 );
session.persist( entity2 );
session.getTransaction().commit();
assertEquals( 1, entity1.getId().intValue() );
assertEquals( 1, entity2.getId().intValue() );
session.close();
}
@Test
public void testColumnDefinition() {
Column idCol = ( Column ) configuration().getClassMapping( Ball.class.getName() )
@ -325,7 +346,8 @@ public class IdTest extends BaseCoreFunctionalTestCase {
Department.class, Dog.class, Computer.class, Home.class,
Phone.class, Tree.class, FirTree.class, Footballer.class,
SoundSystem.class, Furniture.class, GoalKeeper.class,
BreakDance.class, Monkey.class
BreakDance.class, Monkey.class, DedicatedSequenceEntity1.class,
DedicatedSequenceEntity2.class
};
}
@ -333,7 +355,8 @@ public class IdTest extends BaseCoreFunctionalTestCase {
protected String[] getAnnotatedPackages() {
return new String[] {
"org.hibernate.test.annotations",
"org.hibernate.test.annotations.id"
"org.hibernate.test.annotations.id",
"org.hibernate.test.annotations.id.generationmappings"
};
}

View File

@ -29,6 +29,7 @@ import org.hibernate.Session;
import org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertClassAssignability;
@ -36,11 +37,12 @@ import static org.junit.Assert.assertEquals;
/**
* @author Steve Ebersole
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class BasicSequenceTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {
return new String[] { "idgen/enhanced/sequence/Basic.hbm.xml" };
return new String[] { "idgen/enhanced/sequence/Basic.hbm.xml", "idgen/enhanced/sequence/Dedicated.hbm.xml" };
}
@Test
@ -71,4 +73,25 @@ public class BasicSequenceTest extends BaseCoreFunctionalTestCase {
s.getTransaction().commit();
s.close();
}
@Test
@TestForIssue(jiraKey = "HHH-6790")
public void testSequencePerEntity() {
final String overriddenEntityName = "SpecialEntity";
EntityPersister persister = sessionFactory().getEntityPersister( overriddenEntityName );
assertClassAssignability( SequenceStyleGenerator.class, persister.getIdentifierGenerator().getClass() );
SequenceStyleGenerator generator = (SequenceStyleGenerator) persister.getIdentifierGenerator();
assertEquals( overriddenEntityName + SequenceStyleGenerator.DEF_SEQUENCE_SUFFIX, generator.getDatabaseStructure().getName() );
Session s = openSession();
s.beginTransaction();
Entity entity1 = new Entity( "1" );
s.save( overriddenEntityName, entity1 );
Entity entity2 = new Entity( "2" );
s.save( overriddenEntityName, entity2 );
s.getTransaction().commit();
assertEquals( 1, entity1.getId().intValue() );
assertEquals( 2, entity2.getId().intValue() );
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
Demonstrates use of the enhanced sequence-based identifier
generator, with dedicated sequence for each entity.
-->
<hibernate-mapping package="org.hibernate.test.idgen.enhanced.sequence">
<class name="Entity" table="ID_SEQ_BSC_ENTITY" entity-name="SpecialEntity">
<id name="id" column="ID" type="long">
<generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
<param name="prefer_sequence_per_entity">true</param>
</generator>
</id>
<property name="name" type="string"/>
</class>
</hibernate-mapping>