diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/RowId.java b/hibernate-core/src/main/java/org/hibernate/annotations/RowId.java index e394126001..bbbcfb6389 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/RowId.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/RowId.java @@ -15,6 +15,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * Specifies that an Oracle-style {@code rowid} should be used in SQL * {@code update} statements for an entity, instead of the primary key. + *
+ * If the {@linkplain org.hibernate.dialect.Dialect SQL dialect} does + * not support some sort of {@code rowid}, this annotation is ignored. * * @author Steve Ebersole */ @@ -25,6 +28,10 @@ public @interface RowId { * Specifies the {@code rowid} identifier. *
* For example, on Oracle, this should be just {@code "rowid"}.
+ *
+ * @deprecated the {@code rowid} identifier is now inferred
+ * automatically from the {@link org.hibernate.dialect.Dialect}
*/
- String value();
+ @Deprecated(since = "6.2")
+ String value() default "";
}
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 6fdbe48dae..47dfe43d52 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -215,6 +215,7 @@ import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.NUMERIC;
import static org.hibernate.type.SqlTypes.NVARCHAR;
import static org.hibernate.type.SqlTypes.REAL;
+import static org.hibernate.type.SqlTypes.ROWID;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TIME;
import static org.hibernate.type.SqlTypes.TIMESTAMP;
@@ -4622,4 +4623,23 @@ public abstract class Dialect implements ConversionContext {
ServiceRegistryImplementor registry) {
return new HibernateSchemaManagementTool();
}
+
+ /**
+ * The name of a {@code rowid}-like pseudo-column which
+ * acts as a high-performance row locator, or null if
+ * this dialect has no such pseudo-column.
+ */
+ public String rowId() {
+ return null;
+ }
+
+ /**
+ * The JDBC type code of the {@code rowid}-like pseudo-column
+ * which acts as a high-performance row locator.
+ *
+ * @return {@link Types#ROWID} by default
+ */
+ public int rowIdSqlType() {
+ return ROWID;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
index 7687dafbfe..0ad061f097 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java
@@ -75,6 +75,7 @@ import jakarta.persistence.TemporalType;
import static org.hibernate.query.sqm.TemporalUnit.SECOND;
import static org.hibernate.type.SqlTypes.ARRAY;
+import static org.hibernate.type.SqlTypes.BIGINT;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.CHAR;
import static org.hibernate.type.SqlTypes.DECIMAL;
@@ -856,4 +857,14 @@ public class H2Dialect extends Dialect {
public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate;
}
+
+ @Override
+ public String rowId() {
+ return "_rowid_";
+ }
+
+ @Override
+ public int rowIdSqlType() {
+ return BIGINT;
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 689dac2dc4..a47a663d2b 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1406,4 +1406,9 @@ public class OracleDialect extends Dialect {
public String getCreateUserDefinedTypeKindString() {
return "object";
}
+
+ @Override
+ public String rowId() {
+ return "rowid";
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
index 03944f2f83..63cd7183b1 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java
@@ -1349,7 +1349,17 @@ public class PostgreSQLDialect extends Dialect {
// disabled foreign key constraints still prevent 'truncate table'
// (these would help if we used 'delete' instead of 'truncate')
-// @Override
+ @Override
+ public String rowId() {
+ return "ctid";
+ }
+
+ @Override
+ public int rowIdSqlType() {
+ return OTHER;
+ }
+
+ // @Override
// public String getDisableConstraintsStatement() {
// return "set constraints all deferred";
// }
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java
index 49aaeda8c1..e8c1899507 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityRowIdMappingImpl.java
@@ -6,9 +6,9 @@
*/
package org.hibernate.metamodel.mapping.internal;
-import java.sql.Types;
import java.util.function.BiConsumer;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.mapping.EntityMappingType;
@@ -41,9 +41,9 @@ public class EntityRowIdMappingImpl implements EntityRowIdMapping {
this.rowIdName = rowIdName;
this.tableExpression = tableExpression;
this.declaringType = declaringType;
- this.rowIdType = declaringType.getEntityPersister().getFactory().getTypeConfiguration()
- .getBasicTypeRegistry()
- .resolve( Object.class, Types.ROWID );
+ final SessionFactoryImplementor factory = declaringType.getEntityPersister().getFactory();
+ this.rowIdType = factory.getTypeConfiguration().getBasicTypeRegistry()
+ .resolve( Object.class, factory.getJdbcServices().getDialect().rowIdSqlType() );
}
@Override
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 6e3481f5a3..69c1ab5461 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
@@ -544,7 +544,13 @@ public abstract class AbstractEntityPersister
rootTableKeyColumnReaderTemplates = new String[identifierColumnSpan];
identifierAliases = new String[identifierColumnSpan];
- rowIdName = persistentClass.getRootTable().getRowId();
+ final String rowId = persistentClass.getRootTable().getRowId();
+ if ( rowId == null ) {
+ rowIdName = null;
+ }
+ else {
+ rowIdName = rowId.isEmpty() ? dialect.rowId() : rowId;
+ }
if ( persistentClass.getLoaderName() != null ) {
// We must resolve the named query on-demand through the boot model because it isn't initialized yet
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RowIdJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RowIdJdbcType.java
new file mode 100644
index 0000000000..b11f3a6167
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/RowIdJdbcType.java
@@ -0,0 +1,77 @@
+/*
+ * 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.type.descriptor.jdbc;
+
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.ValueBinder;
+import org.hibernate.type.descriptor.ValueExtractor;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.Types;
+
+/**
+ * Descriptor for {@link Types#ROWID ROWID} handling.
+ *
+ * @author Gavin King
+ */
+public class RowIdJdbcType implements JdbcType {
+ public static final RowIdJdbcType INSTANCE = new RowIdJdbcType();
+
+ @Override
+ public int getJdbcTypeCode() {
+ return SqlTypes.ROWID;
+ }
+
+ @Override
+ public String toString() {
+ return "RowIdJdbcType";
+ }
+
+ @Override
+ public
*
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java
index 404550eaa3..226128dad1 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/rowid/RowIdTest.java
@@ -13,11 +13,9 @@ import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.RowId;
-import org.hibernate.dialect.OracleDialect;
import org.hibernate.testing.jdbc.SQLStatementInspector;
import org.hibernate.testing.orm.junit.DomainModel;
-import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeEach;
@@ -33,14 +31,13 @@ import static org.junit.Assert.assertThat;
*/
@DomainModel( annotatedClasses = RowIdTest.Product.class )
@SessionFactory(statementInspectorClass = SQLStatementInspector.class)
-@RequiresDialect( value = OracleDialect.class)
public class RowIdTest {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Product product = new Product();
- product.setId( 1L );
+ product.setId( "1L" );
product.setName( "Mobile phone" );
product.setNumber( "123-456-7890" );
session.persist( product );
@@ -51,15 +48,19 @@ public class RowIdTest {
void testRowId(SessionFactoryScope scope) {
final String updatedName = "Smart phone";
scope.inTransaction( session -> {
+ String rowId = scope.getSessionFactory().getJdbcServices().getDialect().rowId();
+
SQLStatementInspector statementInspector = (SQLStatementInspector) scope.getStatementInspector();
statementInspector.clear();
- Product product = session.find( Product.class, 1L );
+ Product product = session.find( Product.class, "1L" );
List