generalize the InsertReturningDelegates to handle arbitrary InDatabaseGenerators

the bit limitation is that an InDatabaseGenerator can be multi-column, but all the
InsertReturningDelegates assume that they map exactly one IDENTITY column
This commit is contained in:
Gavin 2022-12-02 11:09:08 +01:00 committed by Gavin King
parent 7b0ca427dd
commit 4195772c15
15 changed files with 175 additions and 142 deletions

View File

@ -13,6 +13,7 @@ import org.hibernate.metamodel.mapping.NonTransientException;
*
* todo (6.0) : prior going final, we need to find all usages of this and implement all methods (or throw a different exception)
*/
@Internal
public class NotYetImplementedFor6Exception extends RuntimeException implements NonTransientException,
NotImplementedYetException {
public NotYetImplementedFor6Exception(String message) {

View File

@ -12,7 +12,6 @@ import java.math.BigInteger;
import java.math.RoundingMode;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Locale;
import java.util.Objects;
@ -36,8 +35,8 @@ public final class IdentifierGeneratorHelper {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( IdentifierGeneratorHelper.class );
/**
* Marker object returned from {@link IdentifierGenerator#generate} to indicate that we should short-circuit any
* continued generated id checking. Currently this is only used in the case of the
* Marker object returned from {@link IdentifierGenerator#generate} to indicate that we should
* short-circuit any continued generated id checking. Currently, this is only used in the case of the
* {@linkplain ForeignGenerator foreign} generator as a way to signal that we should use the associated
* entity's id value.
*/
@ -49,8 +48,8 @@ public final class IdentifierGeneratorHelper {
};
/**
* Marker object returned from {@link IdentifierGenerator#generate} to indicate that the entity's identifier will
* be generated as part of the database insertion.
* Marker object returned from {@link IdentifierGenerator#generate} to indicate that the entity's
* identifier will be generated as part of the database insertion.
*/
public static final Serializable POST_INSERT_INDICATOR = new Serializable() {
@Override
@ -63,7 +62,7 @@ public final class IdentifierGeneratorHelper {
/**
* Get the generated identifier when using identity columns
*
* @param rs The result set from which to extract the generated identity.
* @param resultSet The result set from which to extract the generated identity.
* @param identityColumn The name of the identifier column
* @param type The expected type mapping for the identity value.
* @param dialect The current database dialect.
@ -74,16 +73,16 @@ public final class IdentifierGeneratorHelper {
* @throws HibernateException Indicates a problem reading back a generated identity value.
*/
public static Object getGeneratedIdentity(
ResultSet rs,
ResultSet resultSet,
NavigableRole insertionTargetRole,
String identityColumn,
Type type,
Dialect dialect) throws SQLException {
if ( !rs.next() ) {
if ( !resultSet.next() ) {
throw new HibernateException( "The database returned no natively generated identity value : " + insertionTargetRole.getFullPath() );
}
final Object id = get( rs, insertionTargetRole, identityColumn, type, dialect );
final Object id = get( resultSet, insertionTargetRole, identityColumn, type, dialect );
LOG.debugf( "Natively generated identity (%s) : %s", insertionTargetRole.getFullPath(), id );
return id;
}
@ -92,7 +91,7 @@ public final class IdentifierGeneratorHelper {
* Extract the value from the result set (which is assumed to already have been positioned to the appropriate row)
* and wrp it in the appropriate Java numeric type.
*
* @param rs The result set from which to extract the value.
* @param resultSet The result set from which to extract the value.
* @param identifier The name of the identifier column
* @param type The expected type of the value.
* @param dialect The current database dialect.
@ -103,52 +102,44 @@ public final class IdentifierGeneratorHelper {
* @throws IdentifierGenerationException Indicates an unknown type.
*/
public static Object get(
ResultSet rs,
ResultSet resultSet,
NavigableRole insertionTargetRole,
String identifier,
Type type,
Dialect dialect)
throws SQLException, IdentifierGenerationException {
if ( type instanceof ResultSetIdentifierConsumer ) {
return ( (ResultSetIdentifierConsumer) type ).consumeIdentifier( rs );
return ( (ResultSetIdentifierConsumer) type ).consumeIdentifier( resultSet );
}
if ( type instanceof CustomType ) {
final CustomType<?> customType = (CustomType<?>) type;
if (customType.getUserType() instanceof ResultSetIdentifierConsumer) {
return ( (ResultSetIdentifierConsumer) customType.getUserType() ).consumeIdentifier( rs );
if ( customType.getUserType() instanceof ResultSetIdentifierConsumer ) {
return ( (ResultSetIdentifierConsumer) customType.getUserType() ).consumeIdentifier( resultSet );
}
}
ResultSetMetaData resultSetMetaData;
int columnCount = 1;
try {
resultSetMetaData = rs.getMetaData();
columnCount = resultSetMetaData.getColumnCount();
}
catch (Exception e) {
//Oracle driver will throw NPE
}
int columnCount = getColumnCount( resultSet );
final Class<?> clazz = type.getReturnedClass();
if ( columnCount == 1 ) {
if ( clazz == Long.class ) {
return rs.getLong( 1 );
return resultSet.getLong( 1 );
}
else if ( clazz == Integer.class ) {
return rs.getInt( 1 );
return resultSet.getInt( 1 );
}
else if ( clazz == Short.class ) {
return rs.getShort( 1 );
return resultSet.getShort( 1 );
}
else if ( clazz == String.class ) {
return rs.getString( 1 );
return resultSet.getString( 1 );
}
else if ( clazz == BigInteger.class ) {
return rs.getBigDecimal( 1 )
return resultSet.getBigDecimal( 1 )
.setScale( 0, RoundingMode.UNNECESSARY )
.toBigInteger();
}
else if ( clazz == BigDecimal.class ) {
return rs.getBigDecimal( 1 )
return resultSet.getBigDecimal( 1 )
.setScale( 0, RoundingMode.UNNECESSARY );
}
else {
@ -165,21 +156,20 @@ public final class IdentifierGeneratorHelper {
}
else {
try {
return extractIdentifier( rs, identifier, type, clazz );
return extractIdentifier( resultSet, identifier, type, clazz );
}
catch (SQLException e) {
if ( StringHelper.isQuoted( identifier, dialect ) ) {
return extractIdentifier( rs, StringHelper.unquote( identifier, dialect ), type, clazz );
return extractIdentifier( resultSet, StringHelper.unquote( identifier, dialect ), type, clazz );
}
throw e;
}
}
}
private static int getColumnCount(ResultSet rs) {
private static int getColumnCount(ResultSet resultSet) {
try {
final ResultSetMetaData resultSetMetaData = rs.getMetaData();
return resultSetMetaData.getColumnCount();
return resultSet.getMetaData().getColumnCount();
}
catch (Exception e) {
//Oracle driver will throw NPE
@ -724,6 +714,8 @@ public final class IdentifierGeneratorHelper {
}
}
/**
* Disallow instantiation of IdentifierGeneratorHelper.
*/

View File

@ -12,16 +12,15 @@ import java.sql.SQLException;
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.pretty.MessageHelper;
/**
* Abstract InsertGeneratedIdentifierDelegate implementation where the
* underlying strategy causes the generated identifier to be returned as an
* effect of performing the insert statement. Thus, there is no need for an
* additional sql statement to determine the generated identifier.
* Abstract {@link InsertGeneratedIdentifierDelegate} implementation where
* the underlying strategy causes the generated identifier to be returned as
* an effect of performing the insert statement. Thus, there is no need for
* an additional sql statement to determine the generated identifier.
*
* @author Steve Ebersole
*/
@ -38,16 +37,9 @@ public abstract class AbstractReturningDelegate implements InsertGeneratedIdenti
JdbcValueBindings valueBindings,
Object entity,
SharedSessionContractImplementor session) {
final SqlStatementLogger sqlStatementLogger = session.getJdbcServices().getSqlStatementLogger();
sqlStatementLogger.logStatement( insertStatementDetails.getSqlString() );
session.getJdbcServices().getSqlStatementLogger().logStatement( insertStatementDetails.getSqlString() );
valueBindings.beforeStatement( insertStatementDetails, session );
return executeAndExtract(
insertStatementDetails.getSqlString(),
insertStatementDetails.getStatement(),
session
);
return executeAndExtract( insertStatementDetails.getSqlString(), insertStatementDetails.getStatement(), session );
}
@Override

View File

@ -14,7 +14,6 @@ import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.MutationStatementPreparer;
import org.hibernate.engine.jdbc.spi.StatementPreparer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.PostInsertIdentityPersister;
@ -23,9 +22,9 @@ import org.hibernate.pretty.MessageHelper;
import static java.sql.Statement.NO_GENERATED_KEYS;
/**
* Abstract InsertGeneratedIdentifierDelegate implementation where the
* underlying strategy requires a subsequent select after the insert
* to determine the generated identifier.
* Abstract {@link InsertGeneratedIdentifierDelegate} implementation where
* the underlying strategy requires a subsequent {@code select} after the
* {@code insert} to determine the generated identifier.
*
* @author Steve Ebersole
*/
@ -81,9 +80,9 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
try {
bindParameters( entity, idSelect, session );
final ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( idSelect );
final ResultSet resultSet = session.getJdbcCoordinator().getResultSetReturn().extract( idSelect );
try {
return extractGeneratedValue( entity, rs, session );
return extractGeneratedValue( entity, resultSet, session );
}
catch (SQLException e) {
throw jdbcServices.getSqlExceptionHelper().convert(

View File

@ -9,23 +9,23 @@ package org.hibernate.id.insert;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.jdbc.Expectation;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
import org.hibernate.tuple.InDatabaseGenerator;
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
/**
* Delegate for dealing with IDENTITY columns where the dialect requires an
* additional command execution to retrieve the generated IDENTITY value
* Delegate for dealing with {@code IDENTITY} columns where the dialect requires an
* additional command execution to retrieve the generated {@code IDENTITY} value
*/
public class BasicSelectingDelegate extends AbstractSelectingDelegate {
private final PostInsertIdentityPersister persister;
@ -40,7 +40,7 @@ public class BasicSelectingDelegate extends AbstractSelectingDelegate {
@Override
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
insert.addGeneratedColumns( persister.getRootTableKeyColumnNames(), (InDatabaseGenerator) persister.getGenerator() );
return insert;
}
@ -52,13 +52,16 @@ public class BasicSelectingDelegate extends AbstractSelectingDelegate {
final TableInsertBuilder builder =
new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
IdentityColumnSupport identityColumnSupport = dialect.getIdentityColumnSupport();
if ( identityColumnSupport.hasIdentityInsertKeyword() ) {
builder.addKeyColumn(
identifierMapping.getSelectionExpression(),
identityColumnSupport.getIdentityInsertString(),
identifierMapping.getJdbcMapping()
);
final InDatabaseGenerator generator = (InDatabaseGenerator) persister.getGenerator();
if ( generator.referenceColumnsInSql( dialect ) ) {
final String[] columnNames = persister.getRootTableKeyColumnNames();
final String[] columnValues = generator.getReferencedColumnValues( dialect );
if ( columnValues.length != columnNames.length ) {
throw new MappingException("wrong number of generated columns");
}
for ( int i = 0; i < columnValues.length; i++ ) {
builder.addKeyColumn( columnNames[i], columnValues[i], identifierMapping.getJdbcMapping() );
}
}
return builder;
@ -70,12 +73,12 @@ public class BasicSelectingDelegate extends AbstractSelectingDelegate {
}
@Override
protected Object extractGeneratedValue(Object entity, ResultSet rs, SharedSessionContractImplementor session)
protected Object extractGeneratedValue(Object entity, ResultSet resultSet, SharedSessionContractImplementor session)
throws SQLException {
return getGeneratedIdentity(
rs,
resultSet,
persister.getNavigableRole(),
persister.getRootTableKeyColumnNames()[0],
DelegateHelper.getKeyColumnName( persister ),
persister.getIdentifierType(),
session.getJdbcServices().getJdbcEnvironment().getDialect()
);

View File

@ -0,0 +1,23 @@
/*
* 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.insert;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.id.PostInsertIdentityPersister;
class DelegateHelper {
static String getKeyColumnName(PostInsertIdentityPersister persister) {
String[] columnNames = persister.getRootTableKeyColumnNames();
if ( columnNames.length != 1 ) {
//TODO: remove this limitation
throw new NotYetImplementedFor6Exception("GetGeneratedKeysDelegate does not yet support multi-column Generators");
}
return columnNames[0];
}
}

View File

@ -11,24 +11,28 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Locale;
import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.MutationStatementPreparer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.jdbc.Expectation;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
import org.hibernate.tuple.InDatabaseGenerator;
import static java.sql.Statement.RETURN_GENERATED_KEYS;
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
/**
* Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys
* Delegate for dealing with {@code IDENTITY} columns using the JDBC3 method
* {@link PreparedStatement#getGeneratedKeys()}.
*
* @author Andrea Boriero
*/
@ -45,7 +49,7 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
@Override
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
insert.addGeneratedColumns( persister.getRootTableKeyColumnNames(), (InDatabaseGenerator) persister.getGenerator() );
return insert;
}
@ -53,16 +57,20 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
public TableInsertBuilder createTableInsertBuilder(
BasicEntityIdentifierMapping identifierMapping,
Expectation expectation,
SessionFactoryImplementor sessionFactory) {
final TableInsertBuilder builder = new TableInsertBuilderStandard(
persister,
persister.getIdentifierTableMapping(),
sessionFactory
);
SessionFactoryImplementor factory) {
final TableInsertBuilder builder =
new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
final String value = dialect.getIdentityColumnSupport().getIdentityInsertString();
if ( value != null ) {
builder.addKeyColumn( persister.getRootTableKeyColumnNames()[0], value, identifierMapping.getJdbcMapping() );
final InDatabaseGenerator generator = (InDatabaseGenerator) persister.getGenerator();
if ( generator.referenceColumnsInSql( dialect ) ) {
final String[] columnNames = persister.getRootTableKeyColumnNames();
final String[] columnValues = generator.getReferencedColumnValues( dialect );
if ( columnValues.length != columnNames.length ) {
throw new MappingException("wrong number of generated columns");
}
for ( int i = 0; i < columnValues.length; i++ ) {
builder.addKeyColumn( columnNames[i], columnValues[i], identifierMapping.getJdbcMapping() );
}
}
return builder;
@ -70,15 +78,10 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
@Override
public PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session) {
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
final MutationStatementPreparer statementPreparer = jdbcCoordinator.getMutationStatementPreparer();
return statementPreparer.prepareStatement(
insertSql,
PreparedStatement.RETURN_GENERATED_KEYS
);
return session.getJdbcCoordinator().getMutationStatementPreparer()
.prepareStatement( insertSql, RETURN_GENERATED_KEYS );
}
@Override
public Object performInsert(
PreparedStatementDetails insertStatementDetails,
@ -99,12 +102,12 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
jdbcCoordinator.getResultSetReturn().executeUpdate( insertStatement );
try {
final ResultSet rs = insertStatement.getGeneratedKeys();
final ResultSet resultSet = insertStatement.getGeneratedKeys();
try {
return IdentifierGeneratorHelper.getGeneratedIdentity(
rs,
return getGeneratedIdentity(
resultSet,
persister.getNavigableRole(),
persister.getRootTableKeyColumnNames()[ 0 ],
DelegateHelper.getKeyColumnName( persister ),
persister.getIdentifierType(),
jdbcServices.getJdbcEnvironment().getDialect()
);
@ -121,11 +124,11 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
);
}
finally {
if ( rs != null ) {
if ( resultSet != null ) {
jdbcCoordinator
.getLogicalConnection()
.getResourceRegistry()
.release( rs, insertStatement );
.release( resultSet, insertStatement );
}
}
}
@ -153,12 +156,12 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
jdbcCoordinator.getResultSetReturn().executeUpdate( insertStatement );
try {
final ResultSet rs = insertStatement.getGeneratedKeys();
final ResultSet resultSet = insertStatement.getGeneratedKeys();
try {
return IdentifierGeneratorHelper.getGeneratedIdentity(
rs,
return getGeneratedIdentity(
resultSet,
persister.getNavigableRole(),
persister.getRootTableKeyColumnNames()[0],
DelegateHelper.getKeyColumnName( persister ),
persister.getIdentifierType(),
jdbcServices.getJdbcEnvironment().getDialect()
);
@ -171,11 +174,8 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
);
}
finally {
if ( rs != null ) {
jdbcCoordinator
.getLogicalConnection()
.getResourceRegistry()
.release( rs, insertStatement );
if ( resultSet != null ) {
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement );
}
}
}

View File

@ -9,8 +9,8 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.sql.Insert;
/**
* Nothing more than a distinguishing subclass of Insert used to indicate
* intent. Some subclasses of this also provided some additional
* Nothing more than a distinguishing subclass of {@link Insert} used to
* indicate intent. Some subclasses of this also provided some additional
* functionality or semantic to the generated SQL statement string.
*
* @author Steve Ebersole

View File

@ -17,17 +17,18 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.jdbc.Expectation;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
import org.hibernate.tuple.InDatabaseGenerator;
import static java.sql.Statement.NO_GENERATED_KEYS;
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
/**
* Delegate for dealing with IDENTITY columns where the dialect supports returning
* the generated IDENTITY value directly from the insert statement.
* Delegate for dealing with {@code IDENTITY} columns where the dialect supports
* returning the generated {@code IDENTITY} value directly from the insert statement.
*
* @see org.hibernate.id.IdentityGenerator
* @see IdentityColumnSupport#supportsInsertSelectIdentity()
@ -45,7 +46,7 @@ public class InsertReturningDelegate extends AbstractReturningDelegate {
@Override
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
InsertSelectIdentityInsert insert = new InsertSelectIdentityInsert( dialect );
insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[ 0 ] );
insert.addGeneratedColumns( persister.getRootTableKeyColumnNames(), (InDatabaseGenerator) persister.getGenerator() );
return insert;
}
@ -65,13 +66,13 @@ public class InsertReturningDelegate extends AbstractReturningDelegate {
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
final JdbcServices jdbcServices = session.getJdbcServices();
final ResultSet rs = jdbcCoordinator.getResultSetReturn().execute( insertStatement );
final ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( insertStatement );
try {
return IdentifierGeneratorHelper.getGeneratedIdentity(
rs,
return getGeneratedIdentity(
resultSet,
persister.getNavigableRole(),
persister.getRootTableKeyColumnNames()[ 0 ],
DelegateHelper.getKeyColumnName( persister ),
persister.getIdentifierType(),
jdbcServices.getJdbcEnvironment().getDialect()
);
@ -84,10 +85,7 @@ public class InsertReturningDelegate extends AbstractReturningDelegate {
);
}
finally {
jdbcCoordinator
.getLogicalConnection()
.getResourceRegistry()
.release( rs, insertStatement );
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement );
}
}

View File

@ -5,12 +5,14 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.id.insert;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.sql.Insert;
import org.hibernate.tuple.InDatabaseGenerator;
/**
* Specialized IdentifierGeneratingInsert which appends the database
* specific clause which signifies to return generated IDENTITY values
* Specialized {@link IdentifierGeneratingInsert} which appends the database
* specific clause which signifies to return generated {@code IDENTITY} values
* to the end of the insert statement.
*
* @author Steve Ebersole
@ -23,6 +25,17 @@ public class InsertSelectIdentityInsert extends IdentifierGeneratingInsert {
return super.addIdentityColumn( columnName );
}
@Override
public Insert addGeneratedColumns(String[] columnNames, InDatabaseGenerator generator) {
if ( columnNames.length != 1 ) {
//TODO: Should this allow multiple columns? Would require changing
// IdentityColumnSupport.appendIdentitySelectToInsert()
throw new MappingException("wrong number of generated columns");
}
identityColumnName = columnNames[0];
return super.addGeneratedColumns( columnNames, generator );
}
public InsertSelectIdentityInsert(Dialect dialect) {
super( dialect );
}

View File

@ -33,7 +33,8 @@ public class TableInsertReturningBuilder extends AbstractTableInsertBuilder {
@Override
public TableInsert buildMutation() {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) getMutationTarget().getIdentifierMapping();
final BasicEntityIdentifierMapping identifierMapping =
(BasicEntityIdentifierMapping) getMutationTarget().getIdentifierMapping();
return new TableInsertStandard(
getMutatingTable(),
getMutationTarget(),

View File

@ -23,7 +23,10 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class SelectGeneratorDelegate extends AbstractSelectingDelegate {
/**
* Uses a unique key of the inserted entity to locate the newly inserted row.
*/
public class UniqueKeySelectingDelegate extends AbstractSelectingDelegate {
private final PostInsertIdentityPersister persister;
private final Dialect dialect;
@ -33,15 +36,15 @@ public class SelectGeneratorDelegate extends AbstractSelectingDelegate {
private final String idSelectString;
public SelectGeneratorDelegate(PostInsertIdentityPersister persister, Dialect dialect, String uniqueKeyPropertyName) {
public UniqueKeySelectingDelegate(PostInsertIdentityPersister persister, Dialect dialect, String uniqueKeyPropertyName) {
super( persister );
this.persister = persister;
this.dialect = dialect;
this.uniqueKeyPropertyName = uniqueKeyPropertyName;
idSelectString = persister.getSelectByUniqueKeyString(this.uniqueKeyPropertyName);
uniqueKeyType = persister.getPropertyType(this.uniqueKeyPropertyName);
idSelectString = persister.getSelectByUniqueKeyString( uniqueKeyPropertyName );
uniqueKeyType = persister.getPropertyType( uniqueKeyPropertyName );
idType = (BasicType<?>) persister.getIdentifierType();
}
@ -68,12 +71,12 @@ public class SelectGeneratorDelegate extends AbstractSelectingDelegate {
}
@Override
protected Object extractGeneratedValue(Object entity, ResultSet rs, SharedSessionContractImplementor session)
protected Object extractGeneratedValue(Object entity, ResultSet resultSet, SharedSessionContractImplementor session)
throws SQLException {
if ( !rs.next() ) {
if ( !resultSet.next() ) {
throw new IdentifierGenerationException("the inserted row could not be located by the unique key: "
+ uniqueKeyPropertyName);
}
return idType.getJdbcValueExtractor().extract( rs, 1, session );
return idType.getJdbcValueExtractor().extract( resultSet, 1, session );
}
}

View File

@ -97,9 +97,8 @@ import org.hibernate.id.Assigned;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.insert.SelectGeneratorDelegate;
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.id.insert.BasicSelectingDelegate;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
@ -2886,7 +2885,7 @@ public abstract class AbstractEntityPersister
@Override
public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegateForProperty(String uniqueKeyPropertyName) {
Dialect dialect = getFactory().getJdbcServices().getDialect();
return new SelectGeneratorDelegate( this, dialect, uniqueKeyPropertyName );
return new UniqueKeySelectingDelegate( this, dialect, uniqueKeyPropertyName );
}
@Override

View File

@ -13,6 +13,8 @@ import java.util.Map;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.tuple.InDatabaseGenerator;
/**
* An SQL {@code INSERT} statement
@ -89,9 +91,22 @@ public class Insert {
}
public Insert addIdentityColumn(String columnName) {
String value = dialect.getIdentityColumnSupport().getIdentityInsertString();
if ( value != null ) {
addColumn( columnName, value );
final IdentityColumnSupport identityColumnSupport = dialect.getIdentityColumnSupport();
if ( identityColumnSupport.hasIdentityInsertKeyword() ) {
addColumn( columnName, identityColumnSupport.getIdentityInsertString() );
}
return this;
}
public Insert addGeneratedColumns(String[] columnNames, InDatabaseGenerator generator) {
if ( generator.referenceColumnsInSql( dialect ) ) {
String[] columnValues = generator.getReferencedColumnValues( dialect );
if ( columnNames.length != columnValues.length ) {
throw new MappingException("wrong number of generated columns"); //TODO!
}
for ( int i = 0; i < columnNames.length; i++ ) {
addColumn( columnNames[i], columnValues[i] );
}
}
return this;
}

View File

@ -76,10 +76,7 @@ public abstract class AbstractTableInsertBuilder
}
@Override
public void addValueColumn(
String columnName,
String columnWriteFragment,
JdbcMapping jdbcMapping) {
public void addValueColumn(String columnName, String columnWriteFragment, JdbcMapping jdbcMapping) {
final ColumnValueBinding valueBinding = createValueBinding( columnName, columnWriteFragment, jdbcMapping );
if ( jdbcMapping.getJdbcType().isLob() && getJdbcServices().getDialect().forceLobAsLastValue() ) {
@ -94,10 +91,7 @@ public abstract class AbstractTableInsertBuilder
}
@Override
public void addKeyColumn(
String columnName,
String columnWriteFragment,
JdbcMapping jdbcMapping) {
public void addKeyColumn(String columnName, String columnWriteFragment, JdbcMapping jdbcMapping) {
addColumn( columnName, columnWriteFragment, jdbcMapping, keyBindingList );
}