HHH-15958 support the @RowId annotation on DB2 LUW

... and perhaps also on DB2 for z and i (no way to test it)
This commit is contained in:
Gavin 2023-01-01 16:09:26 +01:00 committed by Gavin King
parent 366208924f
commit 76b2f92f39
13 changed files with 112 additions and 25 deletions

View File

@ -13,25 +13,35 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Specifies that an Oracle-style {@code rowid} should be used in SQL * Specifies that a {@code rowid}-like column or pseudo-column should be
* {@code update} statements for an entity, instead of the primary key. * used as the row locator in SQL {@code update} statements for an entity,
* instead of the primary key of the table.
* <p> * <p>
* If the {@linkplain org.hibernate.dialect.Dialect SQL dialect} does * If the {@linkplain org.hibernate.dialect.Dialect SQL dialect} does
* not support some sort of {@code rowid}, this annotation is ignored. * not support some sort of {@code rowid}-like column or pseudo-column,
* then this annotation is ignored, and the primary key is used as the
* row locator.
* *
* @author Steve Ebersole * @author Steve Ebersole
*
* @see org.hibernate.dialect.Dialect#rowId
*/ */
@Target(TYPE) @Target(TYPE)
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface RowId { public @interface RowId {
/** /**
* Specifies the {@code rowid} identifier. * Specifies the name of the {@code rowid}-like column for databases
* where the column is declared explicitly in DDL.
* <p> * <p>
* For example, on Oracle, this should be just {@code "rowid"}. * It is <em>not</em> necessary to specify the name for databases where
* the {@code rowid}-like value is an implicitly-existing pseudo-column,
* and on those databases, this annotation member is ignored.
* *
* @deprecated the {@code rowid} identifier is now inferred * @apiNote Previously, this annotation member was required. But the
* automatically from the {@link org.hibernate.dialect.Dialect} * name of the column it is now usually determined by calling
* {@link org.hibernate.dialect.Dialect#rowId}, and so this
* member is now usually ignored. The exception is for certain
* flavors of DB2.
*/ */
@Deprecated(since = "6.2")
String value() default ""; String value() default "";
} }

View File

@ -948,8 +948,8 @@ public String generatedAs(String generatedAs) {
@Override @Override
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
throws SQLException { throws SQLException {
builder.setAutoQuoteInitialUnderscore(true); builder.setAutoQuoteInitialUnderscore( true );
return super.buildIdentifierHelper(builder, dbMetaData); return super.buildIdentifierHelper( builder, dbMetaData );
} }
@Override @Override
@ -976,4 +976,20 @@ public String getTruncateTableStatement(String tableName) {
public String getCreateUserDefinedTypeExtensionsString() { public String getCreateUserDefinedTypeExtensionsString() {
return " instantiable mode db2sql"; return " instantiable mode db2sql";
} }
/**
* The more "standard" syntax is {@code rid_bit(alias)} but here we use {@code alias.rowid}.
* <p>
* There is also an alternative {@code rid()} of type {@code bigint}, but it cannot be used
* with partitioning.
*/
@Override
public String rowId(String rowId) {
return "rowid";
}
@Override
public int rowIdSqlType() {
return VARBINARY;
}
} }

View File

@ -28,8 +28,10 @@
import java.util.List; import java.util.List;
import static org.hibernate.type.SqlTypes.ROWID;
/** /**
* A SQL dialect for DB2 for iSeries version 7.1 and above, previously known as "DB2/400". * A SQL dialect for DB2 for IBM i version 7.1 and above, previously known as "DB2/400".
* *
* @author Peter DeGregorio (pdegregorio) * @author Peter DeGregorio (pdegregorio)
* @author Christian Beikov * @author Christian Beikov
@ -149,4 +151,23 @@ protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
} }
}; };
} }
// I speculate that this is a correct implementation of rowids for DB2 for i,
// just on the basis of the DB2 docs, but I currently have no way to test it
// Note that the implementation inherited from DB2Dialect for LUW will not work!
@Override
public String rowId(String rowId) {
return rowId.isEmpty() ? "rowid_" : rowId;
}
@Override
public int rowIdSqlType() {
return ROWID;
}
@Override
public String getRowIdColumnString(String rowId) {
return rowId( rowId ) + " rowid not null generated always";
}
} }

View File

@ -30,10 +30,11 @@
import java.util.List; import java.util.List;
import static org.hibernate.type.SqlTypes.ROWID;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE; import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
/** /**
* A SQL dialect for DB2 for z/OS version 12.1 and above, previously known as : * A SQL dialect for DB2 for z/OS version 12.1 and above, previously known as:
* <ul> * <ul>
* <li>"Db2 UDB for z/OS", and * <li>"Db2 UDB for z/OS", and
* <li>"Db2 UDB for z/OS and OS/390". * <li>"Db2 UDB for z/OS and OS/390".
@ -208,4 +209,23 @@ protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
} }
}; };
} }
// I speculate that this is a correct implementation of rowids for DB2 for z/OS,
// just on the basis of the DB2 docs, but I currently have no way to test it
// Note that the implementation inherited from DB2Dialect for LUW will not work!
@Override
public String rowId(String rowId) {
return rowId.isEmpty() ? "rowid_" : rowId;
}
@Override
public int rowIdSqlType() {
return ROWID;
}
@Override
public String getRowIdColumnString(String rowId) {
return rowId( rowId ) + " rowid not null generated always";
}
} }

View File

@ -4628,8 +4628,17 @@ public SchemaManagementTool getFallbackSchemaManagementTool(
* The name of a {@code rowid}-like pseudo-column which * The name of a {@code rowid}-like pseudo-column which
* acts as a high-performance row locator, or null if * acts as a high-performance row locator, or null if
* this dialect has no such pseudo-column. * this dialect has no such pseudo-column.
* <p>
* If the {@code rowid}-like value is an explicitly-declared
* named column instead of an implicit pseudo-column, and if
* the given name is nonempty, return the given name.
*
* @param rowId the name specified by
* {@link org.hibernate.annotations.RowId#value()},
* which is ignored if {@link #getRowIdColumnString}
* is not overridden
*/ */
public String rowId() { public String rowId(String rowId) {
return null; return null;
} }
@ -4642,4 +4651,15 @@ public String rowId() {
public int rowIdSqlType() { public int rowIdSqlType() {
return ROWID; return ROWID;
} }
/**
* If this dialect requires that the {@code rowid} column be
* declared explicitly, return the DDL column definition.
*
* @return the DDL column definition, or {@code null} if
* the {@code rowid} is an implicit pseudo-column
*/
public String getRowIdColumnString(String rowId) {
return null;
}
} }

View File

@ -859,7 +859,7 @@ public UniqueDelegate getUniqueDelegate() {
} }
@Override @Override
public String rowId() { public String rowId(String rowId) {
return "_rowid_"; return "_rowid_";
} }

View File

@ -1408,7 +1408,7 @@ public String getCreateUserDefinedTypeKindString() {
} }
@Override @Override
public String rowId() { public String rowId(String rowId) {
return "rowid"; return "rowid";
} }
} }

View File

@ -1350,7 +1350,7 @@ public boolean canBatchTruncate() {
// (these would help if we used 'delete' instead of 'truncate') // (these would help if we used 'delete' instead of 'truncate')
@Override @Override
public String rowId() { public String rowId(String rowId) {
return "ctid"; return "ctid";
} }

View File

@ -15,7 +15,6 @@
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;

View File

@ -545,12 +545,7 @@ public AbstractEntityPersister(
identifierAliases = new String[identifierColumnSpan]; identifierAliases = new String[identifierColumnSpan];
final String rowId = persistentClass.getRootTable().getRowId(); final String rowId = persistentClass.getRootTable().getRowId();
if ( rowId == null ) { rowIdName = rowId == null ? null : dialect.rowId( rowId );
rowIdName = null;
}
else {
rowIdName = rowId.isEmpty() ? dialect.rowId() : rowId;
}
if ( persistentClass.getLoaderName() != null ) { if ( persistentClass.getLoaderName() != null ) {
// We must resolve the named query on-demand through the boot model because it isn't initialized yet // We must resolve the named query on-demand through the boot model because it isn't initialized yet

View File

@ -71,6 +71,12 @@ public String[] getSqlCreateStrings(
} }
appendColumn( createTable, column, table, metadata, dialect, context ); appendColumn( createTable, column, table, metadata, dialect, context );
} }
if ( table.getRowId() != null ) {
String rowIdColumn = dialect.getRowIdColumnString( table.getRowId() );
if ( rowIdColumn != null ) {
createTable.append(", ").append( rowIdColumn );
}
}
if ( table.hasPrimaryKey() ) { if ( table.hasPrimaryKey() ) {
createTable.append( ", " ).append( table.getPrimaryKey().sqlConstraintString( dialect ) ); createTable.append( ", " ).append( table.getPrimaryKey().sqlConstraintString( dialect ) );
} }

View File

@ -48,7 +48,7 @@ void setUp(SessionFactoryScope scope) {
void testRowId(SessionFactoryScope scope) { void testRowId(SessionFactoryScope scope) {
final String updatedName = "Smart phone"; final String updatedName = "Smart phone";
scope.inTransaction( session -> { scope.inTransaction( session -> {
String rowId = scope.getSessionFactory().getJdbcServices().getDialect().rowId(); String rowId = scope.getSessionFactory().getJdbcServices().getDialect().rowId("");
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector(); SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear(); statementInspector.clear();

View File

@ -314,7 +314,7 @@ public boolean isMatch(Dialect dialect) {
public static class SupportsRowId implements DialectCheck { public static class SupportsRowId implements DialectCheck {
@Override @Override
public boolean isMatch(Dialect dialect) { public boolean isMatch(Dialect dialect) {
return dialect.rowId() != null; return dialect.rowId("") != null;
} }
} }
} }