From 659157a45c8adea79831007f6f1324469171b9c8 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Wed, 2 Sep 2015 11:14:00 +0100 Subject: [PATCH] HHH-10422 - Add test for issue (from HHH-9983) (cherry picked from commit 1f3048e572382a76f81a4d5f3c31f1fd4b26d696) HHH-10422 - Fix Error saving entity with identity id on Oracle 12c (from HHH-9983) (cherry picked from commit 33458ab6f84c997533422f5bac549ed94631e14d) Conflicts: hibernate-core/src/main/java/org/hibernate/dialect/Oracle12cDialect.java hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cIdentityColumnSupport.java HHH-10422 : Backport HHH-9983 to fix identity IDs using Oracle12cDialect in 5.0 HHH-10422 : Custom identity support test HHH-10422 : Custom identity support test --- .../java/org/hibernate/dialect/Dialect.java | 48 +++++- .../hibernate/dialect/Oracle12cDialect.java | 7 + .../identity/GetGeneratedKeysDelegate.java | 73 +++++++++ .../identity/IdentityColumnSupport.java | 104 +++++++++++++ .../identity/IdentityColumnSupportImpl.java | 64 ++++++++ .../Oracle12cGetGeneratedKeysDelegate.java | 40 +++++ .../Oracle12cIdentityColumnSupport.java | 26 ++++ .../org/hibernate/id/IdentityGenerator.java | 53 +------ .../id/insert/InsertSelectIdentityInsert.java | 2 +- .../java/org/hibernate/mapping/Table.java | 4 +- .../entity/AbstractEntityPersister.java | 15 +- .../main/java/org/hibernate/sql/Insert.java | 2 +- .../internal/StandardTableExporter.java | 4 +- .../CustomIdentityColumnSupportTest.java | 145 ++++++++++++++++++ .../identity/hhh9983/SaveEntityTest.java | 78 ++++++++++ .../org/hibernate/testing/DialectChecks.java | 2 +- 16 files changed, 601 insertions(+), 66 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupportImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cGetGeneratedKeysDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cIdentityColumnSupport.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/idgen/identity/CustomIdentityColumnSupportTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/idgen/identity/hhh9983/SaveEntityTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index fd534376c8..e13803217b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -40,6 +40,8 @@ import org.hibernate.dialect.function.SQLFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.StandardAnsiSqlAggregationFunctions; import org.hibernate.dialect.function.StandardSQLFunction; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.dialect.identity.IdentityColumnSupportImpl; import org.hibernate.dialect.lock.LockingStrategy; import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy; import org.hibernate.dialect.lock.OptimisticLockingStrategy; @@ -728,7 +730,7 @@ public abstract class Dialect implements ConversionContext { * @return The native generator class. */ public Class getNativeIdentifierGeneratorClass() { - if ( supportsIdentityColumns() ) { + if ( getIdentityColumnSupport().supportsIdentityColumns() ) { return IdentityGenerator.class; } else { @@ -739,11 +741,25 @@ public abstract class Dialect implements ConversionContext { // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * Get the appropriate {@link IdentityColumnSupport} + * + * @return the IdentityColumnSupport + */ + public IdentityColumnSupport getIdentityColumnSupport(){ + return new IdentityColumnSupportImpl( this ); + } + /** * Does this dialect support identity column key generation? * * @return True if IDENTITY columns are supported; false otherwise. + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().supportsIdentityColumns()} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public boolean supportsIdentityColumns() { return false; } @@ -754,7 +770,12 @@ public abstract class Dialect implements ConversionContext { * * @return True if the dialect supports selecting the just * generated IDENTITY in the insert statement. + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().supportsInsertSelectIdentity()} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public boolean supportsInsertSelectIdentity() { return false; } @@ -764,7 +785,12 @@ public abstract class Dialect implements ConversionContext { * completely separate identity data type * * @return boolean + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().hasDataTypeInIdentityColumn()} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public boolean hasDataTypeInIdentityColumn() { return true; } @@ -779,7 +805,12 @@ public abstract class Dialect implements ConversionContext { * @param insertString The insert command * @return The insert command with any necessary identity select * clause attached. + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().appendIdentitySelectToInsert(String)} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public String appendIdentitySelectToInsert(String insertString) { return insertString; } @@ -793,7 +824,12 @@ public abstract class Dialect implements ConversionContext { * @param type The {@link java.sql.Types} type code. * @return The appropriate select command * @throws MappingException If IDENTITY generation is not supported. + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().getIdentitySelectString(String,String,int)} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public String getIdentitySelectString(String table, String column, int type) throws MappingException { return getIdentitySelectString(); } @@ -819,7 +855,12 @@ public abstract class Dialect implements ConversionContext { * @param type The {@link java.sql.Types} type code. * @return The appropriate DDL fragment. * @throws MappingException If IDENTITY generation is not supported. + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().getIdentityColumnString(int)} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public String getIdentityColumnString(int type) throws MappingException { return getIdentityColumnString(); } @@ -842,7 +883,12 @@ public abstract class Dialect implements ConversionContext { * Need if the dialect does not support inserts that specify no column values. * * @return The appropriate keyword. + * + * @deprecated {@link #getIdentityColumnSupport()} should be overridden instead; + * if {@code getIdentityColumnSupport().getIdentityInsertString()} does not delegate + * to this method, then this method is ignored. */ + @Deprecated public String getIdentityInsertString() { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle12cDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle12cDialect.java index c563ae2d4a..ff2cc7e236 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle12cDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle12cDialect.java @@ -7,6 +7,8 @@ package org.hibernate.dialect; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.SQL2008StandardLimitHandler; import org.hibernate.id.enhanced.SequenceStyleGenerator; @@ -38,6 +40,7 @@ public class Oracle12cDialect extends Oracle10gDialect { return true; } + @Override public Class getNativeIdentifierGeneratorClass() { return SequenceStyleGenerator.class; } @@ -52,4 +55,8 @@ public class Oracle12cDialect extends Oracle10gDialect { return SQL2008StandardLimitHandler.INSTANCE; } + @Override + public IdentityColumnSupport getIdentityColumnSupport() { + return new Oracle12cIdentityColumnSupport( this ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java new file mode 100644 index 0000000000..acb478a612 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/GetGeneratedKeysDelegate.java @@ -0,0 +1,73 @@ +/* + * 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.dialect.identity; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.id.IdentifierGeneratorHelper; +import org.hibernate.id.PostInsertIdentityPersister; +import org.hibernate.id.insert.AbstractReturningDelegate; +import org.hibernate.id.insert.IdentifierGeneratingInsert; +import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; + +/** + * Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys + * + * @author Andrea Boriero + */ +public class GetGeneratedKeysDelegate + extends AbstractReturningDelegate + implements InsertGeneratedIdentifierDelegate { + private final PostInsertIdentityPersister persister; + private final Dialect dialect; + + public GetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) { + super( persister ); + this.persister = persister; + this.dialect = dialect; + } + + @Override + public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() { + IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect ); + insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] ); + return insert; + } + + @Override + protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException { + return session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( insertSQL, PreparedStatement.RETURN_GENERATED_KEYS ); + } + + @Override + public Serializable executeAndExtract(PreparedStatement insert, SessionImplementor session) + throws SQLException { + session.getJdbcCoordinator().getResultSetReturn().executeUpdate( insert ); + ResultSet rs = null; + try { + rs = insert.getGeneratedKeys(); + return IdentifierGeneratorHelper.getGeneratedIdentity( + rs, + persister.getRootTableKeyColumnNames()[0], + persister.getIdentifierType() + ); + } + finally { + if ( rs != null ) { + session.getJdbcCoordinator().getResourceRegistry().release( rs, insert ); + } + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java new file mode 100644 index 0000000000..55182e9b20 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupport.java @@ -0,0 +1,104 @@ +/* + * 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.dialect.identity; + +import org.hibernate.MappingException; +import org.hibernate.dialect.Dialect; +import org.hibernate.id.PostInsertIdentityPersister; + +/** + * Represents a support for the Dialect identity key generation + * + * @author Andrea Boriero + */ +public interface IdentityColumnSupport { + /** + * Does this dialect support identity column key generation? + * + * @return True if IDENTITY columns are supported; false otherwise. + */ + public boolean supportsIdentityColumns(); + + /** + * Does the dialect support some form of inserting and selecting + * the generated IDENTITY value all in the same statement. + * + * @return True if the dialect supports selecting the just + * generated IDENTITY in the insert statement. + */ + public boolean supportsInsertSelectIdentity(); + + /** + * Whether this dialect have an Identity clause added to the data type or a + * completely separate identity data type + * + * @return boolean + */ + public boolean hasDataTypeInIdentityColumn(); + + /** + * Provided we {@link #supportsInsertSelectIdentity}, then attach the + * "select identity" clause to the insert statement. + *

+ * Note, if {@link #supportsInsertSelectIdentity} == false then + * the insert-string should be returned without modification. + * + * @param insertString The insert command + * + * @return The insert command with any necessary identity select + * clause attached. + */ + public String appendIdentitySelectToInsert(String insertString); + + /** + * Get the select command to use to retrieve the last generated IDENTITY + * value for a particular table + * + * @param table The table into which the insert was done + * @param column The PK column. + * @param type The {@link java.sql.Types} type code. + * + * @return The appropriate select command + * + * @throws MappingException If IDENTITY generation is not supported. + */ + public String getIdentitySelectString(String table, String column, int type) throws MappingException; + + + /** + * The syntax used during DDL to define a column as being an IDENTITY of + * a particular type. + * + * @param type The {@link java.sql.Types} type code. + * + * @return The appropriate DDL fragment. + * + * @throws MappingException If IDENTITY generation is not supported. + */ + public String getIdentityColumnString(int type) throws MappingException; + + + /** + * The keyword used to insert a generated value into an identity column (or null). + * Need if the dialect does not support inserts that specify no column values. + * + * @return The appropriate keyword. + */ + public String getIdentityInsertString(); + + /** + * The Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys + * + * @param persister The persister + * @param dialect The dialect against which to generate the delegate + * + * @return the dialect specific GetGeneratedKeys delegate + */ + public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate( + PostInsertIdentityPersister persister, + Dialect dialect); +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupportImpl.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupportImpl.java new file mode 100644 index 0000000000..8d8d4f60b4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/IdentityColumnSupportImpl.java @@ -0,0 +1,64 @@ +/* + * 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.dialect.identity; + +import org.hibernate.MappingException; +import org.hibernate.dialect.Dialect; +import org.hibernate.id.PostInsertIdentityPersister; + +/** + * @author Andrea Boriero + */ +public class IdentityColumnSupportImpl implements IdentityColumnSupport { + private final Dialect dialect; + + public IdentityColumnSupportImpl(Dialect dialect) { + this.dialect = dialect; + } + + @Override + public boolean supportsIdentityColumns() { + return dialect.supportsIdentityColumns(); + } + + @Override + public boolean supportsInsertSelectIdentity() { + return dialect.supportsInsertSelectIdentity(); + } + + @Override + public boolean hasDataTypeInIdentityColumn() { + return dialect.hasDataTypeInIdentityColumn(); + } + + @Override + public String appendIdentitySelectToInsert(String insertString) { + return dialect.appendIdentitySelectToInsert( insertString ); + } + + @Override + public String getIdentitySelectString(String table, String column, int type) throws MappingException { + return dialect.getIdentitySelectString( table, column, type ); + } + + @Override + public String getIdentityColumnString(int type) throws MappingException { + return dialect.getIdentityColumnString( type ); + } + + @Override + public String getIdentityInsertString() { + return dialect.getIdentityInsertString(); + } + + @Override + public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate( + PostInsertIdentityPersister persister, + Dialect dialect) { + return new GetGeneratedKeysDelegate( persister, dialect ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cGetGeneratedKeysDelegate.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cGetGeneratedKeysDelegate.java new file mode 100644 index 0000000000..cea10d9c24 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cGetGeneratedKeysDelegate.java @@ -0,0 +1,40 @@ +/* + * 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.dialect.identity; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.hibernate.HibernateException; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.identity.GetGeneratedKeysDelegate; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.id.PostInsertIdentityPersister; + +/** + * @author Andrea Boriero + */ +public class Oracle12cGetGeneratedKeysDelegate extends GetGeneratedKeysDelegate { + private String[] keyColumns; + + public Oracle12cGetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) { + super( persister, dialect ); + this.keyColumns = getPersister().getRootTableKeyColumnNames(); + if ( keyColumns.length > 1 ) { + throw new HibernateException( "Identity generator cannot be used with multi-column keys" ); + } + + } + + @Override + protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException { + return session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( insertSQL, keyColumns ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cIdentityColumnSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cIdentityColumnSupport.java new file mode 100644 index 0000000000..1a4bb96b6b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/identity/Oracle12cIdentityColumnSupport.java @@ -0,0 +1,26 @@ +/* + * 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.dialect.identity; + +import org.hibernate.dialect.Dialect; +import org.hibernate.id.PostInsertIdentityPersister; + +/** + * @author Andrea Boriero + */ +public class Oracle12cIdentityColumnSupport extends IdentityColumnSupportImpl { + + public Oracle12cIdentityColumnSupport(Dialect dialect) { + super( dialect ); + } + + @Override + public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate( + PostInsertIdentityPersister persister, Dialect dialect) { + return new Oracle12cGetGeneratedKeysDelegate( persister, dialect ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/IdentityGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/IdentityGenerator.java index 0a12f9aaf6..973f330309 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IdentityGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IdentityGenerator.java @@ -38,9 +38,9 @@ public class IdentityGenerator extends AbstractPostInsertGenerator { Dialect dialect, boolean isGetGeneratedKeysEnabled) throws HibernateException { if ( isGetGeneratedKeysEnabled ) { - return new GetGeneratedKeysDelegate( persister, dialect ); + return dialect.getIdentityColumnSupport().buildGetGeneratedKeysDelegate( persister, dialect ); } - else if ( dialect.supportsInsertSelectIdentity() ) { + else if ( dialect.getIdentityColumnSupport().supportsInsertSelectIdentity() ) { return new InsertSelectDelegate( persister, dialect ); } else { @@ -48,56 +48,7 @@ public class IdentityGenerator extends AbstractPostInsertGenerator { } } - /** - * Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys - */ - public static class GetGeneratedKeysDelegate - extends AbstractReturningDelegate - implements InsertGeneratedIdentifierDelegate { - private final PostInsertIdentityPersister persister; - private final Dialect dialect; - public GetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) { - super( persister ); - this.persister = persister; - this.dialect = dialect; - } - - @Override - public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() { - IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect ); - insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] ); - return insert; - } - - @Override - protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException { - return session - .getJdbcCoordinator() - .getStatementPreparer() - .prepareStatement( insertSQL, PreparedStatement.RETURN_GENERATED_KEYS ); - } - - @Override - public Serializable executeAndExtract(PreparedStatement insert, SessionImplementor session) - throws SQLException { - session.getJdbcCoordinator().getResultSetReturn().executeUpdate( insert ); - ResultSet rs = null; - try { - rs = insert.getGeneratedKeys(); - return IdentifierGeneratorHelper.getGeneratedIdentity( - rs, - persister.getRootTableKeyColumnNames()[0], - persister.getIdentifierType() - ); - } - finally { - if ( rs != null ) { - session.getJdbcCoordinator().getResourceRegistry().release( rs, insert ); - } - } - } - } /** * Delegate for dealing with IDENTITY columns where the dialect supports returning 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 80b9b2f6da..e130e19704 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 @@ -20,6 +20,6 @@ public class InsertSelectIdentityInsert extends IdentifierGeneratingInsert { } public String toStatementString() { - return getDialect().appendIdentitySelectToInsert( super.toStatementString() ); + return getDialect().getIdentityColumnSupport().appendIdentitySelectToInsert( super.toStatementString() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 5018861748..593ecb772b 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -535,11 +535,11 @@ public class Table implements RelationalModel, Serializable, Exportable { if ( identityColumn && col.getQuotedName( dialect ).equals( pkname ) ) { // to support dialects that have their own identity data type - if ( dialect.hasDataTypeInIdentityColumn() ) { + if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) { buf.append( col.getSqlType( dialect, p ) ); } buf.append( ' ' ) - .append( dialect.getIdentityColumnString( col.getSqlTypeCode( p ) ) ); + .append( dialect.getIdentityColumnSupport().getIdentityColumnString( col.getSqlTypeCode( p ) ) ); } else { 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 5b5a4166d3..44ea656c50 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 @@ -2496,7 +2496,7 @@ public abstract class AbstractEntityPersister // append the SQL to return the generated identifier if ( j == 0 && identityInsert && useInsertSelectIdentity() ) { //TODO: suck into Insert - result = getFactory().getDialect().appendIdentitySelectToInsert( result ); + result = getFactory().getDialect().getIdentityColumnSupport().appendIdentitySelectToInsert( result ); } return result; @@ -2748,7 +2748,7 @@ public abstract class AbstractEntityPersister } protected boolean useInsertSelectIdentity() { - return !useGetGeneratedKeys() && getFactory().getDialect().supportsInsertSelectIdentity(); + return !useGetGeneratedKeys() && getFactory().getDialect().getIdentityColumnSupport().supportsInsertSelectIdentity(); } protected boolean useGetGeneratedKeys() { @@ -2794,11 +2794,12 @@ public abstract class AbstractEntityPersister public String getIdentitySelectString() { //TODO: cache this in an instvar - return getFactory().getDialect().getIdentitySelectString( - getTableName( 0 ), - getKeyColumns( 0 )[0], - getIdentifierType().sqlTypes( getFactory() )[0] - ); + return getFactory().getDialect().getIdentityColumnSupport() + .getIdentitySelectString( + getTableName( 0 ), + getKeyColumns( 0 )[0], + getIdentifierType().sqlTypes( getFactory() )[0] + ); } public String getSelectByUniqueKeyString(String propertyName) { 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 a2faa2ad01..49d828c92d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Insert.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Insert.java @@ -75,7 +75,7 @@ public class Insert { } public Insert addIdentityColumn(String columnName) { - String value = dialect.getIdentityInsertString(); + String value = dialect.getIdentityColumnSupport().getIdentityInsertString(); if ( value != null ) { addColumn( columnName, value ); } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java index db8bff4805..8adbd1dcc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java @@ -86,11 +86,11 @@ public class StandardTableExporter implements Exporter { if ( isPrimaryKeyIdentity && colName.equals( pkColName ) ) { // to support dialects that have their own identity data type - if ( dialect.hasDataTypeInIdentityColumn() ) { + if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) { buf.append( col.getSqlType( dialect, metadata ) ); } buf.append( ' ' ) - .append( dialect.getIdentityColumnString( col.getSqlTypeCode( metadata ) ) ); + .append( dialect.getIdentityColumnSupport().getIdentityColumnString( col.getSqlTypeCode( metadata ) ) ); } else { buf.append( col.getSqlType( dialect, metadata ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/identity/CustomIdentityColumnSupportTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/identity/CustomIdentityColumnSupportTest.java new file mode 100644 index 0000000000..856665c118 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/identity/CustomIdentityColumnSupportTest.java @@ -0,0 +1,145 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: Apache License, Version 2.0 + * See the LICENSE file in the root directory or visit http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.hibernate.test.idgen.identity; + +import org.junit.Test; + +import org.hibernate.MappingException; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.identity.GetGeneratedKeysDelegate; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.id.PostInsertIdentityPersister; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-10422") +public class CustomIdentityColumnSupportTest extends BaseUnitTestCase { + + @Test + public void testLegacyIdentityColumnSupport() { + testIdentityColumnSupport( new LegacyIdentityColumnSupportDialect().getIdentityColumnSupport() ); + } + + @Test + public void testCustomIdentityColumnSupport() { + testIdentityColumnSupport( new IdentityColumnSupportDialect().getIdentityColumnSupport() ); + } + + private void testIdentityColumnSupport(IdentityColumnSupport support) { + assertEquals( true, support.supportsIdentityColumns() ); + assertEquals( true, support.supportsInsertSelectIdentity() ); + assertEquals( false, support.hasDataTypeInIdentityColumn() ); + assertEquals( "abcInsertString", support.appendIdentitySelectToInsert( "InsertString" ) ); + try { + support.getIdentitySelectString( "a", "b", 1 ); + fail( "should have thrown MappingException" ); + } + catch( MappingException ex ) { + assertEquals( "blah", ex.getMessage() ); + } + try { + support.getIdentityColumnString( 1 ); + fail( "should have thrown MappingException" ); + } + catch( MappingException ex ) { + assertEquals( "blah, blah", ex.getMessage() ); + } + assertEquals( "insert string", support.getIdentityInsertString() ); + } + + + private static class LegacyIdentityColumnSupportDialect extends Dialect { + + @Override + public boolean supportsIdentityColumns() { + return true; + } + + @Override + public boolean supportsInsertSelectIdentity() { + return true; + } + + @Override + public boolean hasDataTypeInIdentityColumn() { + return false; + } + + @Override + public String appendIdentitySelectToInsert(String insertString) { + return "abc" + insertString; + } + + @Override + public String getIdentitySelectString(String table, String column, int type) throws MappingException { + throw new MappingException( "blah" ); + } + + @Override + public String getIdentityColumnString(int type) throws MappingException { + throw new MappingException( "blah, blah" ); + } + + @Override + public String getIdentityInsertString() { + return "insert string"; + } + } + + private static class IdentityColumnSupportDialect extends Dialect { + + public IdentityColumnSupport getIdentityColumnSupport(){ + return new IdentityColumnSupport() { + @Override + public boolean supportsIdentityColumns() { + return true; + } + + @Override + public boolean supportsInsertSelectIdentity() { + return true; + } + + @Override + public boolean hasDataTypeInIdentityColumn() { + return false; + } + + @Override + public String appendIdentitySelectToInsert(String insertString) { + return "abc" + insertString; + } + + @Override + public String getIdentitySelectString(String table, String column, int type) throws MappingException { + throw new MappingException( "blah" ); + } + + @Override + public String getIdentityColumnString(int type) throws MappingException { + throw new MappingException( "blah, blah" ); + } + + @Override + public String getIdentityInsertString() { + return "insert string"; + } + + @Override + public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) { + return new GetGeneratedKeysDelegate( persister, dialect ); + } + }; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/identity/hhh9983/SaveEntityTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/identity/hhh9983/SaveEntityTest.java new file mode 100644 index 0000000000..05badc7c5c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/identity/hhh9983/SaveEntityTest.java @@ -0,0 +1,78 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: Apache License, Version 2.0 + * See the LICENSE file in the root directory or visit http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.hibernate.test.idgen.identity.hhh9983; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.dialect.Oracle12cDialect; + +import org.junit.Test; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Andrea Boriero + */ +@TestForIssue(jiraKey = "HHH-9983") +@RequiresDialect(Oracle12cDialect.class) +public class SaveEntityTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] {Company.class}; + } + + @Test + public void testSave() { + Session s = openSession(); + Transaction transaction = s.beginTransaction(); + try { + s.save( new Company() ); + s.getTransaction().commit(); + } + finally { + s.close(); + } + } + + @Entity + @Table(name = "Company") + public class Company { + private Integer id; + private String name; + + public Company() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index ddfa008da5..94a5f72f95 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -35,7 +35,7 @@ abstract public class DialectChecks { public static class SupportsIdentityColumns implements DialectCheck { public boolean isMatch(Dialect dialect) { - return dialect.supportsIdentityColumns(); + return dialect.getIdentityColumnSupport().supportsIdentityColumns(); } }