allow use of SelectGenerator from annotations

- generalize @GenericGenerator to any Generator
- add tests for @GenericGenerator(type=SelectGenerator)
- move some logic for choosing the right InsertGeneratedIdentifierDelegate
  to the generators themselves
This commit is contained in:
Gavin 2022-12-17 17:57:05 +01:00 committed by Gavin King
parent 6536fe0d72
commit e8c2824976
13 changed files with 243 additions and 69 deletions

View File

@ -6,7 +6,7 @@
*/ */
package org.hibernate.annotations; package org.hibernate.annotations;
import org.hibernate.generator.InMemoryGenerator; import org.hibernate.generator.Generator;
import java.lang.annotation.Repeatable; import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -19,11 +19,10 @@ import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Defines a named identifier generator, an instance of the interface * Defines a named identifier generator, usually an instance of the interface
* {@link org.hibernate.id.IdentifierGenerator}. This allows the use of * {@link org.hibernate.id.IdentifierGenerator}. This allows the use of custom
* custom identifier generation strategies beyond those provided by the * identifier generation strategies beyond those provided by the four basic
* four basic JPA-defined {@linkplain jakarta.persistence.GenerationType * JPA-defined {@linkplain jakarta.persistence.GenerationType generation types}.
* generation types}.
* <p> * <p>
* A named generator may be associated with an entity class by: * A named generator may be associated with an entity class by:
* <ul> * <ul>
@ -74,17 +73,17 @@ public @interface GenericGenerator {
*/ */
String name(); String name();
/** /**
* The type of identifier generator, a class implementing {@link InMemoryGenerator} * The type of identifier generator, a class implementing {@link Generator}
* or, more commonly, {@link org.hibernate.id.IdentifierGenerator}. * or, more commonly, {@link org.hibernate.id.IdentifierGenerator}.
* *
* @since 6.2 * @since 6.2
*/ */
Class<? extends InMemoryGenerator> type() default InMemoryGenerator.class; Class<? extends Generator> type() default Generator.class;
/** /**
* The type of identifier generator, the name of either: * The type of identifier generator, the name of either:
* <ul> * <ul>
* <li>a built-in Hibernate id generator, or * <li>a built-in Hibernate id generator, or
* <li>a custom class implementing {@link InMemoryGenerator}, or, more commonly, * <li>a custom class implementing {@link Generator}, or, more commonly,
* {@link org.hibernate.id.IdentifierGenerator}. * {@link org.hibernate.id.IdentifierGenerator}.
* </ul> * </ul>
* *

View File

@ -81,6 +81,7 @@ import org.hibernate.cfg.annotations.QueryBinder;
import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.dialect.TimeZoneSupport;
import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.generator.Generator;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.GenericsHelper; import org.hibernate.internal.util.GenericsHelper;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
@ -98,7 +99,6 @@ import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.property.access.spi.PropertyAccessStrategy;
import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.generator.InMemoryGenerator;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType; import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.BasicJavaType;
@ -489,7 +489,7 @@ public final class AnnotationBinder {
else if ( generatorAnnotation instanceof GenericGenerator ) { else if ( generatorAnnotation instanceof GenericGenerator ) {
final GenericGenerator genericGenerator = (GenericGenerator) generatorAnnotation; final GenericGenerator genericGenerator = (GenericGenerator) generatorAnnotation;
definitionBuilder.setName( genericGenerator.name() ); definitionBuilder.setName( genericGenerator.name() );
final String strategy = genericGenerator.type().equals(InMemoryGenerator.class) final String strategy = genericGenerator.type().equals(Generator.class)
? genericGenerator.strategy() ? genericGenerator.strategy()
: genericGenerator.type().getName(); : genericGenerator.type().getName();
definitionBuilder.setStrategy( strategy ); definitionBuilder.setStrategy( strategy );

View File

@ -7,7 +7,9 @@
package org.hibernate.generator; package org.hibernate.generator;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.insert.BasicSelectingDelegate;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
/** /**
* A value generated by the database might be generated implicitly, by a trigger, or using * A value generated by the database might be generated implicitly, by a trigger, or using
@ -69,18 +71,15 @@ public interface InDatabaseGenerator extends Generator {
String[] getReferencedColumnValues(Dialect dialect); String[] getReferencedColumnValues(Dialect dialect);
/** /**
* The name of a property of the entity which may be used to locate the just-{@code insert}ed * The {@link InsertGeneratedIdentifierDelegate} used to retrieve the generates value if this
* row containing the generated value. Of course, the columns mapped by this property should * object is an identifier generator.
* form a unique key of the entity.
* <p> * <p>
* This is ignored by {@link org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor}, * This is ignored by {@link org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor},
* which handles multiple generators at once. This method arguably breaks the separation of * which handles multiple generators at once. This method arguably breaks the separation of
* concerns between the generator and the coordinating code. * concerns between the generator and the coordinating code.
*
* @see org.hibernate.id.SelectGenerator
*/ */
default String getUniqueKeyPropertyName(EntityPersister persister) { default InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) {
return null; return new BasicSelectingDelegate( persister, persister.getFactory().getJdbcServices().getDialect() );
} }
default boolean generatedByDatabase() { default boolean generatedByDatabase() {

View File

@ -9,6 +9,9 @@ package org.hibernate.id;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.id.factory.spi.StandardGenerator; import org.hibernate.id.factory.spi.StandardGenerator;
import org.hibernate.generator.InDatabaseGenerator; import org.hibernate.generator.InDatabaseGenerator;
import org.hibernate.id.insert.BasicSelectingDelegate;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.id.insert.InsertReturningDelegate;
/** /**
* An {@link InDatabaseGenerator} that handles {@code IDENTITY}/"autoincrement" columns * An {@link InDatabaseGenerator} that handles {@code IDENTITY}/"autoincrement" columns
@ -16,11 +19,23 @@ import org.hibernate.generator.InDatabaseGenerator;
* <p> * <p>
* Delegates to the {@link org.hibernate.dialect.identity.IdentityColumnSupport} provided * Delegates to the {@link org.hibernate.dialect.identity.IdentityColumnSupport} provided
* by the {@linkplain Dialect#getIdentityColumnSupport() dialect}. * by the {@linkplain Dialect#getIdentityColumnSupport() dialect}.
* <p>
* The actual work involved in retrieving the primary key value is the job of a
* {@link org.hibernate.id.insert.InsertGeneratedIdentifierDelegate}, either:
* <ul>
* <li>a {@link org.hibernate.id.insert.GetGeneratedKeysDelegate},
* <li>an {@link org.hibernate.id.insert.InsertReturningDelegate}, or a
* <li>a {@link org.hibernate.id.insert.BasicSelectingDelegate}.
* </ul>
*
* @see org.hibernate.dialect.identity.IdentityColumnSupport
* @see org.hibernate.id.insert.InsertGeneratedIdentifierDelegate
* *
* @author Christoph Sturm * @author Christoph Sturm
*/ */
public class IdentityGenerator public class IdentityGenerator
implements PostInsertIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, StandardGenerator { implements PostInsertIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, StandardGenerator {
@Override @Override
public boolean referenceColumnsInSql(Dialect dialect) { public boolean referenceColumnsInSql(Dialect dialect) {
return dialect.getIdentityColumnSupport().hasIdentityInsertKeyword(); return dialect.getIdentityColumnSupport().hasIdentityInsertKeyword();
@ -30,4 +45,18 @@ public class IdentityGenerator
public String[] getReferencedColumnValues(Dialect dialect) { public String[] getReferencedColumnValues(Dialect dialect) {
return new String[] { dialect.getIdentityColumnSupport().getIdentityInsertString() }; return new String[] { dialect.getIdentityColumnSupport().getIdentityInsertString() };
} }
@Override
public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) {
Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
if ( persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
return dialect.getIdentityColumnSupport().buildGetGeneratedKeysDelegate( persister, dialect );
}
else if ( dialect.getIdentityColumnSupport().supportsInsertSelectIdentity() ) {
return new InsertReturningDelegate( persister, dialect );
}
else {
return new BasicSelectingDelegate( persister, dialect );
}
}
} }

View File

@ -18,7 +18,7 @@ import java.util.Properties;
import static org.hibernate.generator.EventTypeSets.INSERT_ONLY; import static org.hibernate.generator.EventTypeSets.INSERT_ONLY;
/** /**
* The counterpart of {@link IdentifierGenerator} for values generated by the database. * The counterpart to {@link IdentifierGenerator} for values generated by the database.
* This interface is no longer the only way to handle database-generate identifiers. * This interface is no longer the only way to handle database-generate identifiers.
* Any {@link InDatabaseGenerator} with timing {@link EventTypeSets#INSERT_ONLY} may now * Any {@link InDatabaseGenerator} with timing {@link EventTypeSets#INSERT_ONLY} may now
* be used. * be used.

View File

@ -11,14 +11,16 @@ import java.util.Properties;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.generator.InDatabaseGenerator; import org.hibernate.generator.InDatabaseGenerator;
import org.hibernate.id.factory.spi.StandardGenerator; import org.hibernate.id.factory.spi.StandardGenerator;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
* A generator that {@code select}s the just-{@code insert}ed row to determine the * A generator that {@code select}s the just-{@code insert}ed row to determine the
* column value assigned by the database. The correct row is located * column value assigned by the database. The correct row is located using a unique
* using a unique key of the entity, either: * key of the entity, either:
* <ul> * <ul>
* <li>the mapped {@linkplain org.hibernate.annotations.NaturalId} of the entity, or * <li>the mapped {@linkplain org.hibernate.annotations.NaturalId} of the entity, or
* <li>a property specified using the parameter named {@code "key"}. * <li>a property specified using the parameter named {@code "key"}.
@ -26,11 +28,32 @@ import org.hibernate.type.Type;
* The second approach is provided for backward compatibility with older versions of * The second approach is provided for backward compatibility with older versions of
* Hibernate. * Hibernate.
* <p> * <p>
* This generator is intended for use with primary keys assigned by a database trigger
* or something similar, for example:
* <pre>{@code
* @Entity @Table(name="TableWithPKAssignedByTrigger")
* @GenericGenerator(name = "triggered", type = SelectGenerator.class)
* public class TriggeredEntity {
* @Id @GeneratedValue(generator = "triggered")
* private Long id;
*
* @NaturalId
* private String name;
*
* ...
* }
* }</pre>
* For tables with identity/autoincrement columns, use {@link IdentityGenerator}.
* <p>
* The actual work involved in retrieving the primary key value is the job of
* {@link org.hibernate.id.insert.UniqueKeySelectingDelegate}.
* <p>
* Arguably, this class breaks the natural separation of responsibility between the * Arguably, this class breaks the natural separation of responsibility between the
* {@linkplain InDatabaseGenerator generator} and the coordinating * {@linkplain InDatabaseGenerator generator} and the coordinating code, since its
* code, since it's role is to specify how the generated value is <em>retrieved</em>. * role is to specify how the generated value is <em>retrieved</em>.
* *
* @see org.hibernate.annotations.NaturalId * @see org.hibernate.annotations.NaturalId
* @see org.hibernate.id.insert.UniqueKeySelectingDelegate
* *
* @author Gavin King * @author Gavin King
*/ */
@ -43,8 +66,12 @@ public class SelectGenerator
uniqueKeyPropertyName = parameters.getProperty( "key" ); uniqueKeyPropertyName = parameters.getProperty( "key" );
} }
@Override /**
public String getUniqueKeyPropertyName(EntityPersister persister) { * The name of a property of the entity which may be used to locate the just-{@code insert}ed
* row containing the generated value. Of course, the columns mapped by this property should
* form a unique key of the entity.
*/
protected String getUniqueKeyPropertyName(EntityPersister persister) {
if ( uniqueKeyPropertyName != null ) { if ( uniqueKeyPropertyName != null ) {
return uniqueKeyPropertyName; return uniqueKeyPropertyName;
} }
@ -70,6 +97,12 @@ public class SelectGenerator
return persister.getPropertyNames()[naturalIdPropertyIndices[0]]; return persister.getPropertyNames()[naturalIdPropertyIndices[0]];
} }
@Override
public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) {
Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
return new UniqueKeySelectingDelegate( persister, dialect, getUniqueKeyPropertyName( persister ) );
}
@Override @Override
public boolean referenceColumnsInSql(Dialect dialect) { public boolean referenceColumnsInSql(Dialect dialect) {
return false; return false;

View File

@ -13,14 +13,13 @@ import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.InDatabaseGenerator;
import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectation;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.sql.model.ast.builder.TableInsertBuilder; import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
/** /**
* Each implementation defines a strategy for retrieving a primary key * Each implementation defines a strategy for retrieving a primary key
* {@linkplain InDatabaseGenerator generated by * {@linkplain org.hibernate.generator.InDatabaseGenerator generated by
* the database} from the database after execution of an {@code insert} * the database} from the database after execution of an {@code insert}
* statement. The generated primary key is usually an {@code IDENTITY} * statement. The generated primary key is usually an {@code IDENTITY}
* column, but in principle it might be something else, for example, * column, but in principle it might be something else, for example,
@ -32,9 +31,9 @@ import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
* <li>retrieving the generated identifier value using JDBC. * <li>retrieving the generated identifier value using JDBC.
* </ul> * </ul>
* The implementation should be written to handle any instance of * The implementation should be written to handle any instance of
* {@link InDatabaseGenerator}. * {@link org.hibernate.generator.InDatabaseGenerator}.
* *
* @see InDatabaseGenerator * @see org.hibernate.generator.InDatabaseGenerator
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */

View File

@ -2743,29 +2743,6 @@ public abstract class AbstractEntityPersister
return includeProperty[getVersionProperty()] || entityMetamodel.isVersionGeneratedByDatabase(); return includeProperty[getVersionProperty()] || entityMetamodel.isVersionGeneratedByDatabase();
} }
public boolean useGetGeneratedKeys() {
return getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled();
}
public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate() {
Dialect dialect = getFactory().getJdbcServices().getDialect();
if ( useGetGeneratedKeys() ) {
return dialect.getIdentityColumnSupport().buildGetGeneratedKeysDelegate(this, dialect );
}
else if ( dialect.getIdentityColumnSupport().supportsInsertSelectIdentity() ) {
return new InsertReturningDelegate(this, dialect );
}
else {
return new BasicSelectingDelegate(this, dialect );
}
}
@Override
public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegateForProperty(String uniqueKeyPropertyName) {
Dialect dialect = getFactory().getJdbcServices().getDialect();
return new UniqueKeySelectingDelegate( this, dialect, uniqueKeyPropertyName );
}
@Override @Override
public String getIdentitySelectString() { public String getIdentitySelectString() {
//TODO: cache this in an instvar //TODO: cache this in an instvar
@ -3082,10 +3059,7 @@ public abstract class AbstractEntityPersister
private void doLateInit() { private void doLateInit() {
if ( isIdentifierAssignedByInsert() ) { if ( isIdentifierAssignedByInsert() ) {
final InDatabaseGenerator generator = (InDatabaseGenerator) getGenerator(); final InDatabaseGenerator generator = (InDatabaseGenerator) getGenerator();
final String uniqueKeyPropertyName = generator.getUniqueKeyPropertyName(this); identityDelegate = generator.getGeneratedIdentifierDelegate( this );
identityDelegate = uniqueKeyPropertyName == null
? getGeneratedIdentifierDelegate()
: getGeneratedIdentifierDelegateForProperty( uniqueKeyPropertyName );
} }
tableMappings = buildTableMappings(); tableMappings = buildTableMappings();

View File

@ -1015,14 +1015,6 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc
boolean canUseReferenceCacheEntries(); boolean canUseReferenceCacheEntries();
default InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate() {
return null;
}
default InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegateForProperty(String uniqueKeyPropertyName) {
return null;
}
/** /**
* The property name of the "special" identifier property in HQL * The property name of the "special" identifier property in HQL
* *

View File

@ -25,6 +25,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType; import org.hibernate.generator.EventType;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.enhanced.Optimizer; import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.id.insert.Binder; import org.hibernate.id.insert.Binder;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
@ -559,10 +560,8 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
if ( generator.generatedByDatabase() ) { if ( generator.generatedByDatabase() ) {
final InDatabaseGenerator databaseGenerator = (InDatabaseGenerator) generator; final InDatabaseGenerator databaseGenerator = (InDatabaseGenerator) generator;
final String uniqueKeyPropertyName = databaseGenerator.getUniqueKeyPropertyName( entityPersister ); final InsertGeneratedIdentifierDelegate identifierDelegate =
final InsertGeneratedIdentifierDelegate identifierDelegate = uniqueKeyPropertyName == null databaseGenerator.getGeneratedIdentifierDelegate( (PostInsertIdentityPersister) entityPersister );
? entityPersister.getGeneratedIdentifierDelegate()
: entityPersister.getGeneratedIdentifierDelegateForProperty( uniqueKeyPropertyName );
final String finalSql = identifierDelegate.prepareIdentifierGeneratingInsert( jdbcInsert.getSqlString() ); final String finalSql = identifierDelegate.prepareIdentifierGeneratingInsert( jdbcInsert.getSqlString() );
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping(); final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final ValueBinder jdbcValueBinder = identifierMapping.getJdbcMapping().getJdbcValueBinder(); final ValueBinder jdbcValueBinder = identifierMapping.getJdbcMapping().getJdbcValueBinder();

View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.orm.test.generatedkeys.select" default-access="field">
<database-object>
<create>
<![CDATA[CREATE OR REPLACE TRIGGER t_i_my_entity
BEFORE INSERT ON my_entity
FOR EACH ROW
BEGIN
select nvl( max(id), 0 ) + 1
into :new.id
from my_entity;
END;]]>
</create>
<drop>
<![CDATA[DROP TRIGGER t_i_my_entity]]>
</drop>
<dialect-scope name="org.hibernate.dialect.OracleDialect"/>
</database-object>
</hibernate-mapping>

View File

@ -0,0 +1,48 @@
/*
* 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.orm.test.generatedkeys.selectannotated;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.NaturalId;
import org.hibernate.id.SelectGenerator;
/**
* @author <a href="mailto:steve@hibernate.org">Steve Ebersole </a>
*/
@Entity @Table(name="my_entity")
@GenericGenerator(name = "triggered", type = SelectGenerator.class)
public class MyEntity {
@Id @GeneratedValue(generator = "triggered")
private Long id;
@NaturalId
private String name;
public MyEntity() {
}
public MyEntity(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.orm.test.generatedkeys.selectannotated;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.EnumSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
/**
* @author Steve Ebersole
* @author Marco Belladelli
*/
@DomainModel(
annotatedClasses = MyEntity.class,
xmlMappings = "org/hibernate/orm/test/generatedkeys/selectannotated/MyEntity.hbm.xml"
)
@SessionFactory
@RequiresDialect(value = OracleDialect.class)
public class SelectGeneratorTest {
@Test
public void testJDBC3GetGeneratedKeysSupportOnOracle(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
MyEntity e = new MyEntity( "entity-1" );
session.persist( e );
// this insert should happen immediately!
assertEquals( Long.valueOf( 1L ), e.getId(), "id not generated through forced insertion" );
session.remove( e );
}
);
}
@Test
@JiraKey("HHH-15900")
public void testGeneratedKeyNotIdentityColumn(SessionFactoryScope scope) throws IOException {
File output = File.createTempFile( "schema_export", ".sql" );
output.deleteOnExit();
final SchemaExport schemaExport = new SchemaExport();
schemaExport.setOutputFile( output.getAbsolutePath() );
schemaExport.execute(
EnumSet.of( TargetType.SCRIPT ),
SchemaExport.Action.CREATE,
scope.getMetadataImplementor()
);
String fileContent = new String( Files.readAllBytes( output.toPath() ) );
assertFalse( fileContent.toLowerCase().contains( "identity" ), "Column was generated as identity" );
}
}