From 5589c95ab860e98adce0ac9d6686fca4384d6951 Mon Sep 17 00:00:00 2001 From: Filip Prochazka Date: Mon, 28 Jul 2025 00:20:09 +0200 Subject: [PATCH 1/3] add more detailed schema asserts --- .../hibernate/ObjectBigIntIdTypeMySQLTest.java | 8 ++++++++ .../hibernate/ObjectBigIntIdTypePostgreSQLTest.java | 12 ++++++++++++ .../hibernate/ObjectBigIntIdTypeMySQLTest.java | 8 ++++++++ .../hibernate/ObjectBigIntIdTypePostgreSQLTest.java | 12 ++++++++++++ .../hibernate/ObjectBigIntIdTypeMySQLTest.java | 8 ++++++++ .../hibernate/ObjectBigIntIdTypePostgreSQLTest.java | 12 ++++++++++++ 6 files changed, 60 insertions(+) diff --git a/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java b/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java index 85c3f98..254f667 100644 --- a/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java +++ b/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java @@ -34,18 +34,26 @@ public void testSchema() var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table1.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("extra", String.class)).isEmpty(); var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table2.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("extra", String.class)).isEmpty(); var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table3.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).isNull(); + assertThat(table3.get("extra", String.class)).isEqualToIgnoringCase("auto_increment"); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table4.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("extra", String.class)).isEmpty(); }); } diff --git a/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java b/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java index e53d211..86769d2 100644 --- a/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java +++ b/modules/typed-ids-hibernate-61/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java @@ -33,15 +33,27 @@ public void testSchema() doInJPA(em -> { var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table1.get("identity_generation", String.class)).isNull(); var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table2.get("identity_generation", String.class)).isNull(); var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).startsWith("nextval('"); + assertThat(table3.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table3.get("identity_generation", String.class)).isNull(); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table4.get("identity_generation", String.class)).isNull(); }); } diff --git a/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java b/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java index 85c3f98..254f667 100644 --- a/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java +++ b/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java @@ -34,18 +34,26 @@ public void testSchema() var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table1.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("extra", String.class)).isEmpty(); var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table2.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("extra", String.class)).isEmpty(); var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table3.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).isNull(); + assertThat(table3.get("extra", String.class)).isEqualToIgnoringCase("auto_increment"); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table4.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("extra", String.class)).isEmpty(); }); } diff --git a/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java b/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java index e53d211..86769d2 100644 --- a/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java +++ b/modules/typed-ids-hibernate-62/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java @@ -33,15 +33,27 @@ public void testSchema() doInJPA(em -> { var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table1.get("identity_generation", String.class)).isNull(); var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table2.get("identity_generation", String.class)).isNull(); var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).startsWith("nextval('"); + assertThat(table3.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table3.get("identity_generation", String.class)).isNull(); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table4.get("identity_generation", String.class)).isNull(); }); } diff --git a/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java b/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java index 85c3f98..254f667 100644 --- a/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java +++ b/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java @@ -34,18 +34,26 @@ public void testSchema() var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table1.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("extra", String.class)).isEmpty(); var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table2.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("extra", String.class)).isEmpty(); var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table3.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).isNull(); + assertThat(table3.get("extra", String.class)).isEqualToIgnoringCase("auto_increment"); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); assertThat(table4.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("extra", String.class)).isEmpty(); }); } diff --git a/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java b/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java index e53d211..86769d2 100644 --- a/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java +++ b/modules/typed-ids-hibernate-63/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java @@ -33,15 +33,27 @@ public void testSchema() doInJPA(em -> { var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table1.get("identity_generation", String.class)).isNull(); var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table2.get("identity_generation", String.class)).isNull(); var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).startsWith("nextval('"); + assertThat(table3.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table3.get("identity_generation", String.class)).isNull(); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table4.get("identity_generation", String.class)).isNull(); }); } From d77f6814154d6169a82ee532ad95728dcb0b4176 Mon Sep 17 00:00:00 2001 From: Filip Prochazka Date: Mon, 28 Jul 2025 00:27:44 +0200 Subject: [PATCH 2/3] use hibernate-63 as skeleton for hibernate-70 --- .../build.gradle.kts | 10 + .../AbstractContainerDataSourceProvider.java | 64 +++ .../tests/AbstractMySQLIntegrationTest.java | 31 ++ .../AbstractPostgreSQLIntegrationTest.java | 31 ++ .../tests/MySQLDataSourceProvider.java | 67 +++ .../tests/PostgreSQLDataSourceProvider.java | 70 +++ .../typed-ids-hibernate-70/build.gradle.kts | 18 + .../hibernate/ObjectBigIntIdJavaType.java | 171 +++++++ .../bigint/hibernate/ObjectBigIntIdType.java | 65 +++ ...ntIdTypeGenerationMetadataContributor.java | 60 +++ .../ObjectBigIntIdTypesContributor.java | 41 ++ .../id/ObjectBigIntIdIdentityGenerator.java | 121 +++++ .../ObjectBigIntIdSequenceStyleGenerator.java | 66 +++ .../common/hibernate/ImmutableType.java | 426 ++++++++++++++++++ .../hibernate/ParameterizedTypeUtils.java | 84 ++++ .../uuid/hibernate/ObjectUuidJavaType.java | 187 ++++++++ .../uuid/hibernate/ObjectUuidType.java | 49 ++ .../hibernate/ObjectUuidTypesContributor.java | 76 ++++ .../hibernate/jdbc/BinaryUuidJdbcType.java | 48 ++ .../hibernate/jdbc/NativeUuidJdbcType.java | 35 ++ ...gIntAppGeneratedExplicitMappingEntity.java | 70 +++ ...tDbAutoGeneratedExplicitMappingEntity.java | 75 +++ ...dentityGeneratedExplicitMappingEntity.java | 75 +++ ...equenceGeneratedExplicitMappingEntity.java | 75 +++ .../ObjectBigIntIdTypeMySQLTest.java | 240 ++++++++++ .../ObjectBigIntIdTypePostgreSQLTest.java | 240 ++++++++++ .../hibernate/ObjectUuidTypeMySQLTest.java | 80 ++++ .../ObjectUuidTypePostgreSQLTest.java | 79 ++++ ...UuidAppGeneratedExplicitMappingEntity.java | 72 +++ .../build.gradle.kts | 17 + ...gIntAppGeneratedExplicitMappingEntity.java | 68 +++ ...tDbAutoGeneratedExplicitMappingEntity.java | 73 +++ ...dentityGeneratedExplicitMappingEntity.java | 73 +++ ...equenceGeneratedExplicitMappingEntity.java | 73 +++ ...gIntIdTypeIndexedHibernate70MySQLTest.java | 232 ++++++++++ ...dTypeIndexedHibernate70PostgreSQLTest.java | 228 ++++++++++ ...ctUuidTypeIndexedHibernate70MySQLTest.java | 80 ++++ ...dTypeIndexedHibernate70PostgreSQLTest.java | 79 ++++ ...UuidAppGeneratedExplicitMappingEntity.java | 70 +++ 39 files changed, 3719 insertions(+) create mode 100644 modules/typed-ids-hibernate-70-testing/build.gradle.kts create mode 100644 modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractContainerDataSourceProvider.java create mode 100644 modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractMySQLIntegrationTest.java create mode 100644 modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractPostgreSQLIntegrationTest.java create mode 100644 modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/MySQLDataSourceProvider.java create mode 100644 modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/PostgreSQLDataSourceProvider.java create mode 100644 modules/typed-ids-hibernate-70/build.gradle.kts create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdType.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypesContributor.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidType.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypesContributor.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/BinaryUuidJdbcType.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/NativeUuidJdbcType.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntAppGeneratedExplicitMappingEntity.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbAutoGeneratedExplicitMappingEntity.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbIdentityGeneratedExplicitMappingEntity.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbSequenceGeneratedExplicitMappingEntity.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypeMySQLTest.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypePostgreSQLTest.java create mode 100644 modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/UuidAppGeneratedExplicitMappingEntity.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntAppGeneratedExplicitMappingEntity.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbAutoGeneratedExplicitMappingEntity.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbIdentityGeneratedExplicitMappingEntity.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbSequenceGeneratedExplicitMappingEntity.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70MySQLTest.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70PostgreSQLTest.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70MySQLTest.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70PostgreSQLTest.java create mode 100644 testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/UuidAppGeneratedExplicitMappingEntity.java diff --git a/modules/typed-ids-hibernate-70-testing/build.gradle.kts b/modules/typed-ids-hibernate-70-testing/build.gradle.kts new file mode 100644 index 0000000..84c13a8 --- /dev/null +++ b/modules/typed-ids-hibernate-70-testing/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("framefork.java") +} + +dependencies { + api(project(":typed-ids-testing")) + + api(libs.hibernate.orm.v63) + api(libs.hypersistence.utils.hibernate63) +} diff --git a/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractContainerDataSourceProvider.java b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractContainerDataSourceProvider.java new file mode 100644 index 0000000..102d739 --- /dev/null +++ b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractContainerDataSourceProvider.java @@ -0,0 +1,64 @@ +package org.framefork.typedIds.hibernate.tests; + +import io.hypersistence.utils.test.providers.DataSourceProvider; +import org.testcontainers.containers.JdbcDatabaseContainer; + +import javax.sql.DataSource; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +public abstract class AbstractContainerDataSourceProvider implements DataSourceProvider +{ + + private final AtomicReference> container = new AtomicReference<>(); + + public JdbcDatabaseContainer getContainer() + { + if (container.get() == null) { + synchronized (container) { + if (container.get() == null) { + container.set(initContainer()); + } + } + } + + return Objects.requireNonNull(container.get(), "database container must not be null"); + } + + private JdbcDatabaseContainer initContainer() + { + var newContainer = (JdbcDatabaseContainer) newJdbcDatabaseContainer(); + + if (supportsDatabaseName()) { + newContainer.withDatabaseName(databaseName()); + } + if (supportsCredentials()) { + newContainer.withUsername(username()).withPassword(password()); + } + + newContainer.withReuse(true).start(); + + return newContainer; + } + + @Override + public final DataSource dataSource() + { + getContainer(); // force init + return newDataSource(); + } + + @Override + public final String url() + { + return getContainer().getJdbcUrl(); + } + + public String databaseName() + { + return "framefork"; + } + + protected abstract DataSource newDataSource(); + +} diff --git a/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractMySQLIntegrationTest.java b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractMySQLIntegrationTest.java new file mode 100644 index 0000000..7269ec6 --- /dev/null +++ b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractMySQLIntegrationTest.java @@ -0,0 +1,31 @@ +package org.framefork.typedIds.hibernate.tests; + +import io.hypersistence.utils.test.AbstractHibernateTest; +import io.hypersistence.utils.test.providers.DataSourceProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public abstract class AbstractMySQLIntegrationTest extends AbstractHibernateTest +{ + + @BeforeEach + @Override + public void init() + { + super.init(); + } + + @AfterEach + @Override + public void destroy() + { + super.destroy(); + } + + @Override + protected DataSourceProvider dataSourceProvider() + { + return MySQLDataSourceProvider.V8; + } + +} diff --git a/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractPostgreSQLIntegrationTest.java b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractPostgreSQLIntegrationTest.java new file mode 100644 index 0000000..b9011f1 --- /dev/null +++ b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/AbstractPostgreSQLIntegrationTest.java @@ -0,0 +1,31 @@ +package org.framefork.typedIds.hibernate.tests; + +import io.hypersistence.utils.test.AbstractHibernateTest; +import io.hypersistence.utils.test.providers.DataSourceProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public abstract class AbstractPostgreSQLIntegrationTest extends AbstractHibernateTest +{ + + @BeforeEach + @Override + public void init() + { + super.init(); + } + + @AfterEach + @Override + public void destroy() + { + super.destroy(); + } + + @Override + protected DataSourceProvider dataSourceProvider() + { + return PostgreSQLDataSourceProvider.V16; + } + +} diff --git a/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/MySQLDataSourceProvider.java b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/MySQLDataSourceProvider.java new file mode 100644 index 0000000..3d67126 --- /dev/null +++ b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/MySQLDataSourceProvider.java @@ -0,0 +1,67 @@ +package org.framefork.typedIds.hibernate.tests; + +import com.mysql.cj.jdbc.MysqlDataSource; +import org.hibernate.dialect.Database; +import org.hibernate.dialect.MySQLDialect; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.MySQLContainer; + +import javax.sql.DataSource; +import java.util.Map; + +public final class MySQLDataSourceProvider extends AbstractContainerDataSourceProvider +{ + + public static final MySQLDataSourceProvider V8 = new MySQLDataSourceProvider("8.4"); + + private final String version; + + private MySQLDataSourceProvider(final String version) + { + this.version = version; + } + + @Override + public Database database() + { + return Database.MYSQL; + } + + @Override + public String hibernateDialect() + { + return MySQLDialect.class.getName(); + } + + @Override + protected DataSource newDataSource() + { + var dataSource = new MysqlDataSource(); + dataSource.setURL(url()); + dataSource.setUser(username()); + dataSource.setPassword(password()); + return dataSource; + } + + @Override + public String username() + { + return "mysql"; + } + + @Override + public String password() + { + return "admin"; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public JdbcDatabaseContainer newJdbcDatabaseContainer() + { + var container = new MySQLContainer("mysql:" + version); + container.withTmpFs(Map.of("/var/lib/mysql", "rw")); + return container; + } + +} diff --git a/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/PostgreSQLDataSourceProvider.java b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/PostgreSQLDataSourceProvider.java new file mode 100644 index 0000000..40dfd79 --- /dev/null +++ b/modules/typed-ids-hibernate-70-testing/src/main/java/org/framefork/typedIds/hibernate/tests/PostgreSQLDataSourceProvider.java @@ -0,0 +1,70 @@ +package org.framefork.typedIds.hibernate.tests; + +import org.hibernate.dialect.Database; +import org.hibernate.dialect.PostgreSQLDialect; +import org.postgresql.ds.PGSimpleDataSource; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.PostgreSQLContainer; + +import javax.sql.DataSource; +import java.util.Map; + +public final class PostgreSQLDataSourceProvider extends AbstractContainerDataSourceProvider +{ + + public static final PostgreSQLDataSourceProvider V16 = new PostgreSQLDataSourceProvider("16.4"); + + private final String version; + + private PostgreSQLDataSourceProvider(final String version) + { + this.version = version; + } + + @Override + public Database database() + { + return Database.POSTGRESQL; + } + + @Override + public String hibernateDialect() + { + return PostgreSQLDialect.class.getName(); + } + + @Override + protected DataSource newDataSource() + { + var dataSource = new PGSimpleDataSource(); + dataSource.setURL(url()); + dataSource.setUser(username()); + dataSource.setPassword(password()); + + return dataSource; + } + + @Override + public String username() + { + return "postgres"; + } + + @Override + public String password() + { + return "admin"; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public JdbcDatabaseContainer newJdbcDatabaseContainer() + { + var container = new PostgreSQLContainer("postgres:" + version); + container.withCommand("postgres", "-c", "fsync=off", "-c", "random_page_cost=1.0", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"); + container.withEnv(Map.of("PGDATA", "/var/lib/postgresql/data")); + container.withTmpFs(Map.of("/var/lib/postgresql/data", "rw")); + return container; + } + +} diff --git a/modules/typed-ids-hibernate-70/build.gradle.kts b/modules/typed-ids-hibernate-70/build.gradle.kts new file mode 100644 index 0000000..7da241e --- /dev/null +++ b/modules/typed-ids-hibernate-70/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("framefork.java-public") +} + +dependencies { + api(project(":typed-ids")) + api(libs.hibernate.orm.v63) + + compileOnly(libs.jetbrains.annotations) + + compileOnly(libs.autoService.annotations) + annotationProcessor(libs.autoService.processor) + + testImplementation(project(":typed-ids-hibernate-70-testing")) + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +project.description = "TypeIds seamless integration into Hibernate ORMs type system" diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java new file mode 100644 index 0000000..08021eb --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java @@ -0,0 +1,171 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.ObjectBigIntIdTypeUtils; +import org.framefork.typedIds.common.ReflectionHacks; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.BasicJavaType; +import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; +import org.hibernate.type.descriptor.java.LongJavaType; +import org.hibernate.type.descriptor.java.MutabilityPlan; +import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import org.hibernate.usertype.DynamicParameterizedType; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Type; +import java.util.Objects; +import java.util.Properties; + +public class ObjectBigIntIdJavaType implements BasicJavaType>, DynamicParameterizedType, Serializable +{ + + private final LongJavaType inner; + + @Nullable + private Class> identifierClass; + @Nullable + private MethodHandle constructor; + + public ObjectBigIntIdJavaType() + { + this.inner = LongJavaType.INSTANCE; + } + + @SuppressWarnings("unchecked") + @Override + public void setParameterValues(final Properties parameters) + { + var parameterType = (ParameterType) parameters.get(PARAMETER_TYPE); + if (parameterType != null) { + this.identifierClass = (Class>) parameterType.getReturnedClass(); + + } else { + String entityClass = Objects.requireNonNull(parameters.get(ENTITY), "parameters.get(ENTITY) must not be null").toString(); + String propertyName = Objects.requireNonNull(parameters.get(PROPERTY), "parameters.get(PROPERTY) must not be null").toString(); + + this.identifierClass = ReflectionHacks.getFieldTypeChecked(entityClass, propertyName, ObjectBigIntId.class); + } + + if (!ObjectBigIntId.class.isAssignableFrom(identifierClass)) { + throw new IllegalArgumentException("Type %s is not a subtype of %s".formatted(identifierClass, ObjectBigIntId.class)); + } + + this.constructor = ReflectionHacks.getConstructor(identifierClass, long.class); + } + + @Override + public Type getJavaType() + { + return getJavaTypeClass(); + } + + @Override + public Class> getJavaTypeClass() + { + return Objects.requireNonNull(identifierClass, "identifierClass must not be null"); + } + + @Override + public int extractHashCode(final ObjectBigIntId value) + { + return Objects.hashCode(value); + } + + @Override + public boolean areEqual( + final ObjectBigIntId one, + final ObjectBigIntId another + ) + { + return Objects.equals(one, another); + } + + @Override + public JdbcType getRecommendedJdbcType(final JdbcTypeIndicators indicators) + { + final JdbcType descriptor = indicators.getJdbcType(indicators.resolveJdbcTypeCode(SqlTypes.BIGINT)); + return descriptor instanceof AdjustableJdbcType + ? ((AdjustableJdbcType) descriptor).resolveIndicatedType(indicators, this) + : descriptor; + } + + @Contract("null, _, _ -> null; !null, _, _ -> !null") + @Nullable + @Override + public X unwrap( + @Nullable final ObjectBigIntId value, + @NonNull final Class type, + @Nullable final WrapperOptions options + ) + { + if (value == null) { + return null; + } + + return inner.unwrap(value.toLong(), type, options); + } + + @Contract("null, _ -> null; !null, _ -> !null") + @Nullable + @Override + public ObjectBigIntId wrap( + @Nullable final X value, + @Nullable final WrapperOptions options + ) + { + if (value == null) { + return null; + } + + return wrapBigIntToIdentifier(inner.wrap(value, options)); + } + + @Nullable + @Override + public ObjectBigIntId fromString(@Nullable final CharSequence string) + { + return (string == null) ? null : wrapBigIntToIdentifier(Long.parseLong(string.toString())); + } + + @Override + public MutabilityPlan> getMutabilityPlan() + { + return ImmutableMutabilityPlan.instance(); + } + + private ObjectBigIntId wrapBigIntToIdentifier(final long id) + { + return ObjectBigIntIdTypeUtils.wrapBigIntToIdentifier( + id, + Objects.requireNonNull(constructor, "constructor was not yet initialized") + ); + } + + @Override + public String toString() + { + return "object-bigint-id(%s)".formatted(identifierClass != null ? identifierClass.getName() : "???"); + } + + @SuppressWarnings("unused") + private void writeObject(final ObjectOutputStream stream) + { + throw new UnsupportedOperationException("Serialization not supported"); + } + + @SuppressWarnings("unused") + private void readObject(final ObjectInputStream stream) + { + throw new UnsupportedOperationException("Serialization not supported"); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdType.java new file mode 100644 index 0000000..d5eb01c --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdType.java @@ -0,0 +1,65 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.common.hibernate.ImmutableType; +import org.framefork.typedIds.common.hibernate.ParameterizedTypeUtils; +import org.hibernate.HibernateException; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.jspecify.annotations.Nullable; + +public class ObjectBigIntIdType extends ImmutableType, ObjectBigIntIdJavaType> +{ + + public static final String NAME = "object-bigint-id"; + + public ObjectBigIntIdType() + { + this((JdbcType) null); + } + + public ObjectBigIntIdType(@Nullable final JdbcType longJdbcType) + { + super(new ObjectBigIntIdJavaType(), longJdbcType); + } + + public ObjectBigIntIdType(final Class implClass) + { + this(implClass, null); + } + + @SuppressWarnings("this-escape") + public ObjectBigIntIdType(final Class implClass, @Nullable final JdbcType longJdbcType) + { + this(longJdbcType); + this.setParameterValues(ParameterizedTypeUtils.forClass(implClass)); + } + + @Override + public String getName() + { + return NAME; + } + + @Override + public int getSqlType() + { + return SqlTypes.BIGINT; + } + + public ObjectBigIntId wrapJdbcValue(final Object value) + { + if (value instanceof Long longValue) { + return getExpressibleJavaType().wrap(longValue, null); + } + if (value instanceof Number numberValue) { + return wrapJdbcValue(numberValue.longValue()); + } + if (getReturnedClass().isInstance(value)) { + return getReturnedClass().cast(value); + } + + throw new HibernateException("Could not convert '%s' to '%s'".formatted(value.getClass().getName(), getReturnedClass())); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java new file mode 100644 index 0000000..b935142 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java @@ -0,0 +1,60 @@ +package org.framefork.typedIds.bigint.hibernate; + +import com.google.auto.service.AutoService; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.id.ObjectBigIntIdIdentityGenerator; +import org.framefork.typedIds.bigint.hibernate.id.ObjectBigIntIdSequenceStyleGenerator; +import org.hibernate.boot.ResourceStreamLocator; +import org.hibernate.boot.spi.AdditionalMappingContributions; +import org.hibernate.boot.spi.AdditionalMappingContributor; +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.SimpleValue; + +@AutoService(AdditionalMappingContributor.class) +public class ObjectBigIntIdTypeGenerationMetadataContributor implements AdditionalMappingContributor +{ + + @Override + public String getContributorName() + { + return this.getClass().getSimpleName(); + } + + @Override + public void contribute( + final AdditionalMappingContributions contributions, + final InFlightMetadataCollector metadata, + final ResourceStreamLocator resourceStreamLocator, + final MetadataBuildingContext buildingContext + ) + { + // TODO: handle mapped superclass? + + for (PersistentClass entityBinding : metadata.getEntityBindings()) { + var identifier = entityBinding.getIdentifier(); + if (identifier instanceof SimpleValue simpleValueIdentifier) { + var identifierClass = simpleValueIdentifier.getType().getReturnedClass(); + if (!ObjectBigIntId.class.isAssignableFrom(identifierClass)) { + continue; + } + + remapIdentifierGeneratorStrategy(simpleValueIdentifier); + } + } + } + + private void remapIdentifierGeneratorStrategy(final SimpleValue identifier) + { + var newIdentifierGeneratorStrategy = switch (identifier.getIdentifierGeneratorStrategy()) { + case "org.hibernate.id.enhanced.SequenceStyleGenerator" -> ObjectBigIntIdSequenceStyleGenerator.class.getName(); + case "org.hibernate.id.IdentityGenerator" -> ObjectBigIntIdIdentityGenerator.class.getName(); + case "identity" -> ObjectBigIntIdIdentityGenerator.class.getName(); + default -> identifier.getIdentifierGeneratorStrategy(); + }; + + identifier.setIdentifierGeneratorStrategy(newIdentifierGeneratorStrategy); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypesContributor.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypesContributor.java new file mode 100644 index 0000000..449499a --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypesContributor.java @@ -0,0 +1,41 @@ +package org.framefork.typedIds.bigint.hibernate; + +import com.google.auto.service.AutoService; +import org.framefork.typedIds.TypedIdsRegistry; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.spi.TypeConfiguration; + +@SuppressWarnings("rawtypes") +@AutoService(TypeContributor.class) +public class ObjectBigIntIdTypesContributor implements TypeContributor +{ + + @Override + public void contribute( + final TypeContributions typeContributions, + final ServiceRegistry serviceRegistry + ) + { + contributeIndexedTypes(typeContributions); + } + + private void contributeIndexedTypes(final TypeContributions typeContributions) + { + TypeConfiguration typeConfiguration = typeContributions.getTypeConfiguration(); + JdbcType bigintJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor(SqlTypes.BIGINT); + + var idTypes = TypedIdsRegistry.getObjectBigIntIdClasses(); + for (var idType : idTypes) { + var objectBigIntIdType = new ObjectBigIntIdType(idType, bigintJdbcType); + + typeContributions.contributeType(objectBigIntIdType); + + // todo: array type + } + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java new file mode 100644 index 0000000..16c45fd --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java @@ -0,0 +1,121 @@ +package org.framefork.typedIds.bigint.hibernate.id; + +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.framefork.typedIds.common.ReflectionHacks; +import org.hibernate.HibernateException; +import org.hibernate.id.IdentityGenerator; +import org.hibernate.id.PostInsertIdentityPersister; +import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.CustomType; +import org.hibernate.type.Type; +import org.hibernate.type.descriptor.java.spi.JavaTypeBasicAdaptor; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.internal.ImmutableNamedBasicTypeImpl; +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Objects; +import java.util.Properties; + +/** + * This class handles making Hibernate think it's generating a primitive long type instead of a custom type, + * and once Hibernate internals do their job, it then handles wrapping the generated long value in an ObjectBigIntId. + */ +public class ObjectBigIntIdIdentityGenerator extends IdentityGenerator +{ + + @Nullable + private ObjectBigIntIdType objectBigIntIdType; + @Nullable + private ImmutableNamedBasicTypeImpl primitiveType; + + @Override + public void configure(final Type type, final Properties parameters, final ServiceRegistry serviceRegistry) + { + this.objectBigIntIdType = toObjectBigIntIdType(type); + this.primitiveType = new ImmutableNamedBasicTypeImpl<>( + new JavaTypeBasicAdaptor(Long.class), + toJdbcType(objectBigIntIdType), + "bigint" + ); + } + + private ObjectBigIntIdType toObjectBigIntIdType(final Type type) + { + if (type instanceof CustomType customType) { + var userType = customType.getUserType(); + if (userType instanceof ObjectBigIntIdType objectBigIntIdType) { + return objectBigIntIdType; + } + } + + throw new HibernateException("The given type is expected to be a CustomType wrapper over a %s, but was '%s' instead".formatted(ObjectBigIntIdType.class.getSimpleName(), type)); + } + + private JdbcType toJdbcType(final ObjectBigIntIdType objectBigIntIdType) + { + return objectBigIntIdType.getJdbcType(null); + } + + @Override + public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(final PostInsertIdentityPersister persister) + { + var persisterProxy = proxyPersister(persister); + + var originalDelegate = super.getGeneratedIdentifierDelegate(persisterProxy); + + return proxyIdentifierDelegate(originalDelegate); + } + + private PostInsertIdentityPersister proxyPersister(final PostInsertIdentityPersister persister) + { + return (PostInsertIdentityPersister) Proxy.newProxyInstance( + persister.getClass().getClassLoader(), + ReflectionHacks.getAllInterfaces(persister), + new InvocationHandler() + { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + if ("getIdentifierType".equals(method.getName()) && (args == null || args.length == 0)) { + // Override the getIdentifierType method + // Return a primitive long type instead of the custom type + return Objects.requireNonNull(primitiveType, "primitiveType must not be null"); + } + + // For all other methods, delegate to the original persister + return method.invoke(persister, args); + } + } + ); + } + + private InsertGeneratedIdentifierDelegate proxyIdentifierDelegate(final InsertGeneratedIdentifierDelegate delegate) + { + return (InsertGeneratedIdentifierDelegate) Proxy.newProxyInstance( + delegate.getClass().getClassLoader(), + ReflectionHacks.getAllInterfaces(delegate), + new InvocationHandler() + { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + // For all other methods, delegate to the original persister + Object result = method.invoke(delegate, args); + + if ("performInsert".equals(method.getName())) { + var idType = Objects.requireNonNull(objectBigIntIdType, "objectBigIntIdType must not be null"); + return idType.wrapJdbcValue(result); + + } else { + return result; + } + } + } + ); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java new file mode 100644 index 0000000..b713e4a --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java @@ -0,0 +1,66 @@ +package org.framefork.typedIds.bigint.hibernate.id; + +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.CustomType; +import org.hibernate.type.Type; +import org.hibernate.type.descriptor.java.spi.JavaTypeBasicAdaptor; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.internal.ImmutableNamedBasicTypeImpl; +import org.jspecify.annotations.Nullable; + +import java.util.Objects; +import java.util.Properties; + +/** + * This class handles making Hibernate think it's generating a primitive long type instead of a custom type, + * and once Hibernate internals do their job, it then handles wrapping the generated long value in an ObjectBigIntId. + */ +public class ObjectBigIntIdSequenceStyleGenerator extends SequenceStyleGenerator +{ + + @Nullable + private ObjectBigIntIdType objectBigIntIdType; + + @Override + public Object generate(final SharedSessionContractImplementor session, final Object object) + { + var idType = Objects.requireNonNull(objectBigIntIdType, "objectBigIntIdType must not be null"); + return idType.wrapJdbcValue(super.generate(session, object)); + } + + @Override + public void configure(final Type type, final Properties parameters, final ServiceRegistry serviceRegistry) + { + this.objectBigIntIdType = toObjectBigIntIdType(type); + + var primitiveType = new ImmutableNamedBasicTypeImpl<>( + new JavaTypeBasicAdaptor(Long.class), + toJdbcType(objectBigIntIdType), + "bigint" + ); + + super.configure(primitiveType, parameters, serviceRegistry); + } + + private ObjectBigIntIdType toObjectBigIntIdType(final Type type) + { + if (type instanceof CustomType customType) { + var userType = customType.getUserType(); + if (userType instanceof ObjectBigIntIdType objectBigIntIdType) { + return objectBigIntIdType; + } + } + + throw new HibernateException("The given type is expected to be a CustomType wrapper over a %s, but was '%s' instead".formatted(ObjectBigIntIdType.class.getSimpleName(), type)); + } + + private JdbcType toJdbcType(final ObjectBigIntIdType objectBigIntIdType) + { + return objectBigIntIdType.getJdbcType(null); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java new file mode 100644 index 0000000..1e38a74 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java @@ -0,0 +1,426 @@ +package org.framefork.typedIds.common.hibernate; + +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.Mapping; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.query.BindableType; +import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.type.ForeignKeyDirection; +import org.hibernate.type.Type; +import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; +import org.hibernate.type.descriptor.java.IncomparableComparator; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.java.MutabilityPlan; +import org.hibernate.type.descriptor.java.MutabilityPlanExposer; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.spi.TypeConfiguration; +import org.hibernate.type.spi.TypeConfigurationAware; +import org.hibernate.usertype.DynamicParameterizedType; +import org.hibernate.usertype.EnhancedUserType; +import org.hibernate.usertype.ParameterizedType; +import org.hibernate.usertype.UserType; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +@ApiStatus.Internal +public abstract class ImmutableType> implements + UserType, + Type, + EnhancedUserType, + BindableType, + SqmExpressible, + DomainType, + MutabilityPlanExposer, + DynamicParameterizedType, + TypeConfigurationAware +{ + + private final JavaTypeType javaType; + @Nullable + private JdbcType jdbcType; + + @Nullable + private TypeConfiguration typeConfiguration; + + protected ImmutableType( + final JavaTypeType javaType, + @Nullable final JdbcType jdbcType + ) + { + this.javaType = Objects.requireNonNull(javaType, "javaType must not be null"); + this.jdbcType = jdbcType; + } + + @Override + public void setTypeConfiguration(final TypeConfiguration typeConfiguration) + { + this.typeConfiguration = typeConfiguration; + getJdbcType(null); // initialize + } + + @Nullable + @Override + public TypeConfiguration getTypeConfiguration() + { + return typeConfiguration; + } + + @Override + public JavaTypeType getExpressibleJavaType() + { + return javaType; + } + + @Override + public Class getBindableJavaType() + { + return returnedClass(); + } + + @Override + public JdbcType getJdbcType(@Nullable final TypeConfiguration typeConfiguration) + { + if (jdbcType == null && getTypeConfiguration() != null) { + jdbcType = getTypeConfiguration().getJdbcTypeRegistry().getDescriptor(getSqlType()); + } + + return Objects.requireNonNull(jdbcType, "jdbcType must not be null"); + } + + @Override + public Class returnedClass() + { + return javaType.getJavaTypeClass(); + } + + @Override + public Class getReturnedClass() + { + return returnedClass(); + } + + @Override + public String getName() + { + return getClass().getSimpleName(); + } + + @Override + public void setParameterValues(final Properties parameters) + { + if (javaType instanceof ParameterizedType javaTypeParametrized) { + javaTypeParametrized.setParameterValues(parameters); + } + } + + @Nullable + protected T get(final ResultSet rs, final int position, final SharedSessionContractImplementor session, final Object owner) throws SQLException + { + return getJdbcType(null).getExtractor(getExpressibleJavaType()).extract(rs, position, session); + } + + protected void set(final PreparedStatement st, @Nullable final T value, final int index, final SharedSessionContractImplementor session) throws SQLException + { + getJdbcType(null).getBinder(getExpressibleJavaType()).bind(st, value, index, session); + } + + @Nullable + @Override + public T nullSafeGet(final ResultSet rs, final int position, final SharedSessionContractImplementor session, final Object owner) throws SQLException + { + return get(rs, position, session, owner); + } + + @Override + public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final SharedSessionContractImplementor session) throws SQLException + { + set(st, returnedClass().cast(value), index, session); + } + + @Override + public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final boolean[] settable, final SharedSessionContractImplementor session) throws SQLException + { + set(st, returnedClass().cast(value), index, session); + } + + @Override + public boolean equals(final Object x, final Object y) + { + return Objects.equals(x, y); + } + + @Override + public int hashCode(final Object x) + { + return x.hashCode(); + } + + @Override + public boolean isMutable() + { + return false; + } + + @Override + public MutabilityPlan getExposedMutabilityPlan() + { + return ImmutableMutabilityPlan.instance(); + } + + @Override + public boolean isAssociationType() + { + return false; + } + + @Override + public boolean isCollectionType() + { + return false; + } + + @Override + public boolean isEntityType() + { + return false; + } + + @Override + public boolean isAnyType() + { + return false; + } + + @Override + public boolean isComponentType() + { + return false; + } + + @SuppressWarnings("deprecation") + @Override + public int getColumnSpan(final Mapping mapping) + { + return 1; + } + + @Override + public boolean isSame(final Object x, final Object y) + { + return equals(x, y); + } + + @Override + public boolean isEqual(final Object x, final Object y) + { + return equals(x, y); + } + + @Override + public boolean isEqual(final Object x, final Object y, final SessionFactoryImplementor factory) + { + return equals(x, y); + } + + @Override + public int getHashCode(final Object x) + { + return hashCode(x); + } + + @Override + public int getHashCode(final Object x, final SessionFactoryImplementor factory) + { + return hashCode(x); + } + + @Override + public int compare(final Object x, final Object y, final SessionFactoryImplementor sessionFactoryImplementor) + { + return compare(x, y); + } + + @SuppressWarnings("unchecked") + @Override + public int compare(final Object x, final Object y) + { + if (x instanceof Comparable xComparable && y instanceof Comparable yComparable) { + return ((Comparator) getComparator()).compare(xComparable, yComparable); + } + + return IncomparableComparator.INSTANCE.compare(x, y); + } + + @SuppressWarnings("unchecked") + public Comparator getComparator() + { + @Nullable Comparator comparator = javaType.getComparator(); + return comparator != null ? comparator : (Comparator) IncomparableComparator.INSTANCE; + } + + @Override + public final boolean isDirty(final Object old, final Object current, final SharedSessionContractImplementor session) + { + return isDirty(old, current); + } + + @Override + public final boolean isDirty(final Object old, final Object current, final boolean[] checkable, final SharedSessionContractImplementor session) + { + return checkable[0] && isDirty(old, current); + } + + protected final boolean isDirty(final Object old, final Object current) + { + return !isSame(old, current); + } + + @Override + public boolean isModified(final Object dbState, final Object currentState, final boolean[] checkable, final SharedSessionContractImplementor session) + { + return isDirty(dbState, currentState); + } + + @SuppressWarnings("unchecked") + @Override + public T deepCopy(final Object value) + { + return (T) value; + } + + @Override + public Object deepCopy(final Object value, final SessionFactoryImplementor factory) + { + return deepCopy(value); + } + + @Override + public Serializable disassemble(final Object o) + { + return (Serializable) o; + } + + @Override + public Serializable disassemble(final Object value, final SharedSessionContractImplementor session, final Object owner) + { + return disassemble(value); + } + + @SuppressWarnings("unchecked") + @Override + public T assemble(final Serializable cached, final Object owner) + { + return (T) cached; + } + + @Override + public Object assemble(final Serializable cached, final SharedSessionContractImplementor session, final Object owner) + { + return assemble(cached, session); + } + + @Override + public void beforeAssemble(final Serializable cached, final SharedSessionContractImplementor session) + { + + } + + @SuppressWarnings("unchecked") + @Override + public T replace(final Object o, final Object target, final Object owner) + { + return (T) o; + } + + @SuppressWarnings("rawtypes") + @Override + public Object replace(final Object original, final Object target, final SharedSessionContractImplementor session, final Object owner, final Map copyCache) + { + return replace(original, target, owner); + } + + @SuppressWarnings("rawtypes") + @Override + public Object replace(final Object original, final Object target, final SharedSessionContractImplementor session, final Object owner, final Map copyCache, final ForeignKeyDirection foreignKeyDirection) + { + return replace(original, target, owner); + } + + @SuppressWarnings("deprecation") + @Override + public boolean[] toColumnNullness(final Object value, final Mapping mapping) + { + return value == null ? ArrayHelper.FALSE : ArrayHelper.TRUE; + } + + @Override + public long getDefaultSqlLength(final Dialect dialect, final JdbcType jdbcType) + { + return javaType.getDefaultSqlLength(dialect, jdbcType); + } + + @Override + public int getDefaultSqlPrecision(final Dialect dialect, final JdbcType jdbcType) + { + return javaType.getDefaultSqlPrecision(dialect, jdbcType); + } + + @Override + public int getDefaultSqlScale(final Dialect dialect, final JdbcType jdbcType) + { + return javaType.getDefaultSqlScale(dialect, jdbcType); + } + + @SuppressWarnings("deprecation") + @Override + public int[] getSqlTypeCodes(final Mapping mapping) + { + return new int[]{getSqlType()}; + } + + @Override + public T fromStringValue(final CharSequence sequence) + { + return javaType.fromString(sequence); + } + + @Override + public String toLoggableString(@Nullable final Object value, final SessionFactoryImplementor factory) + { + return String.valueOf(value); + } + + @Nullable + @Override + public String toSqlLiteral(@Nullable final T o) + { + return (o != null) ? String.format(Locale.ROOT, "'%s'", o) : null; + } + + @Nullable + @Override + public String toString(@Nullable final T o) + { + return (o != null) ? o.toString() : null; + } + + @Override + public String toString() + { + return "%s backed by %s".formatted(javaType, getJdbcType(null)); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java new file mode 100644 index 0000000..23f428e --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java @@ -0,0 +1,84 @@ +package org.framefork.typedIds.common.hibernate; + +import org.hibernate.usertype.DynamicParameterizedType; +import org.jetbrains.annotations.ApiStatus; + +import java.lang.annotation.Annotation; +import java.util.Properties; + +@ApiStatus.Internal +public final class ParameterizedTypeUtils +{ + + private ParameterizedTypeUtils() + { + } + + public static Properties forClass(final Class implClass) + { + var parameters = new Properties(); + parameters.put(DynamicParameterizedType.PARAMETER_TYPE, new ParameterizedParameterType(implClass)); + return parameters; + } + + public static final class ParameterizedParameterType implements DynamicParameterizedType.ParameterType + { + + private final Class clazz; + + public ParameterizedParameterType(final Class clazz) + { + this.clazz = clazz; + } + + @Override + public Class getReturnedClass() + { + return clazz; + } + + @Override + public Annotation[] getAnnotationsMethod() + { + return new Annotation[0]; + } + + @Override + public String getCatalog() + { + throw new UnsupportedOperationException(); + } + + @Override + public String getSchema() + { + throw new UnsupportedOperationException(); + } + + @Override + public String getTable() + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPrimaryKey() + { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getColumns() + { + throw new UnsupportedOperationException(); + } + + @Override + public Long[] getColumnLengths() + { + throw new UnsupportedOperationException(); + } + + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java new file mode 100644 index 0000000..37f780f --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java @@ -0,0 +1,187 @@ +package org.framefork.typedIds.uuid.hibernate; + +import org.framefork.typedIds.common.ReflectionHacks; +import org.framefork.typedIds.uuid.ObjectUuid; +import org.framefork.typedIds.uuid.ObjectUuidTypeUtils; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.Size; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.BasicJavaType; +import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; +import org.hibernate.type.descriptor.java.MutabilityPlan; +import org.hibernate.type.descriptor.java.UUIDJavaType; +import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType; +import org.hibernate.usertype.DynamicParameterizedType; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Type; +import java.util.Objects; +import java.util.Properties; +import java.util.UUID; + +public class ObjectUuidJavaType implements BasicJavaType>, DynamicParameterizedType, Serializable +{ + + public static final int UUID_BYTE_LENGTH = 16; + + private final UUIDJavaType inner; + + @Nullable + private Class> identifierClass; + @Nullable + private MethodHandle constructor; + + public ObjectUuidJavaType() + { + this.inner = UUIDJavaType.INSTANCE; + } + + @SuppressWarnings("unchecked") + @Override + public void setParameterValues(final Properties parameters) + { + var parameterType = (ParameterType) parameters.get(PARAMETER_TYPE); + if (parameterType != null) { + this.identifierClass = (Class>) parameterType.getReturnedClass(); + + } else { + String entityClass = Objects.requireNonNull(parameters.get(ENTITY), "parameters.get(ENTITY) must not be null").toString(); + String propertyName = Objects.requireNonNull(parameters.get(PROPERTY), "parameters.get(PROPERTY) must not be null").toString(); + + this.identifierClass = ReflectionHacks.getFieldTypeChecked(entityClass, propertyName, ObjectUuid.class); + } + + if (!ObjectUuid.class.isAssignableFrom(identifierClass)) { + throw new IllegalArgumentException("Type %s is not a subtype of %s".formatted(identifierClass, ObjectUuid.class)); + } + + this.constructor = ReflectionHacks.getConstructor(identifierClass, UUID.class); + } + + @Override + public Type getJavaType() + { + return getJavaTypeClass(); + } + + @Override + public Class> getJavaTypeClass() + { + return Objects.requireNonNull(identifierClass, "identifierClass must not be null"); + } + + @Override + public int extractHashCode(final ObjectUuid value) + { + return Objects.hashCode(value); + } + + @Override + public boolean areEqual( + final ObjectUuid one, + final ObjectUuid another + ) + { + return Objects.equals(one, another); + } + + @Override + public JdbcType getRecommendedJdbcType(final JdbcTypeIndicators indicators) + { + final JdbcType descriptor = indicators.getJdbcType(indicators.resolveJdbcTypeCode(SqlTypes.UUID)); + return descriptor instanceof AdjustableJdbcType + ? ((AdjustableJdbcType) descriptor).resolveIndicatedType(indicators, this) + : descriptor; + } + + @Override + public long getDefaultSqlLength(final Dialect dialect, final JdbcType jdbcType) + { + if (jdbcType instanceof VarbinaryJdbcType) { + return UUID_BYTE_LENGTH; + } + + return Size.DEFAULT_LENGTH; + } + + @Contract("null, _, _ -> null; !null, _, _ -> !null") + @Nullable + @Override + public X unwrap( + @Nullable final ObjectUuid value, + @NonNull final Class type, + @Nullable final WrapperOptions options + ) + { + if (value == null) { + return null; + } + + return inner.unwrap(value.toNativeUuid(), type, options); + } + + @Contract("null, _ -> null; !null, _ -> !null") + @Nullable + @Override + public ObjectUuid wrap( + @Nullable final X value, + @Nullable final WrapperOptions options + ) + { + if (value == null) { + return null; + } + + return wrapUuidToIdentifier(inner.wrap(value, options)); + } + + @Nullable + @Override + public ObjectUuid fromString(@Nullable final CharSequence string) + { + return (string == null) ? null : wrapUuidToIdentifier(UUID.fromString(string.toString())); + } + + @Override + public MutabilityPlan> getMutabilityPlan() + { + return ImmutableMutabilityPlan.instance(); + } + + private ObjectUuid wrapUuidToIdentifier(final UUID uuid) + { + return ObjectUuidTypeUtils.wrapUuidToIdentifier( + uuid, + Objects.requireNonNull(constructor, "constructor was not yet initialized") + ); + } + + @Override + public String toString() + { + return "object-uuid(%s)".formatted(identifierClass != null ? identifierClass.getName() : "???"); + } + + @SuppressWarnings("unused") + private void writeObject(final ObjectOutputStream stream) + { + throw new UnsupportedOperationException("Serialization not supported"); + } + + @SuppressWarnings("unused") + private void readObject(final ObjectInputStream stream) + { + throw new UnsupportedOperationException("Serialization not supported"); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidType.java new file mode 100644 index 0000000..04a315f --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidType.java @@ -0,0 +1,49 @@ +package org.framefork.typedIds.uuid.hibernate; + +import org.framefork.typedIds.common.hibernate.ImmutableType; +import org.framefork.typedIds.common.hibernate.ParameterizedTypeUtils; +import org.framefork.typedIds.uuid.ObjectUuid; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.jspecify.annotations.Nullable; + +public class ObjectUuidType extends ImmutableType, ObjectUuidJavaType> +{ + + public static final String NAME = "object-uuid"; + + public ObjectUuidType() + { + this((JdbcType) null); + } + + public ObjectUuidType(@Nullable final JdbcType uuidJdbcType) + { + super(new ObjectUuidJavaType(), uuidJdbcType); + } + + public ObjectUuidType(final Class implClass) + { + this(implClass, null); + } + + @SuppressWarnings("this-escape") + public ObjectUuidType(final Class implClass, @Nullable final JdbcType uuidJdbcType) + { + this(uuidJdbcType); + this.setParameterValues(ParameterizedTypeUtils.forClass(implClass)); + } + + @Override + public String getName() + { + return NAME; + } + + @Override + public int getSqlType() + { + return SqlTypes.UUID; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypesContributor.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypesContributor.java new file mode 100644 index 0000000..3168754 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypesContributor.java @@ -0,0 +1,76 @@ +package org.framefork.typedIds.uuid.hibernate; + +import com.google.auto.service.AutoService; +import org.framefork.typedIds.TypedIdsRegistry; +import org.framefork.typedIds.uuid.hibernate.jdbc.BinaryUuidJdbcType; +import org.framefork.typedIds.uuid.hibernate.jdbc.NativeUuidJdbcType; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.MariaDBDialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; +import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl; +import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; + +@SuppressWarnings("rawtypes") +@AutoService(TypeContributor.class) +public class ObjectUuidTypesContributor implements TypeContributor +{ + + /** + * https://mariadb.com/kb/en/uuid-data-type/ + */ + private static final DatabaseVersion MARIADB_NATIVE_UUID_SINCE = DatabaseVersion.make(10, 7); + + @Override + public void contribute( + final TypeContributions typeContributions, + final ServiceRegistry serviceRegistry + ) + { + remapUuidType(typeContributions, serviceRegistry); + contributeIndexedTypes(typeContributions); + } + + private void remapUuidType(final TypeContributions typeContributions, final ServiceRegistry serviceRegistry) + { + JdbcServices jdbcServices = serviceRegistry.requireService(JdbcServices.class); + Dialect dialect = jdbcServices.getDialect(); + + DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry(); + JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry(); + if (!jdbcTypeRegistry.hasRegisteredDescriptor(SqlTypes.UUID)) { + if (dialect instanceof MariaDBDialect && dialect.getVersion().isSameOrAfter(ObjectUuidTypesContributor.MARIADB_NATIVE_UUID_SINCE)) { + typeContributions.contributeJdbcType(NativeUuidJdbcType.INSTANCE); + + } else if (dialect instanceof MySQLDialect) { + typeContributions.contributeJdbcType(BinaryUuidJdbcType.INSTANCE); + ddlTypeRegistry.addDescriptorIfAbsent(new DdlTypeImpl(SqlTypes.UUID, false, "binary(16)", "binary(16)", "binary(16)", dialect)); + + } else { + typeContributions.contributeJdbcType(NativeUuidJdbcType.INSTANCE); + } + } + } + + private void contributeIndexedTypes(final TypeContributions typeContributions) + { + TypeConfiguration typeConfiguration = typeContributions.getTypeConfiguration(); + JdbcType uuidJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor(SqlTypes.UUID); + + var idTypes = TypedIdsRegistry.getObjectUuidClasses(); + for (var idType : idTypes) { + var objectUuidType = new ObjectUuidType(idType, uuidJdbcType); + + typeContributions.contributeType(objectUuidType); + } + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/BinaryUuidJdbcType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/BinaryUuidJdbcType.java new file mode 100644 index 0000000..6602a6a --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/BinaryUuidJdbcType.java @@ -0,0 +1,48 @@ +package org.framefork.typedIds.uuid.hibernate.jdbc; + +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType; + +public class BinaryUuidJdbcType extends VarbinaryJdbcType +{ + + public static final BinaryUuidJdbcType INSTANCE = new BinaryUuidJdbcType(); + + @Override + public String getFriendlyName() + { + return "uuid-bin(16)"; + } + + @Override + public int getJdbcTypeCode() + { + return SqlTypes.UUID; + } + + @Override + protected boolean shouldUseMaterializedLob(final JdbcTypeIndicators indicators) + { + return false; + } + + @Override + public boolean isLob() + { + return false; + } + + @Override + public boolean isLobOrLong() + { + return false; + } + + @Override + public String toString() + { + return getFriendlyName(); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/NativeUuidJdbcType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/NativeUuidJdbcType.java new file mode 100644 index 0000000..052a526 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/jdbc/NativeUuidJdbcType.java @@ -0,0 +1,35 @@ +package org.framefork.typedIds.uuid.hibernate.jdbc; + +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; + +public class NativeUuidJdbcType extends UUIDJdbcType +{ + + public static final NativeUuidJdbcType INSTANCE = new NativeUuidJdbcType(); + + @Override + public String getFriendlyName() + { + return "uuid"; + } + + @Override + public int getJdbcTypeCode() + { + return SqlTypes.UUID; + } + + @Override + public int getDefaultSqlTypeCode() + { + return SqlTypes.UUID; + } + + @Override + public String toString() + { + return "uuid-native"; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntAppGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntAppGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..a681b84 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntAppGeneratedExplicitMappingEntity.java @@ -0,0 +1,70 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.hibernate.annotations.Type; + +@Entity +@Table(name = BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntAppGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_app_generated_explicit_mapping"; + + @jakarta.persistence.Id + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntAppGeneratedExplicitMappingEntity(final String title) + { + this.id = Id.random(); + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntAppGeneratedExplicitMappingEntity() + { + } + + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbAutoGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbAutoGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..40aab15 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbAutoGeneratedExplicitMappingEntity.java @@ -0,0 +1,75 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntDbAutoGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_db_auto_generated_explicit_mapping"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntDbAutoGeneratedExplicitMappingEntity(final String title) + { + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntDbAutoGeneratedExplicitMappingEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbIdentityGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbIdentityGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..8639305 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbIdentityGeneratedExplicitMappingEntity.java @@ -0,0 +1,75 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntDbIdentityGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_db_identity_generated_explicit_mapping"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntDbIdentityGeneratedExplicitMappingEntity(final String title) + { + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntDbIdentityGeneratedExplicitMappingEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbSequenceGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbSequenceGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..b5bd06d --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntDbSequenceGeneratedExplicitMappingEntity.java @@ -0,0 +1,75 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntDbSequenceGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_db_sequence_generated_explicit_mapping"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntDbSequenceGeneratedExplicitMappingEntity(final String title) + { + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntDbSequenceGeneratedExplicitMappingEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java new file mode 100644 index 0000000..254f667 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java @@ -0,0 +1,240 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectBigIntIdTypeMySQLTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntAppGeneratedExplicitMappingEntity.class, + BigIntDbAutoGeneratedExplicitMappingEntity.class, + BigIntDbIdentityGeneratedExplicitMappingEntity.class, + BigIntDbSequenceGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("extra", String.class)).isEmpty(); + + var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("extra", String.class)).isEmpty(); + + var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).isNull(); + assertThat(table3.get("extra", String.class)).isEqualToIgnoringCase("auto_increment"); + + var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("extra", String.class)).isEmpty(); + }); + } + + private static Tuple getIdColumnInfo(final EntityManager em, final String tableName) + { + return (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", tableName) + .setParameter("column_name", "id") + .getSingleResult(); + } + + @Test + public void testUsageAppGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntAppGeneratedExplicitMappingEntity("one"), + new BigIntAppGeneratedExplicitMappingEntity("two"), + new BigIntAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbAutoGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbAutoGeneratedExplicitMappingEntity("one"), + new BigIntDbAutoGeneratedExplicitMappingEntity("two"), + new BigIntDbAutoGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbAutoGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbIdentityGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbIdentityGeneratedExplicitMappingEntity("one"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("two"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbIdentityGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbSequenceGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbSequenceGeneratedExplicitMappingEntity("one"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("two"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbSequenceGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java new file mode 100644 index 0000000..86769d2 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java @@ -0,0 +1,240 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectBigIntIdTypePostgreSQLTest extends AbstractPostgreSQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntAppGeneratedExplicitMappingEntity.class, + BigIntDbAutoGeneratedExplicitMappingEntity.class, + BigIntDbIdentityGeneratedExplicitMappingEntity.class, + BigIntDbSequenceGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_default", String.class)).isNull(); + assertThat(table1.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table1.get("identity_generation", String.class)).isNull(); + + var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_default", String.class)).isNull(); + assertThat(table2.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table2.get("identity_generation", String.class)).isNull(); + + var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_default", String.class)).startsWith("nextval('"); + assertThat(table3.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table3.get("identity_generation", String.class)).isNull(); + + var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_default", String.class)).isNull(); + assertThat(table4.get("is_identity", String.class)).isEqualTo("NO"); + assertThat(table4.get("identity_generation", String.class)).isNull(); + }); + } + + private static Tuple getIdColumnInfo(final EntityManager em, final String tableName) + { + return (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", tableName) + .setParameter("column_name", "id") + .getSingleResult(); + } + + @Test + public void testUsageAppGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntAppGeneratedExplicitMappingEntity("one"), + new BigIntAppGeneratedExplicitMappingEntity("two"), + new BigIntAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbAutoGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbAutoGeneratedExplicitMappingEntity("one"), + new BigIntDbAutoGeneratedExplicitMappingEntity("two"), + new BigIntDbAutoGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbAutoGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbIdentityGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbIdentityGeneratedExplicitMappingEntity("one"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("two"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbIdentityGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbSequenceGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbSequenceGeneratedExplicitMappingEntity("one"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("two"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbSequenceGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypeMySQLTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypeMySQLTest.java new file mode 100644 index 0000000..516ee4e --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypeMySQLTest.java @@ -0,0 +1,80 @@ +package org.framefork.typedIds.uuid.hibernate; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectUuidTypeMySQLTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + UuidAppGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var result = (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", UuidAppGeneratedExplicitMappingEntity.TABLE_NAME) + .setParameter("column_name", "id") + .getSingleResult(); + + assertThat(result.get("data_type", String.class)).isEqualToIgnoringCase("binary"); + assertThat(result.get("column_type", String.class)).isEqualToIgnoringCase("binary(16)"); + }); + } + + @Test + public void testUsage() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new UuidAppGeneratedExplicitMappingEntity("one"), + new UuidAppGeneratedExplicitMappingEntity("two"), + new UuidAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(UuidAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id = :id", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypePostgreSQLTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypePostgreSQLTest.java new file mode 100644 index 0000000..577ed7c --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypePostgreSQLTest.java @@ -0,0 +1,79 @@ +package org.framefork.typedIds.uuid.hibernate; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectUuidTypePostgreSQLTest extends AbstractPostgreSQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + UuidAppGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var result = (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", UuidAppGeneratedExplicitMappingEntity.TABLE_NAME) + .setParameter("column_name", "id") + .getSingleResult(); + + assertThat(result.get("data_type", String.class)).isEqualToIgnoringCase("uuid"); + }); + } + + @Test + public void testUsage() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new UuidAppGeneratedExplicitMappingEntity("one"), + new UuidAppGeneratedExplicitMappingEntity("two"), + new UuidAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(UuidAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id = :id", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/UuidAppGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/UuidAppGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..16ee30e --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/UuidAppGeneratedExplicitMappingEntity.java @@ -0,0 +1,72 @@ +package org.framefork.typedIds.uuid.hibernate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.framefork.typedIds.uuid.ObjectUuid; +import org.hibernate.annotations.Type; + +import java.util.UUID; + +@Entity +@Table(name = UuidAppGeneratedExplicitMappingEntity.TABLE_NAME) +public class UuidAppGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "uuid_app_generated_explicit_mapping"; + + @jakarta.persistence.Id + @Column(nullable = false) + @Type(ObjectUuidType.class) + private Id id; + + @Column(nullable = false) + private String title; + + public UuidAppGeneratedExplicitMappingEntity(final String title) + { + this.id = Id.random(); + this.title = title; + } + + @SuppressWarnings("NullAway") + protected UuidAppGeneratedExplicitMappingEntity() + { + } + + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectUuid + { + + private Id(final UUID inner) + { + super(inner); + } + + public static Id random() + { + return ObjectUuid.randomUUID(Id::new); + } + + public static Id from(final String value) + { + return ObjectUuid.fromString(Id::new, value); + } + + public static Id from(final UUID value) + { + return ObjectUuid.fromUuid(Id::new, value); + } + + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts b/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts new file mode 100644 index 0000000..02f25d6 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("framefork.java") +} + +dependencies { + implementation(project(":typed-ids-hibernate-70")) + + annotationProcessor(project(":typed-ids-index-java-classes-processor")) + testAnnotationProcessor(project(":typed-ids-index-java-classes-processor")) + + testImplementation(project(":typed-ids-hibernate-70-testing")) + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + constraints { + implementation(libs.hibernate.orm.v66) + } +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntAppGeneratedExplicitMappingEntity.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntAppGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..c5be64b --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntAppGeneratedExplicitMappingEntity.java @@ -0,0 +1,68 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; + +@Entity +@Table(name = BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntAppGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_app_generated_explicit_mapping"; + + @jakarta.persistence.Id + @Column(nullable = false) + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntAppGeneratedExplicitMappingEntity(final String title) + { + this.id = Id.random(); + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntAppGeneratedExplicitMappingEntity() + { + } + + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbAutoGeneratedExplicitMappingEntity.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbAutoGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..7e9f7e8 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbAutoGeneratedExplicitMappingEntity.java @@ -0,0 +1,73 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntDbAutoGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_db_auto_generated_explicit_mapping"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(nullable = false) + @Nullable + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntDbAutoGeneratedExplicitMappingEntity(final String title) + { + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntDbAutoGeneratedExplicitMappingEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbIdentityGeneratedExplicitMappingEntity.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbIdentityGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..e0498e7 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbIdentityGeneratedExplicitMappingEntity.java @@ -0,0 +1,73 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntDbIdentityGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_db_identity_generated_explicit_mapping"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + @Nullable + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntDbIdentityGeneratedExplicitMappingEntity(final String title) + { + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntDbIdentityGeneratedExplicitMappingEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbSequenceGeneratedExplicitMappingEntity.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbSequenceGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..530ef33 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/BigIntDbSequenceGeneratedExplicitMappingEntity.java @@ -0,0 +1,73 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME) +public class BigIntDbSequenceGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "bigint_db_sequence_generated_explicit_mapping"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Column(nullable = false) + @Nullable + private Id id; + + @Column(nullable = false) + private String title; + + public BigIntDbSequenceGeneratedExplicitMappingEntity(final String title) + { + this.title = title; + } + + @SuppressWarnings("NullAway") + protected BigIntDbSequenceGeneratedExplicitMappingEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectBigIntId + { + + private Id(final long inner) + { + super(inner); + } + + public static Id random() + { + return ObjectBigIntId.randomBigInt(Id::new); + } + + public static Id from(final String value) + { + return ObjectBigIntId.fromString(Id::new, value); + } + + public static Id from(final long value) + { + return ObjectBigIntId.fromLong(Id::new, value); + } + + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70MySQLTest.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70MySQLTest.java new file mode 100644 index 0000000..13d74b8 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70MySQLTest.java @@ -0,0 +1,232 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectBigIntIdTypeIndexedHibernate70MySQLTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntAppGeneratedExplicitMappingEntity.class, + BigIntDbAutoGeneratedExplicitMappingEntity.class, + BigIntDbIdentityGeneratedExplicitMappingEntity.class, + BigIntDbSequenceGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table1.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + + var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table2.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + + var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table3.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + + var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + assertThat(table4.get("column_type", String.class)).isEqualToIgnoringCase("bigint"); + }); + } + + private static Tuple getIdColumnInfo(final EntityManager em, final String tableName) + { + return (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", tableName) + .setParameter("column_name", "id") + .getSingleResult(); + } + + @Test + public void testUsageAppGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntAppGeneratedExplicitMappingEntity("one"), + new BigIntAppGeneratedExplicitMappingEntity("two"), + new BigIntAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbAutoGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbAutoGeneratedExplicitMappingEntity("one"), + new BigIntDbAutoGeneratedExplicitMappingEntity("two"), + new BigIntDbAutoGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbAutoGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbIdentityGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbIdentityGeneratedExplicitMappingEntity("one"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("two"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbIdentityGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbSequenceGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbSequenceGeneratedExplicitMappingEntity("one"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("two"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbSequenceGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70PostgreSQLTest.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70PostgreSQLTest.java new file mode 100644 index 0000000..88febde --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70PostgreSQLTest.java @@ -0,0 +1,228 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectBigIntIdTypeIndexedHibernate70PostgreSQLTest extends AbstractPostgreSQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntAppGeneratedExplicitMappingEntity.class, + BigIntDbAutoGeneratedExplicitMappingEntity.class, + BigIntDbIdentityGeneratedExplicitMappingEntity.class, + BigIntDbSequenceGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var table1 = getIdColumnInfo(em, BigIntAppGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table1.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + + var table2 = getIdColumnInfo(em, BigIntDbAutoGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table2.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + + var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + + var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); + assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); + }); + } + + private static Tuple getIdColumnInfo(final EntityManager em, final String tableName) + { + return (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", tableName) + .setParameter("column_name", "id") + .getSingleResult(); + } + + @Test + public void testUsageAppGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntAppGeneratedExplicitMappingEntity("one"), + new BigIntAppGeneratedExplicitMappingEntity("two"), + new BigIntAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbAutoGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbAutoGeneratedExplicitMappingEntity("one"), + new BigIntDbAutoGeneratedExplicitMappingEntity("two"), + new BigIntDbAutoGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbAutoGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbAutoGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbAutoGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbIdentityGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbIdentityGeneratedExplicitMappingEntity("one"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("two"), + new BigIntDbIdentityGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbIdentityGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbIdentityGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + + @Test + public void testUsageDbSequenceGenerated() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new BigIntDbSequenceGeneratedExplicitMappingEntity("one"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("two"), + new BigIntDbSequenceGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(article -> Assertions.assertNull(article.getId())); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> Assertions.assertNotNull(article.getId())); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(BigIntDbSequenceGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id = :id", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM BigIntDbSequenceGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", BigIntDbSequenceGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70MySQLTest.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70MySQLTest.java new file mode 100644 index 0000000..4165b91 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70MySQLTest.java @@ -0,0 +1,80 @@ +package org.framefork.typedIds.uuid.hibernate.v70; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectUuidTypeIndexedHibernate70MySQLTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + UuidAppGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var result = (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", UuidAppGeneratedExplicitMappingEntity.TABLE_NAME) + .setParameter("column_name", "id") + .getSingleResult(); + + assertThat(result.get("data_type", String.class)).isEqualToIgnoringCase("binary"); + assertThat(result.get("column_type", String.class)).isEqualToIgnoringCase("binary(16)"); + }); + } + + @Test + public void testUsage() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new UuidAppGeneratedExplicitMappingEntity("one"), + new UuidAppGeneratedExplicitMappingEntity("two"), + new UuidAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(UuidAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id = :id", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70PostgreSQLTest.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70PostgreSQLTest.java new file mode 100644 index 0000000..af14ee0 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70PostgreSQLTest.java @@ -0,0 +1,79 @@ +package org.framefork.typedIds.uuid.hibernate.v70; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +final class ObjectUuidTypeIndexedHibernate70PostgreSQLTest extends AbstractPostgreSQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + UuidAppGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSchema() + { + doInJPA(em -> { + var result = (Tuple) em.createNativeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = :table_name AND column_name = :column_name", Tuple.class) + .setParameter("table_name", UuidAppGeneratedExplicitMappingEntity.TABLE_NAME) + .setParameter("column_name", "id") + .getSingleResult(); + + assertThat(result.get("data_type", String.class)).isEqualToIgnoringCase("uuid"); + }); + } + + @Test + public void testUsage() + { + Map idsByTitle = new HashMap<>(); + + doInJPA(em -> { + var articles = List.of( + new UuidAppGeneratedExplicitMappingEntity("one"), + new UuidAppGeneratedExplicitMappingEntity("two"), + new UuidAppGeneratedExplicitMappingEntity("three") + ); + + articles.forEach(em::persist); + em.flush(); + + articles.forEach(article -> idsByTitle.put(article.getTitle(), article.getId())); + }); + + var idOfTwo = Objects.requireNonNull(idsByTitle.get("two"), "id must not be null"); + + doInJPA(em -> { + var article = em.find(UuidAppGeneratedExplicitMappingEntity.class, idOfTwo); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var article = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id = :id", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("id", idOfTwo) + .getSingleResult(); + Assertions.assertEquals("two", article.getTitle()); + }); + + doInJPA(em -> { + var articles = em.createQuery("SELECT a FROM UuidAppGeneratedExplicitMappingEntity a WHERE a.id IN (:ids)", UuidAppGeneratedExplicitMappingEntity.class) + .setParameter("ids", List.copyOf(idsByTitle.values())) + .getResultList(); + Assertions.assertEquals(3, articles.size()); + }); + } + +} diff --git a/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/UuidAppGeneratedExplicitMappingEntity.java b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/UuidAppGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..a9f90f4 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/UuidAppGeneratedExplicitMappingEntity.java @@ -0,0 +1,70 @@ +package org.framefork.typedIds.uuid.hibernate.v70; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.framefork.typedIds.uuid.ObjectUuid; + +import java.util.UUID; + +@Entity +@Table(name = UuidAppGeneratedExplicitMappingEntity.TABLE_NAME) +public class UuidAppGeneratedExplicitMappingEntity +{ + + public static final String TABLE_NAME = "uuid_app_generated_explicit_mapping"; + + @jakarta.persistence.Id + @Column(nullable = false) + private Id id; + + @Column(nullable = false) + private String title; + + public UuidAppGeneratedExplicitMappingEntity(final String title) + { + this.id = Id.random(); + this.title = title; + } + + @SuppressWarnings("NullAway") + protected UuidAppGeneratedExplicitMappingEntity() + { + } + + public Id getId() + { + return id; + } + + public String getTitle() + { + return title; + } + + public static final class Id extends ObjectUuid + { + + private Id(final UUID inner) + { + super(inner); + } + + public static Id random() + { + return ObjectUuid.randomUUID(Id::new); + } + + public static Id from(final String value) + { + return ObjectUuid.fromString(Id::new, value); + } + + public static Id from(final UUID value) + { + return ObjectUuid.fromUuid(Id::new, value); + } + + } + +} From 69b6a1781f95ad08333126afc20159e5c15bf59c Mon Sep 17 00:00:00 2001 From: Filip Prochazka Date: Sun, 27 Jul 2025 17:54:20 +0200 Subject: [PATCH 3/3] add support for Hibernate ORM 7.0 --- README.md | 1 + gradle/libs.versions.toml | 9 +- .../build.gradle.kts | 4 +- .../typed-ids-hibernate-70/build.gradle.kts | 3 +- .../hibernate/ObjectBigIntIdJavaType.java | 14 +- ...ntIdTypeGenerationMetadataContributor.java | 20 ++- .../id/ObjectBigIntIdGeneratorCreator.java | 49 +++++++ .../id/ObjectBigIntIdIdentityGenerator.java | 121 ---------------- .../ObjectBigIntIdSequenceStyleGenerator.java | 19 ++- .../common/hibernate/ImmutableType.java | 133 +++++++++++------- .../hibernate/ParameterizedTypeUtils.java | 22 +++ .../TypeOverrideGeneratorCreationContext.java | 83 +++++++++++ .../uuid/hibernate/ObjectUuidJavaType.java | 13 +- .../ObjectBigIntIdTypePostgreSQLTest.java | 6 +- .../build.gradle.kts | 2 +- 15 files changed, 278 insertions(+), 221 deletions(-) create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdGeneratorCreator.java delete mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java create mode 100644 modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/TypeOverrideGeneratorCreationContext.java diff --git a/README.md b/README.md index 60a5809..a4aafdf 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ For seamless type support in Hibernate ORM, you should pick one of the following | Hibernate Version | Artifact | |-------------------------------|----------------------------------------------------------------------------------------------------------------------| +| 7.0 | [org.framefork:typed-ids-hibernate-70](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-70) | | 6.6, 6.5, 6.4, and 6.3 | [org.framefork:typed-ids-hibernate-63](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-63) | | 6.2 | [org.framefork:typed-ids-hibernate-62](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-62) | | 6.1 | [org.framefork:typed-ids-hibernate-61](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-61) | diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 02e6b6e..8478b44 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,13 +28,16 @@ postgresql = { module = "org.postgresql:postgresql", version = "42.7.4" } mysql = { module = "com.mysql:mysql-connector-j", version = "8.3.0" } logback-classic = { module = "ch.qos.logback:logback-classic", version = "1.5.12" } hibernate-orm-v61 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.1.7.Final" } -hibernate-orm-v62 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.2.36.Final" } +hibernate-orm-v62 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.2.41.Final" } hibernate-orm-v63 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.3.2.Final" } hibernate-orm-v64 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.4.10.Final" } hibernate-orm-v65 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.5.3.Final" } -hibernate-orm-v66 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.6.12.Final" } +hibernate-orm-v66 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.6.22.Final" } +hibernate-orm-v70 = { group = "org.hibernate.orm", name = "hibernate-core", version = "7.0.7.Final" } +hibernate-models-v70 = { group = "org.hibernate.models", name = "hibernate-models", version = "1.0.0" } hypersistence-utils-hibernate61 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-60", version = "3.9.4" } hypersistence-utils-hibernate62 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-62", version = "3.9.4" } -hypersistence-utils-hibernate63 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.9.9" } +hypersistence-utils-hibernate63 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.10.3" } +hypersistence-utils-hibernate70 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-70", version = "3.10.3" } hypersistence-tsid = { module = "io.hypersistence:hypersistence-tsid", version = "2.1.4" } kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.7.0" } diff --git a/modules/typed-ids-hibernate-70-testing/build.gradle.kts b/modules/typed-ids-hibernate-70-testing/build.gradle.kts index 84c13a8..2fcb964 100644 --- a/modules/typed-ids-hibernate-70-testing/build.gradle.kts +++ b/modules/typed-ids-hibernate-70-testing/build.gradle.kts @@ -5,6 +5,6 @@ plugins { dependencies { api(project(":typed-ids-testing")) - api(libs.hibernate.orm.v63) - api(libs.hypersistence.utils.hibernate63) + api(libs.hibernate.orm.v70) + api(libs.hypersistence.utils.hibernate70) } diff --git a/modules/typed-ids-hibernate-70/build.gradle.kts b/modules/typed-ids-hibernate-70/build.gradle.kts index 7da241e..9133562 100644 --- a/modules/typed-ids-hibernate-70/build.gradle.kts +++ b/modules/typed-ids-hibernate-70/build.gradle.kts @@ -4,7 +4,8 @@ plugins { dependencies { api(project(":typed-ids")) - api(libs.hibernate.orm.v63) + api(libs.hibernate.orm.v70) + compileOnly(libs.hibernate.models.v70) // this is really a runtime dependency, but in runtime the version from hibernate is provided compileOnly(libs.jetbrains.annotations) diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java index 08021eb..cceb504 100644 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java @@ -3,6 +3,7 @@ import org.framefork.typedIds.bigint.ObjectBigIntId; import org.framefork.typedIds.bigint.ObjectBigIntIdTypeUtils; import org.framefork.typedIds.common.ReflectionHacks; +import org.framefork.typedIds.common.hibernate.ParameterizedTypeUtils; import org.hibernate.type.SqlTypes; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.BasicJavaType; @@ -25,6 +26,7 @@ import java.util.Objects; import java.util.Properties; +@SuppressWarnings("removal") // DynamicParameterizedType usage public class ObjectBigIntIdJavaType implements BasicJavaType>, DynamicParameterizedType, Serializable { @@ -40,20 +42,10 @@ public ObjectBigIntIdJavaType() this.inner = LongJavaType.INSTANCE; } - @SuppressWarnings("unchecked") @Override public void setParameterValues(final Properties parameters) { - var parameterType = (ParameterType) parameters.get(PARAMETER_TYPE); - if (parameterType != null) { - this.identifierClass = (Class>) parameterType.getReturnedClass(); - - } else { - String entityClass = Objects.requireNonNull(parameters.get(ENTITY), "parameters.get(ENTITY) must not be null").toString(); - String propertyName = Objects.requireNonNull(parameters.get(PROPERTY), "parameters.get(PROPERTY) must not be null").toString(); - - this.identifierClass = ReflectionHacks.getFieldTypeChecked(entityClass, propertyName, ObjectBigIntId.class); - } + this.identifierClass = ParameterizedTypeUtils.getReturnedClass(parameters, ObjectBigIntId.class); if (!ObjectBigIntId.class.isAssignableFrom(identifierClass)) { throw new IllegalArgumentException("Type %s is not a subtype of %s".formatted(identifierClass, ObjectBigIntId.class)); diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java index b935142..f91f71e 100644 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java @@ -2,8 +2,7 @@ import com.google.auto.service.AutoService; import org.framefork.typedIds.bigint.ObjectBigIntId; -import org.framefork.typedIds.bigint.hibernate.id.ObjectBigIntIdIdentityGenerator; -import org.framefork.typedIds.bigint.hibernate.id.ObjectBigIntIdSequenceStyleGenerator; +import org.framefork.typedIds.bigint.hibernate.id.ObjectBigIntIdGeneratorCreator; import org.hibernate.boot.ResourceStreamLocator; import org.hibernate.boot.spi.AdditionalMappingContributions; import org.hibernate.boot.spi.AdditionalMappingContributor; @@ -12,6 +11,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; +@SuppressWarnings("unused") @AutoService(AdditionalMappingContributor.class) public class ObjectBigIntIdTypeGenerationMetadataContributor implements AdditionalMappingContributor { @@ -40,21 +40,17 @@ public void contribute( continue; } - remapIdentifierGeneratorStrategy(simpleValueIdentifier); + remapIdentifierCustomIdGeneratorCreator(simpleValueIdentifier); } } } - private void remapIdentifierGeneratorStrategy(final SimpleValue identifier) + private void remapIdentifierCustomIdGeneratorCreator(final SimpleValue identifier) { - var newIdentifierGeneratorStrategy = switch (identifier.getIdentifierGeneratorStrategy()) { - case "org.hibernate.id.enhanced.SequenceStyleGenerator" -> ObjectBigIntIdSequenceStyleGenerator.class.getName(); - case "org.hibernate.id.IdentityGenerator" -> ObjectBigIntIdIdentityGenerator.class.getName(); - case "identity" -> ObjectBigIntIdIdentityGenerator.class.getName(); - default -> identifier.getIdentifierGeneratorStrategy(); - }; - - identifier.setIdentifierGeneratorStrategy(newIdentifierGeneratorStrategy); + var currentCreator = identifier.getCustomIdGeneratorCreator(); + if (currentCreator != null && !currentCreator.isAssigned()) { + identifier.setCustomIdGeneratorCreator(new ObjectBigIntIdGeneratorCreator(currentCreator)); + } } } diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdGeneratorCreator.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdGeneratorCreator.java new file mode 100644 index 0000000..aa3cc98 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdGeneratorCreator.java @@ -0,0 +1,49 @@ +package org.framefork.typedIds.bigint.hibernate.id; + +import org.hibernate.annotations.IdGeneratorType; +import org.hibernate.boot.model.internal.GeneratorAnnotationHelper; +import org.hibernate.generator.Generator; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.mapping.GeneratorCreator; + +import java.util.Objects; + +public class ObjectBigIntIdGeneratorCreator implements GeneratorCreator +{ + + private final GeneratorCreator originalCreator; + + public ObjectBigIntIdGeneratorCreator(final GeneratorCreator originalCreator) + { + this.originalCreator = Objects.requireNonNull(originalCreator, "originalCreator must not be null"); + } + + @Override + public Generator createGenerator(final GeneratorCreationContext context) + { + var originalGenerator = originalCreator.createGenerator(context); + + if (originalGenerator instanceof SequenceStyleGenerator) { + return prepareForUse(new ObjectBigIntIdSequenceStyleGenerator(), context); + + } else { + // For any other generator type, return the original + // Note: the IdentityGenerator seems to be capable of handling custom types now + return originalGenerator; + } + } + + private static T prepareForUse(final T generator, final GeneratorCreationContext context) + { + GeneratorAnnotationHelper.prepareForUse(generator, null, null, null, null, context); + return generator; + } + + @Override + public boolean isAssigned() + { + return originalCreator.isAssigned(); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java deleted file mode 100644 index 16c45fd..0000000 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdIdentityGenerator.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.framefork.typedIds.bigint.hibernate.id; - -import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; -import org.framefork.typedIds.common.ReflectionHacks; -import org.hibernate.HibernateException; -import org.hibernate.id.IdentityGenerator; -import org.hibernate.id.PostInsertIdentityPersister; -import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; -import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.CustomType; -import org.hibernate.type.Type; -import org.hibernate.type.descriptor.java.spi.JavaTypeBasicAdaptor; -import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.internal.ImmutableNamedBasicTypeImpl; -import org.jspecify.annotations.Nullable; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Objects; -import java.util.Properties; - -/** - * This class handles making Hibernate think it's generating a primitive long type instead of a custom type, - * and once Hibernate internals do their job, it then handles wrapping the generated long value in an ObjectBigIntId. - */ -public class ObjectBigIntIdIdentityGenerator extends IdentityGenerator -{ - - @Nullable - private ObjectBigIntIdType objectBigIntIdType; - @Nullable - private ImmutableNamedBasicTypeImpl primitiveType; - - @Override - public void configure(final Type type, final Properties parameters, final ServiceRegistry serviceRegistry) - { - this.objectBigIntIdType = toObjectBigIntIdType(type); - this.primitiveType = new ImmutableNamedBasicTypeImpl<>( - new JavaTypeBasicAdaptor(Long.class), - toJdbcType(objectBigIntIdType), - "bigint" - ); - } - - private ObjectBigIntIdType toObjectBigIntIdType(final Type type) - { - if (type instanceof CustomType customType) { - var userType = customType.getUserType(); - if (userType instanceof ObjectBigIntIdType objectBigIntIdType) { - return objectBigIntIdType; - } - } - - throw new HibernateException("The given type is expected to be a CustomType wrapper over a %s, but was '%s' instead".formatted(ObjectBigIntIdType.class.getSimpleName(), type)); - } - - private JdbcType toJdbcType(final ObjectBigIntIdType objectBigIntIdType) - { - return objectBigIntIdType.getJdbcType(null); - } - - @Override - public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(final PostInsertIdentityPersister persister) - { - var persisterProxy = proxyPersister(persister); - - var originalDelegate = super.getGeneratedIdentifierDelegate(persisterProxy); - - return proxyIdentifierDelegate(originalDelegate); - } - - private PostInsertIdentityPersister proxyPersister(final PostInsertIdentityPersister persister) - { - return (PostInsertIdentityPersister) Proxy.newProxyInstance( - persister.getClass().getClassLoader(), - ReflectionHacks.getAllInterfaces(persister), - new InvocationHandler() - { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable - { - if ("getIdentifierType".equals(method.getName()) && (args == null || args.length == 0)) { - // Override the getIdentifierType method - // Return a primitive long type instead of the custom type - return Objects.requireNonNull(primitiveType, "primitiveType must not be null"); - } - - // For all other methods, delegate to the original persister - return method.invoke(persister, args); - } - } - ); - } - - private InsertGeneratedIdentifierDelegate proxyIdentifierDelegate(final InsertGeneratedIdentifierDelegate delegate) - { - return (InsertGeneratedIdentifierDelegate) Proxy.newProxyInstance( - delegate.getClass().getClassLoader(), - ReflectionHacks.getAllInterfaces(delegate), - new InvocationHandler() - { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable - { - // For all other methods, delegate to the original persister - Object result = method.invoke(delegate, args); - - if ("performInsert".equals(method.getName())) { - var idType = Objects.requireNonNull(objectBigIntIdType, "objectBigIntIdType must not be null"); - return idType.wrapJdbcValue(result); - - } else { - return result; - } - } - } - ); - } - -} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java index b713e4a..7505890 100644 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java @@ -1,10 +1,12 @@ package org.framefork.typedIds.bigint.hibernate.id; import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.framefork.typedIds.common.hibernate.TypeOverrideGeneratorCreationContext; import org.hibernate.HibernateException; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.GeneratorCreationContext; import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.service.ServiceRegistry; import org.hibernate.type.CustomType; import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.spi.JavaTypeBasicAdaptor; @@ -33,17 +35,24 @@ public Object generate(final SharedSessionContractImplementor session, final Obj } @Override - public void configure(final Type type, final Properties parameters, final ServiceRegistry serviceRegistry) + public void configure(final GeneratorCreationContext creationContext, final Properties parameters) { - this.objectBigIntIdType = toObjectBigIntIdType(type); + this.objectBigIntIdType = toObjectBigIntIdType(creationContext.getType()); var primitiveType = new ImmutableNamedBasicTypeImpl<>( - new JavaTypeBasicAdaptor(Long.class), + new JavaTypeBasicAdaptor<>(Long.class), toJdbcType(objectBigIntIdType), "bigint" ); - super.configure(primitiveType, parameters, serviceRegistry); + super.configure(new TypeOverrideGeneratorCreationContext(creationContext, primitiveType), parameters); + } + + @Override + public void initialize(final SqlStringGenerationContext context) + { + // Initialize the parent to set up the database structure + super.initialize(context); } private ObjectBigIntIdType toObjectBigIntIdType(final Type type) diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java index 1e38a74..3f52826 100644 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java @@ -1,15 +1,15 @@ package org.framefork.typedIds.common.hibernate; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.ArrayHelper; -import org.hibernate.metamodel.model.domain.DomainType; -import org.hibernate.query.BindableType; import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.query.sqm.tree.domain.SqmDomainType; import org.hibernate.type.ForeignKeyDirection; +import org.hibernate.type.MappingContext; import org.hibernate.type.Type; +import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; import org.hibernate.type.descriptor.java.IncomparableComparator; import org.hibernate.type.descriptor.java.JavaType; @@ -35,14 +35,16 @@ import java.util.Objects; import java.util.Properties; +import static jakarta.persistence.metamodel.Type.PersistenceType.BASIC; + @ApiStatus.Internal +@SuppressWarnings("removal") // DynamicParameterizedType usage public abstract class ImmutableType> implements UserType, Type, EnhancedUserType, - BindableType, SqmExpressible, - DomainType, + SqmDomainType, MutabilityPlanExposer, DynamicParameterizedType, TypeConfigurationAware @@ -85,7 +87,7 @@ public JavaTypeType getExpressibleJavaType() } @Override - public Class getBindableJavaType() + public Class getJavaType() { return returnedClass(); } @@ -118,6 +120,24 @@ public String getName() return getClass().getSimpleName(); } + @Override + public String getTypeName() + { + return getName(); + } + + @Override + public SqmDomainType getSqmType() + { + return this; + } + + @Override + public PersistenceType getPersistenceType() + { + return BASIC; + } + @Override public void setParameterValues(final Properties parameters) { @@ -127,45 +147,40 @@ public void setParameterValues(final Properties parameters) } @Nullable - protected T get(final ResultSet rs, final int position, final SharedSessionContractImplementor session, final Object owner) throws SQLException + protected T get(final ResultSet rs, final int position, final WrapperOptions options) throws SQLException { - return getJdbcType(null).getExtractor(getExpressibleJavaType()).extract(rs, position, session); + return getJdbcType(null).getExtractor(getExpressibleJavaType()).extract(rs, position, options); } - protected void set(final PreparedStatement st, @Nullable final T value, final int index, final SharedSessionContractImplementor session) throws SQLException + protected void set(final PreparedStatement st, @Nullable final T value, final int index, final WrapperOptions options) throws SQLException { - getJdbcType(null).getBinder(getExpressibleJavaType()).bind(st, value, index, session); + getJdbcType(null).getBinder(getExpressibleJavaType()).bind(st, value, index, options); } @Nullable @Override - public T nullSafeGet(final ResultSet rs, final int position, final SharedSessionContractImplementor session, final Object owner) throws SQLException + public T nullSafeGet(final ResultSet rs, final int position, final WrapperOptions options) throws SQLException { - return get(rs, position, session, owner); + return get(rs, position, options); } @Override - public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final SharedSessionContractImplementor session) throws SQLException + public void nullSafeSet(final PreparedStatement st, @Nullable final T value, final int index, final WrapperOptions options) throws SQLException { - set(st, returnedClass().cast(value), index, session); + set(st, returnedClass().cast(value), index, options); } @Override - public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final boolean[] settable, final SharedSessionContractImplementor session) throws SQLException + public void nullSafeSet(final PreparedStatement st, @Nullable final Object value, final int index, final boolean[] settable, final SharedSessionContractImplementor session) throws SQLException { set(st, returnedClass().cast(value), index, session); } + @SuppressWarnings("removal") @Override - public boolean equals(final Object x, final Object y) + public void nullSafeSet(final PreparedStatement st, @Nullable final Object value, final int index, final SharedSessionContractImplementor session) throws SQLException { - return Objects.equals(x, y); - } - - @Override - public int hashCode(final Object x) - { - return x.hashCode(); + set(st, returnedClass().cast(value), index, session); } @Override @@ -210,52 +225,51 @@ public boolean isComponentType() return false; } - @SuppressWarnings("deprecation") @Override - public int getColumnSpan(final Mapping mapping) + public int getColumnSpan(final MappingContext mappingContext) { return 1; } @Override - public boolean isSame(final Object x, final Object y) + public boolean isSame(@Nullable final Object x, @Nullable final Object y) { - return equals(x, y); + return Objects.equals(x, y); } @Override - public boolean isEqual(final Object x, final Object y) + public boolean isEqual(@Nullable final Object x, @Nullable final Object y) { - return equals(x, y); + return Objects.equals(x, y); } @Override - public boolean isEqual(final Object x, final Object y, final SessionFactoryImplementor factory) + public boolean isEqual(@Nullable final Object x, @Nullable final Object y, final SessionFactoryImplementor factory) { - return equals(x, y); + return Objects.equals(x, y); } @Override public int getHashCode(final Object x) { - return hashCode(x); + return Objects.hashCode(x); } @Override public int getHashCode(final Object x, final SessionFactoryImplementor factory) { - return hashCode(x); + return Objects.hashCode(x); } @Override - public int compare(final Object x, final Object y, final SessionFactoryImplementor sessionFactoryImplementor) + public int compare(@Nullable final Object x, @Nullable final Object y, final SessionFactoryImplementor sessionFactoryImplementor) { return compare(x, y); } @SuppressWarnings("unchecked") @Override - public int compare(final Object x, final Object y) + public int compare(@Nullable final Object x, @Nullable final Object y) { if (x instanceof Comparable xComparable && y instanceof Comparable yComparable) { return ((Comparator) getComparator()).compare(xComparable, yComparable); @@ -272,66 +286,73 @@ public Comparator getComparator() } @Override - public final boolean isDirty(final Object old, final Object current, final SharedSessionContractImplementor session) + public final boolean isDirty(@Nullable final Object old, @Nullable final Object current, final SharedSessionContractImplementor session) { return isDirty(old, current); } @Override - public final boolean isDirty(final Object old, final Object current, final boolean[] checkable, final SharedSessionContractImplementor session) + public final boolean isDirty(@Nullable final Object old, @Nullable final Object current, final boolean[] checkable, final SharedSessionContractImplementor session) { return checkable[0] && isDirty(old, current); } - protected final boolean isDirty(final Object old, final Object current) + protected final boolean isDirty(@Nullable final Object old, @Nullable final Object current) { return !isSame(old, current); } @Override - public boolean isModified(final Object dbState, final Object currentState, final boolean[] checkable, final SharedSessionContractImplementor session) + public boolean isModified(@Nullable final Object dbState, @Nullable final Object currentState, final boolean[] checkable, final SharedSessionContractImplementor session) { return isDirty(dbState, currentState); } @SuppressWarnings("unchecked") + @Nullable @Override - public T deepCopy(final Object value) + public T deepCopy(@Nullable final Object value) { return (T) value; } + @Nullable @Override - public Object deepCopy(final Object value, final SessionFactoryImplementor factory) + public Object deepCopy(@Nullable final Object value, final SessionFactoryImplementor factory) { return deepCopy(value); } + @Nullable @Override - public Serializable disassemble(final Object o) + public Serializable disassemble(@Nullable final Object o) { return (Serializable) o; } + @Nullable @Override - public Serializable disassemble(final Object value, final SharedSessionContractImplementor session, final Object owner) + public Serializable disassemble(@Nullable final Object value, @Nullable final SharedSessionContractImplementor session, @Nullable final Object owner) { return disassemble(value); } @SuppressWarnings("unchecked") + @Nullable @Override - public T assemble(final Serializable cached, final Object owner) + public T assemble(@Nullable final Serializable cached, final Object owner) { return (T) cached; } + @Nullable @Override - public Object assemble(final Serializable cached, final SharedSessionContractImplementor session, final Object owner) + public Object assemble(@Nullable final Serializable cached, final SharedSessionContractImplementor session, final Object owner) { return assemble(cached, session); } + @SuppressWarnings("removal") @Override public void beforeAssemble(final Serializable cached, final SharedSessionContractImplementor session) { @@ -339,29 +360,38 @@ public void beforeAssemble(final Serializable cached, final SharedSessionContrac } @SuppressWarnings("unchecked") + @Nullable @Override - public T replace(final Object o, final Object target, final Object owner) + public T replace(@Nullable final Object o, @Nullable final Object target, @Nullable final Object owner) { return (T) o; } @SuppressWarnings("rawtypes") + @Nullable @Override - public Object replace(final Object original, final Object target, final SharedSessionContractImplementor session, final Object owner, final Map copyCache) + public Object replace(@Nullable final Object original, @Nullable final Object target, final SharedSessionContractImplementor session, @Nullable final Object owner, final Map copyCache) { return replace(original, target, owner); } @SuppressWarnings("rawtypes") + @Nullable @Override - public Object replace(final Object original, final Object target, final SharedSessionContractImplementor session, final Object owner, final Map copyCache, final ForeignKeyDirection foreignKeyDirection) + public Object replace( + @Nullable final Object original, + @Nullable final Object target, + final SharedSessionContractImplementor session, + @Nullable final Object owner, + final Map copyCache, + final ForeignKeyDirection foreignKeyDirection + ) { return replace(original, target, owner); } - @SuppressWarnings("deprecation") @Override - public boolean[] toColumnNullness(final Object value, final Mapping mapping) + public boolean[] toColumnNullness(@Nullable final Object value, final MappingContext mappingContext) { return value == null ? ArrayHelper.FALSE : ArrayHelper.TRUE; } @@ -384,9 +414,8 @@ public int getDefaultSqlScale(final Dialect dialect, final JdbcType jdbcType) return javaType.getDefaultSqlScale(dialect, jdbcType); } - @SuppressWarnings("deprecation") @Override - public int[] getSqlTypeCodes(final Mapping mapping) + public int[] getSqlTypeCodes(final MappingContext mappingContext) { return new int[]{getSqlType()}; } diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java index 23f428e..bc6551c 100644 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java @@ -1,12 +1,15 @@ package org.framefork.typedIds.common.hibernate; +import org.framefork.typedIds.common.ReflectionHacks; import org.hibernate.usertype.DynamicParameterizedType; import org.jetbrains.annotations.ApiStatus; import java.lang.annotation.Annotation; +import java.util.Objects; import java.util.Properties; @ApiStatus.Internal +@SuppressWarnings("removal") public final class ParameterizedTypeUtils { @@ -21,6 +24,25 @@ public static Properties forClass(final Class implClass) return parameters; } + @SuppressWarnings("unchecked") + public static Class getReturnedClass(final Properties parameters, final Class expectedType) + { + var parameterType = (DynamicParameterizedType.ParameterType) parameters.get(DynamicParameterizedType.PARAMETER_TYPE); + if (parameterType != null) { + Class actualType = parameterType.getReturnedClass(); + if (!expectedType.isAssignableFrom(actualType)) { + throw new IllegalArgumentException("Expected %s but found %s".formatted(expectedType, actualType)); + } + return (Class) actualType; + + } else { + String entityClass = Objects.requireNonNull(parameters.get(DynamicParameterizedType.ENTITY), "parameters.get(ENTITY) must not be null").toString(); + String propertyName = Objects.requireNonNull(parameters.get(DynamicParameterizedType.PROPERTY), "parameters.get(PROPERTY) must not be null").toString(); + + return ReflectionHacks.getFieldTypeChecked(entityClass, propertyName, expectedType); + } + } + public static final class ParameterizedParameterType implements DynamicParameterizedType.ParameterType { diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/TypeOverrideGeneratorCreationContext.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/TypeOverrideGeneratorCreationContext.java new file mode 100644 index 0000000..0bf8fa7 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/TypeOverrideGeneratorCreationContext.java @@ -0,0 +1,83 @@ +package org.framefork.typedIds.common.hibernate; + +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.Type; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public class TypeOverrideGeneratorCreationContext implements GeneratorCreationContext +{ + + private final GeneratorCreationContext delegate; + private final Type typeOverride; + + public TypeOverrideGeneratorCreationContext( + final GeneratorCreationContext delegate, + final Type typeOverride + ) + { + this.delegate = delegate; + this.typeOverride = typeOverride; + } + + @Override + public SqlStringGenerationContext getSqlStringGenerationContext() + { + return delegate.getSqlStringGenerationContext(); + } + + @Override + public Type getType() + { + return typeOverride; + } + + @Override + public Property getProperty() + { + return delegate.getProperty(); + } + + @Override + public RootClass getRootClass() + { + return delegate.getRootClass(); + } + + @Override + public PersistentClass getPersistentClass() + { + return delegate.getPersistentClass(); + } + + @Override + public String getDefaultSchema() + { + return delegate.getDefaultSchema(); + } + + @Override + public String getDefaultCatalog() + { + return delegate.getDefaultCatalog(); + } + + @Override + public ServiceRegistry getServiceRegistry() + { + return delegate.getServiceRegistry(); + } + + @Override + public Database getDatabase() + { + return delegate.getDatabase(); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java index 37f780f..7b44967 100644 --- a/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java @@ -1,6 +1,7 @@ package org.framefork.typedIds.uuid.hibernate; import org.framefork.typedIds.common.ReflectionHacks; +import org.framefork.typedIds.common.hibernate.ParameterizedTypeUtils; import org.framefork.typedIds.uuid.ObjectUuid; import org.framefork.typedIds.uuid.ObjectUuidTypeUtils; import org.hibernate.dialect.Dialect; @@ -29,6 +30,7 @@ import java.util.Properties; import java.util.UUID; +@SuppressWarnings("removal") // DynamicParameterizedType usage public class ObjectUuidJavaType implements BasicJavaType>, DynamicParameterizedType, Serializable { @@ -50,16 +52,7 @@ public ObjectUuidJavaType() @Override public void setParameterValues(final Properties parameters) { - var parameterType = (ParameterType) parameters.get(PARAMETER_TYPE); - if (parameterType != null) { - this.identifierClass = (Class>) parameterType.getReturnedClass(); - - } else { - String entityClass = Objects.requireNonNull(parameters.get(ENTITY), "parameters.get(ENTITY) must not be null").toString(); - String propertyName = Objects.requireNonNull(parameters.get(PROPERTY), "parameters.get(PROPERTY) must not be null").toString(); - - this.identifierClass = ReflectionHacks.getFieldTypeChecked(entityClass, propertyName, ObjectUuid.class); - } + this.identifierClass = ParameterizedTypeUtils.getReturnedClass(parameters, ObjectUuid.class); if (!ObjectUuid.class.isAssignableFrom(identifierClass)) { throw new IllegalArgumentException("Type %s is not a subtype of %s".formatted(identifierClass, ObjectUuid.class)); diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java index 86769d2..3a10f6a 100644 --- a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java @@ -45,9 +45,9 @@ public void testSchema() var table3 = getIdColumnInfo(em, BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table3.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); - assertThat(table3.get("column_default", String.class)).startsWith("nextval('"); - assertThat(table3.get("is_identity", String.class)).isEqualTo("NO"); - assertThat(table3.get("identity_generation", String.class)).isNull(); + assertThat(table3.get("column_default", String.class)).isNull(); + assertThat(table3.get("is_identity", String.class)).isEqualTo("YES"); + assertThat(table3.get("identity_generation", String.class)).isEqualToIgnoringCase("BY DEFAULT"); var table4 = getIdColumnInfo(em, BigIntDbSequenceGeneratedExplicitMappingEntity.TABLE_NAME); assertThat(table4.get("data_type", String.class)).isEqualToIgnoringCase("bigint"); diff --git a/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts b/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts index 02f25d6..9d1b4a5 100644 --- a/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts +++ b/testing/testing-typed-ids-hibernate-70-indexed/build.gradle.kts @@ -12,6 +12,6 @@ dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher") constraints { - implementation(libs.hibernate.orm.v66) + implementation(libs.hibernate.orm.v70) } }