diff --git a/hibernate-core/src/main/java/org/hibernate/NotYetImplementedFor6Exception.java b/hibernate-core/src/main/java/org/hibernate/NotYetImplementedFor6Exception.java index be86ff8331..11ef51be69 100644 --- a/hibernate-core/src/main/java/org/hibernate/NotYetImplementedFor6Exception.java +++ b/hibernate-core/src/main/java/org/hibernate/NotYetImplementedFor6Exception.java @@ -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) { diff --git a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGeneratorHelper.java b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGeneratorHelper.java index e39a304f45..6957596f7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGeneratorHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGeneratorHelper.java @@ -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. */ diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java index 615e2075d4..ab636e20fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java @@ -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 diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java index 90c88df652..c72c3850c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractSelectingDelegate.java @@ -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( diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/BasicSelectingDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/BasicSelectingDelegate.java index 96ec07e979..d84bd093a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/BasicSelectingDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/BasicSelectingDelegate.java @@ -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() ); diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/DelegateHelper.java b/hibernate-core/src/main/java/org/hibernate/id/insert/DelegateHelper.java new file mode 100644 index 0000000000..16343c405c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/DelegateHelper.java @@ -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 . + */ +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]; + } + + +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java index 140371beaa..5f41cf6447 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/GetGeneratedKeysDelegate.java @@ -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 ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java b/hibernate-core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java index 064ccb3866..499597c1c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/IdentifierGeneratingInsert.java @@ -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 diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java index 03a6fd419e..81162fbf24 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/InsertReturningDelegate.java @@ -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 ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java b/hibernate-core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java index a7f9863315..030aed98f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/InsertSelectIdentityInsert.java @@ -5,12 +5,14 @@ * See the lgpl.txt file in the root directory or . */ 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 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/TableInsertReturningBuilder.java b/hibernate-core/src/main/java/org/hibernate/id/insert/TableInsertReturningBuilder.java index f975265e85..e9913ea76a 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/TableInsertReturningBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/TableInsertReturningBuilder.java @@ -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(), diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/SelectGeneratorDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/UniqueKeySelectingDelegate.java similarity index 80% rename from hibernate-core/src/main/java/org/hibernate/id/insert/SelectGeneratorDelegate.java rename to hibernate-core/src/main/java/org/hibernate/id/insert/UniqueKeySelectingDelegate.java index 2fdf28ba76..1306f36f10 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/SelectGeneratorDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/UniqueKeySelectingDelegate.java @@ -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 ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 6668c0755f..ef0e5dafe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -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 diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java index 0cd143b32b..632e165fcf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java @@ -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; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/AbstractTableInsertBuilder.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/AbstractTableInsertBuilder.java index e70cdef01a..dddcc5afe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/AbstractTableInsertBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/AbstractTableInsertBuilder.java @@ -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 ); }