diff --git a/README.md b/README.md index 45a563b..b80f351 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, 7.1 | [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 03dd64f..7237a49 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,6 +35,7 @@ hibernate-orm-v65 = { group = "org.hibernate.orm", name = "hibernate-core", vers hibernate-orm-v66 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.6.31.Final" } hibernate-orm-v70 = { group = "org.hibernate.orm", name = "hibernate-core", version = "7.0.10.Final" } hibernate-orm-v71 = { group = "org.hibernate.orm", name = "hibernate-core", version = "7.1.2.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.11.0" } diff --git a/modules/typed-ids-hibernate-70-testing/build.gradle.kts b/modules/typed-ids-hibernate-70-testing/build.gradle.kts index 2fcb964..90577d7 100644 --- a/modules/typed-ids-hibernate-70-testing/build.gradle.kts +++ b/modules/typed-ids-hibernate-70-testing/build.gradle.kts @@ -4,6 +4,7 @@ plugins { dependencies { api(project(":typed-ids-testing")) + api(testFixtures(project(":typed-ids-hibernate-70"))) 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 new file mode 100644 index 0000000..ddc81bf --- /dev/null +++ b/modules/typed-ids-hibernate-70/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("framefork.java-public") + id("java-test-fixtures") +} + +dependencies { + api(project(":typed-ids")) + 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) + + 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..cceb504 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdJavaType.java @@ -0,0 +1,163 @@ +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.framefork.typedIds.common.hibernate.ParameterizedTypeUtils; +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; + +@SuppressWarnings("removal") // DynamicParameterizedType usage +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; + } + + @Override + public void setParameterValues(final Properties parameters) + { + 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)); + } + + 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..f91f71e --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeGenerationMetadataContributor.java @@ -0,0 +1,56 @@ +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.ObjectBigIntIdGeneratorCreator; +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; + +@SuppressWarnings("unused") +@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; + } + + remapIdentifierCustomIdGeneratorCreator(simpleValueIdentifier); + } + } + } + + private void remapIdentifierCustomIdGeneratorCreator(final SimpleValue identifier) + { + 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/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/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/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..7505890 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/bigint/hibernate/id/ObjectBigIntIdSequenceStyleGenerator.java @@ -0,0 +1,75 @@ +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.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 GeneratorCreationContext creationContext, final Properties parameters) + { + this.objectBigIntIdType = toObjectBigIntIdType(creationContext.getType()); + + var primitiveType = new ImmutableNamedBasicTypeImpl<>( + new JavaTypeBasicAdaptor<>(Long.class), + toJdbcType(objectBigIntIdType), + "bigint" + ); + + 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) + { + 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..3f52826 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ImmutableType.java @@ -0,0 +1,455 @@ +package org.framefork.typedIds.common.hibernate; + +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.collections.ArrayHelper; +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; +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; + +import static jakarta.persistence.metamodel.Type.PersistenceType.BASIC; + +@ApiStatus.Internal +@SuppressWarnings("removal") // DynamicParameterizedType usage +public abstract class ImmutableType> implements + UserType, + Type, + EnhancedUserType, + SqmExpressible, + SqmDomainType, + 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 getJavaType() + { + 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 String getTypeName() + { + return getName(); + } + + @Override + public SqmDomainType getSqmType() + { + return this; + } + + @Override + public PersistenceType getPersistenceType() + { + return BASIC; + } + + @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 WrapperOptions options) throws SQLException + { + return getJdbcType(null).getExtractor(getExpressibleJavaType()).extract(rs, position, options); + } + + 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, options); + } + + @Nullable + @Override + public T nullSafeGet(final ResultSet rs, final int position, final WrapperOptions options) throws SQLException + { + return get(rs, position, options); + } + + @Override + public void nullSafeSet(final PreparedStatement st, @Nullable final T value, final int index, final WrapperOptions options) throws SQLException + { + set(st, returnedClass().cast(value), index, options); + } + + @Override + 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 void nullSafeSet(final PreparedStatement st, @Nullable final Object value, final int index, final SharedSessionContractImplementor session) throws SQLException + { + set(st, returnedClass().cast(value), index, session); + } + + @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; + } + + @Override + public int getColumnSpan(final MappingContext mappingContext) + { + return 1; + } + + @Override + public boolean isSame(@Nullable final Object x, @Nullable final Object y) + { + return Objects.equals(x, y); + } + + @Override + public boolean isEqual(@Nullable final Object x, @Nullable final Object y) + { + return Objects.equals(x, y); + } + + @Override + public boolean isEqual(@Nullable final Object x, @Nullable final Object y, final SessionFactoryImplementor factory) + { + return Objects.equals(x, y); + } + + @Override + public int getHashCode(final Object x) + { + return Objects.hashCode(x); + } + + @Override + public int getHashCode(final Object x, final SessionFactoryImplementor factory) + { + return Objects.hashCode(x); + } + + @Override + public int compare(@Nullable final Object x, @Nullable final Object y, final SessionFactoryImplementor sessionFactoryImplementor) + { + return compare(x, y); + } + + @SuppressWarnings("unchecked") + @Override + 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); + } + + 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(@Nullable final Object old, @Nullable final Object current, final SharedSessionContractImplementor session) + { + return isDirty(old, current); + } + + @Override + 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(@Nullable final Object old, @Nullable final Object current) + { + return !isSame(old, current); + } + + @Override + 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(@Nullable final Object value) + { + return (T) value; + } + + @Nullable + @Override + public Object deepCopy(@Nullable final Object value, final SessionFactoryImplementor factory) + { + return deepCopy(value); + } + + @Nullable + @Override + public Serializable disassemble(@Nullable final Object o) + { + return (Serializable) o; + } + + @Nullable + @Override + 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(@Nullable final Serializable cached, final Object owner) + { + return (T) cached; + } + + @Nullable + @Override + 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) + { + + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + 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(@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( + @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); + } + + @Override + public boolean[] toColumnNullness(@Nullable final Object value, final MappingContext mappingContext) + { + 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); + } + + @Override + public int[] getSqlTypeCodes(final MappingContext mappingContext) + { + 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..bc6551c --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/common/hibernate/ParameterizedTypeUtils.java @@ -0,0 +1,106 @@ +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 +{ + + private ParameterizedTypeUtils() + { + } + + public static Properties forClass(final Class implClass) + { + var parameters = new Properties(); + parameters.put(DynamicParameterizedType.PARAMETER_TYPE, new ParameterizedParameterType(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 + { + + 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/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 new file mode 100644 index 0000000..7b44967 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/main/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidJavaType.java @@ -0,0 +1,180 @@ +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; +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; + +@SuppressWarnings("removal") // DynamicParameterizedType usage +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) + { + 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)); + } + + 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/AdvancedGenerationStrategyPostgreSQLTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedGenerationStrategyPostgreSQLTest.java new file mode 100644 index 0000000..ecb53b5 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedGenerationStrategyPostgreSQLTest.java @@ -0,0 +1,112 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntPooledLoOptimizerEntity; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + +final class AdvancedGenerationStrategyPostgreSQLTest extends AbstractPostgreSQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntPooledLoOptimizerEntity.class, + BigIntDbSequenceGeneratedExplicitMappingEntity.class, + }; + } + + @Override + protected void additionalProperties(Properties properties) + { + // Enable JDBC batching for testing batching behavior + properties.setProperty("hibernate.jdbc.batch_size", "20"); + properties.setProperty("hibernate.order_inserts", "true"); + properties.setProperty("hibernate.order_updates", "true"); + } + + @Test + public void testPooledLoOptimizerPerformance() + { + // This test validates that the pooled-lo optimizer reduces database calls + doInJPA(em -> { + // Create 21 entities (more than allocation size of 20) + List entities = new ArrayList<>(); + for (int i = 1; i <= 21; i++) { + entities.add(new BigIntPooledLoOptimizerEntity("data-" + i)); + } + + // Persist all entities + entities.forEach(em::persist); + em.flush(); + + // Verify all entities have IDs assigned + entities.forEach(entity -> { + Assertions.assertNotNull(entity.getId(), "ID must not be null after persist"); + }); + + // With pooled-lo optimizer and allocationSize=20: + // - First 20 entities should get IDs from first sequence call + // - 21st entity should get ID from second sequence call + List idValues = entities.stream() + .map(BigIntPooledLoOptimizerEntity::getId) + .filter(Objects::nonNull) + .map(ObjectBigIntId::toLong) + .sorted() + .toList(); + + assertThat(idValues.get(0)).as("First ID should be 1").isEqualTo(1L); + assertThat(idValues.get(20)).as("21st ID should be 21").isEqualTo(21L); + }); + } + + @Test + public void testSequenceStrategyBasicFunctionality() + { + doInJPA(em -> { + // Create multiple entities with SEQUENCE generation + List entities = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + entities.add(new BigIntDbSequenceGeneratedExplicitMappingEntity("sequence-" + i)); + } + + // Verify IDs are null before persist + entities.forEach(entity -> { + Assertions.assertNull(entity.getId(), "ID should be null before persist"); + }); + + // Persist all entities + entities.forEach(em::persist); + em.flush(); + + // Verify all entities have IDs assigned from sequence + entities.forEach(entity -> { + Assertions.assertNotNull(entity.getId(), "ID must not be null after persist"); + }); + + // All IDs should be unique and positive + List idValues = entities.stream() + .map(BigIntDbSequenceGeneratedExplicitMappingEntity::getId) + .filter(Objects::nonNull) + .map(ObjectBigIntId::toLong) + .toList(); + + assertThat(idValues).as("All IDs should be unique") + .doesNotHaveDuplicates(); + + assertThat(idValues).as("All IDs should be positive") + .allMatch(id -> id > 0); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedGenerationStrategyTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedGenerationStrategyTest.java new file mode 100644 index 0000000..a60799a --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedGenerationStrategyTest.java @@ -0,0 +1,165 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntNullIdEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntPooledLoOptimizerEntity; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.hibernate.id.IdentifierGenerationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +final class AdvancedGenerationStrategyTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntPooledLoOptimizerEntity.class, + BigIntDbIdentityGeneratedExplicitMappingEntity.class, + BigIntNullIdEntity.class, + }; + } + + @Override + protected void additionalProperties(Properties properties) + { + // Enable JDBC batching for testing batching behavior + properties.setProperty("hibernate.jdbc.batch_size", "20"); + properties.setProperty("hibernate.order_inserts", "true"); + properties.setProperty("hibernate.order_updates", "true"); + } + + @Test + public void testPooledLoOptimizerPerformance() + { + doInJPA(em -> { + // Create 21 entities (more than allocation size of 20) + List entities = new ArrayList<>(); + for (int i = 1; i <= 21; i++) { + entities.add(new BigIntPooledLoOptimizerEntity("data-" + i)); + } + + // Persist all entities + entities.forEach(em::persist); + em.flush(); + + // Verify all entities have IDs assigned + entities.forEach(entity -> { + Assertions.assertNotNull(entity.getId(), "ID must not be null after persist"); + }); + + // With pooled-lo optimizer and allocationSize=20: + // - First 20 entities should get IDs 1-20 from first sequence call + // - 21st entity should get ID 21 from second sequence call + // Validate that IDs are sequential and within expected ranges + List idValues = entities.stream() + .map(BigIntPooledLoOptimizerEntity::getId) + .filter(Objects::nonNull) + .map(ObjectBigIntId::toLong) + .sorted() + .toList(); + + assertThat(idValues.get(0)).as("First ID should be 1").isEqualTo(1L); + assertThat(idValues.get(20)).as("21st ID should be 21").isEqualTo(21L); + }); + } + + + @Test + public void testIdentityStrategyBasicFunctionality() + { + doInJPA(em -> { + // Create multiple entities with IDENTITY generation + List entities = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + entities.add(new BigIntDbIdentityGeneratedExplicitMappingEntity("identity-" + i)); + } + + // Verify IDs are null before persist + entities.forEach(entity -> { + Assertions.assertNull(entity.getId(), "ID should be null before persist"); + }); + + // Persist all entities + entities.forEach(em::persist); + em.flush(); + + // Verify all entities have IDs assigned by database + entities.forEach(entity -> { + Assertions.assertNotNull(entity.getId(), "ID must not be null after persist"); + }); + + // IDs should be sequential (MySQL AUTO_INCREMENT behavior) + List idValues = entities.stream() + .map(BigIntDbIdentityGeneratedExplicitMappingEntity::getId) + .filter(Objects::nonNull) + .map(ObjectBigIntId::toLong) + .sorted() + .toList(); + + // Verify IDs are sequential starting from 1 + for (int i = 0; i < idValues.size(); i++) { + assertThat(idValues.get(i)).as("ID should be sequential") + .isEqualTo((long) (i + 1)); + } + }); + } + + + @Test + public void testNullIdRejectionForAssignedStrategy() + { + // Test that attempting to persist an entity with null ID throws appropriate exception + var exception = assertThrows( + IdentifierGenerationException.class, + () -> doInJPA(em -> { + var entity = new BigIntNullIdEntity("test-data"); + // ID is null by default + Assertions.assertNull(entity.getId()); + + em.persist(entity); + em.flush(); // This should trigger the exception + }) + ); + + // Verify the exception message indicates the problem + assertThat(exception.getMessage()).containsAnyOf("id", "identifier", "manually assigned"); + } + + @Test + public void testAssignedStrategyWithValidId() + { + // Test successful persistence when ID is properly assigned + BigIntNullIdEntity.Id assignedId = BigIntNullIdEntity.Id.random(); + + doInJPA(em -> { + var entity = new BigIntNullIdEntity("test-data"); + entity.setId(assignedId); + + em.persist(entity); + em.flush(); + + // Verify ID was preserved + Assertions.assertEquals(assignedId, entity.getId()); + }); + + // Verify entity can be retrieved by assigned ID + doInJPA(em -> { + var retrieved = em.find(BigIntNullIdEntity.class, assignedId); + Assertions.assertNotNull(retrieved); + Assertions.assertEquals("test-data", retrieved.getData()); + Assertions.assertEquals(assignedId, retrieved.getId()); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedHibernateApiTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedHibernateApiTest.java new file mode 100644 index 0000000..b1bc346 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/AdvancedHibernateApiTest.java @@ -0,0 +1,260 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +final class AdvancedHibernateApiTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntDbIdentityGeneratedExplicitMappingEntity.class, + }; + } + + @Test + public void testSessionGetReferenceProxyCreation() + { + // First, persist an entity and get its ID + var savedId = doInJPA(em -> { + var entity = new BigIntDbIdentityGeneratedExplicitMappingEntity("proxy-test-data"); + em.persist(entity); + em.flush(); + return entity.getId(); + }); + + // Test getReference proxy creation + doInJPA(em -> { + Session session = em.unwrap(Session.class); + + // Get reference should return a proxy without hitting the database + BigIntDbIdentityGeneratedExplicitMappingEntity proxy = + session.getReference(BigIntDbIdentityGeneratedExplicitMappingEntity.class, savedId); + + // Proxy should not be null + Assertions.assertNotNull(proxy); + + // Proxy should not be initialized yet + assertThat(Hibernate.isInitialized(proxy)) + .as("Proxy should not be initialized immediately after getReference()") + .isFalse(); + + // Accessing ID should not trigger initialization (ID is always available) + BigIntDbIdentityGeneratedExplicitMappingEntity.Id proxyId = proxy.getId(); + assertThat(proxyId).isEqualTo(savedId); + assertThat(Hibernate.isInitialized(proxy)) + .as("Accessing ID should not initialize proxy") + .isFalse(); + + // Accessing non-ID property should trigger lazy loading + String data = proxy.getTitle(); + assertThat(data).isEqualTo("proxy-test-data"); + assertThat(Hibernate.isInitialized(proxy)) + .as("Accessing non-ID property should initialize proxy") + .isTrue(); + }); + } + + @Test + public void testSessionByMultipleIdsBatchLoading() + { + // Persist multiple entities + var savedIds = doInJPA(em -> { + List ids = new ArrayList<>(); + + for (int i = 1; i <= 5; i++) { + var entity = new BigIntDbIdentityGeneratedExplicitMappingEntity("batch-data-" + i); + em.persist(entity); + em.flush(); + ids.add(entity.getId()); + } + + return ids; + }); + + // Test batch loading with byMultipleIds + doInJPA(em -> { + Session session = em.unwrap(Session.class); + + // Load multiple entities by their IDs in a single operation + List entities = + session.byMultipleIds(BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .multiLoad(savedIds); + + // Should return all 5 entities + assertThat(entities).hasSize(5); + + // All entities should be non-null and have correct IDs + for (int i = 0; i < entities.size(); i++) { + var entity = entities.get(i); + Assertions.assertNotNull(entity); + assertThat(savedIds).contains(entity.getId()); + assertThat(entity.getTitle()).startsWith("batch-data-"); + } + + // Verify that all saved IDs are represented + List retrievedIds = + entities.stream().map(BigIntDbIdentityGeneratedExplicitMappingEntity::getId).toList(); + + assertThat(retrievedIds).containsExactlyInAnyOrderElementsOf(savedIds); + }); + } + + @Test + public void testSessionByMultipleIdsWithPartialResults() + { + // Persist one entity + var existingId = doInJPA(em -> { + var entity = new BigIntDbIdentityGeneratedExplicitMappingEntity("existing-entity"); + em.persist(entity); + em.flush(); + return entity.getId(); + }); + + // Create a non-existent ID (but valid type) + var nonExistentId = BigIntDbIdentityGeneratedExplicitMappingEntity.Id.from(999999L); + + // Test batch loading with mix of existing and non-existing IDs + doInJPA(em -> { + Session session = em.unwrap(Session.class); + + var mixedIds = List.of(existingId, nonExistentId); + + var entities = + session.byMultipleIds(BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .multiLoad(mixedIds); + + // Should return only the existing entity, with null for non-existent + assertThat(entities).hasSize(2); + + // One should be the actual entity, one should be null + long nonNullCount = entities.stream().mapToLong(e -> e != null ? 1 : 0).sum(); + assertThat(nonNullCount).isEqualTo(1); + + // The non-null entity should have the existing ID + var existingEntity = entities.stream().filter(e -> e != null).findFirst().orElse(null); + Assertions.assertNotNull(existingEntity); + assertThat(existingEntity.getId()).isEqualTo(existingId); + assertThat(existingEntity.getTitle()).isEqualTo("existing-entity"); + }); + } + + @Test + public void testNativeSqlParameterBinding() + { + // Persist an entity + var savedId = doInJPA(em -> { + var entity = new BigIntDbIdentityGeneratedExplicitMappingEntity("native-sql-test"); + em.persist(entity); + em.flush(); + return entity.getId(); + }); + + // Test native SQL query with custom ID parameter binding + doInJPA(em -> { + Session session = em.unwrap(Session.class); + + // Execute native SQL with custom ID as parameter + String sql = "SELECT * FROM " + BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME + " WHERE id = :id"; + + BigIntDbIdentityGeneratedExplicitMappingEntity result = + session.createNativeQuery(sql, BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameter("id", savedId.toLong()) + .getSingleResult(); + + // Verify the result + Assertions.assertNotNull(result); + assertThat(result.getId()).isEqualTo(savedId); + assertThat(result.getTitle()).isEqualTo("native-sql-test"); + }); + } + + @Test + public void testNativeSqlParameterBindingWithMultipleParams() + { + // Persist multiple entities + var savedIds = doInJPA(em -> { + List ids = new ArrayList<>(); + + var entity1 = new BigIntDbIdentityGeneratedExplicitMappingEntity("sql-test-1"); + var entity2 = new BigIntDbIdentityGeneratedExplicitMappingEntity("sql-test-2"); + + em.persist(entity1); + em.persist(entity2); + em.flush(); + + ids.add(entity1.getId()); + ids.add(entity2.getId()); + + return ids; + }); + + // Test native SQL with IN clause using custom IDs + doInJPA(em -> { + Session session = em.unwrap(Session.class); + + // Note: This tests the UserType's ability to handle collection parameter binding + String sql = "SELECT * FROM " + BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME + + " WHERE id IN (:ids) ORDER BY title"; + + // Convert custom IDs to long values for native SQL + List idValues = savedIds.stream().map(id -> id.toLong()).toList(); + + @SuppressWarnings("unchecked") + List results = + session.createNativeQuery(sql, BigIntDbIdentityGeneratedExplicitMappingEntity.class) + .setParameterList("ids", idValues) + .getResultList(); + + // Verify results + assertThat(results).hasSize(2); + assertThat(results.get(0).getTitle()).isEqualTo("sql-test-1"); + assertThat(results.get(1).getTitle()).isEqualTo("sql-test-2"); + + List resultIds = + results.stream().map(BigIntDbIdentityGeneratedExplicitMappingEntity::getId).toList(); + + assertThat(resultIds).containsExactlyInAnyOrderElementsOf(savedIds); + }); + } + + @Test + public void testNativeSqlWithCustomIdComparison() + { + // Persist an entity + var savedId = doInJPA(em -> { + var entity = new BigIntDbIdentityGeneratedExplicitMappingEntity("comparison-test"); + em.persist(entity); + em.flush(); + return entity.getId(); + }); + + // Test native SQL with comparison operators + doInJPA(em -> { + Session session = em.unwrap(Session.class); + + // Test greater than comparison + String sql = "SELECT COUNT(*) FROM " + BigIntDbIdentityGeneratedExplicitMappingEntity.TABLE_NAME + + " WHERE id >= :minId"; + + var count = session.createNativeQuery(sql, Long.class) + .setParameter("minId", savedId.toLong()) + .getSingleResult(); + + // Should find at least the entity we just created + assertThat(count.longValue()).isGreaterThanOrEqualTo(1L); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntComplexRelationshipMappingTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntComplexRelationshipMappingTest.java new file mode 100644 index 0000000..c9205ad --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntComplexRelationshipMappingTest.java @@ -0,0 +1,233 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.hibernate.composite.BigIntCompositeEmbeddedIdEntity; +import org.framefork.typedIds.bigint.hibernate.composite.BigIntCompositeIdClassEntity; +import org.framefork.typedIds.bigint.hibernate.composite.BigIntCompositePK; +import org.framefork.typedIds.bigint.hibernate.composite.BigIntIdClassPK; +import org.framefork.typedIds.bigint.hibernate.relationships.BigIntChildEntity; +import org.framefork.typedIds.bigint.hibernate.relationships.BigIntParentEntity; +import org.framefork.typedIds.bigint.hibernate.relationships.BigIntPersonDetailsEntity; +import org.framefork.typedIds.bigint.hibernate.relationships.BigIntPersonEntity; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.hibernate.Hibernate; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +final class BigIntComplexRelationshipMappingTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + BigIntParentEntity.class, + BigIntChildEntity.class, + BigIntPersonEntity.class, + BigIntPersonDetailsEntity.class, + BigIntCompositeEmbeddedIdEntity.class, + BigIntCompositeIdClassEntity.class, + }; + } + + @Test + public void testManyToOneRelationshipWithCustomIdAsForeignKey() + { + // Create and persist parent entity + doInJPA(em -> { + var parent = new BigIntParentEntity("Test Parent"); + em.persist(parent); + em.flush(); + + Assertions.assertNotNull(parent.getId()); + return parent.getId(); + }); + + // Store parent ID for later use + var parentId = doInJPA(em -> { + var parent = em.createQuery("SELECT p FROM BigIntParentEntity p WHERE p.name = :name", BigIntParentEntity.class) + .setParameter("name", "Test Parent") + .getSingleResult(); + return parent.getId(); + }); + + // Create child entity with reference to parent + doInJPA(em -> { + var parent = em.find(BigIntParentEntity.class, parentId); + Assertions.assertNotNull(parent); + + var child = new BigIntChildEntity("Child Data"); + child.setParent(parent); + em.persist(child); + em.flush(); + + Assertions.assertNotNull(child.getId()); + }); + + // Test lazy loading behavior + doInJPA(em -> { + var child = em.createQuery("SELECT c FROM BigIntChildEntity c WHERE c.data = :data", BigIntChildEntity.class) + .setParameter("data", "Child Data") + .getSingleResult(); + + // Parent should be lazy-loaded proxy + var parent = child.getParent(); + Assertions.assertNotNull(parent); + assertThat(Hibernate.isInitialized(parent)).isFalse(); + + // Accessing parent's name should trigger lazy initialization + var retrievedParentName = parent.getName(); + assertThat(Hibernate.isInitialized(parent)).isTrue(); + assertThat(retrievedParentName).isEqualTo("Test Parent"); + assertThat(parent.getId()).isEqualTo(parentId); + }); + } + + @Test + public void testOneToOneWithMapsIdDerivedIdentity() + { + // Create and persist person entity + doInJPA(em -> { + var person = new BigIntPersonEntity("John", "Doe"); + em.persist(person); + em.flush(); + + Assertions.assertNotNull(person.getId()); + return person.getId(); + }); + + // Get the persisted person ID + var personId = doInJPA(em -> { + var person = em.createQuery("SELECT p FROM BigIntPersonEntity p WHERE p.firstName = :firstName", BigIntPersonEntity.class) + .setParameter("firstName", "John") + .getSingleResult(); + return person.getId(); + }); + + // Create PersonDetails with derived identity + doInJPA(em -> { + var person = em.find(BigIntPersonEntity.class, personId); + Assertions.assertNotNull(person); + + var details = new BigIntPersonDetailsEntity("john.doe@example.com", "+1-555-0123"); + details.setPerson(person); + + // ID should be null before persist + Assertions.assertNull(details.getId()); + + em.persist(details); + em.flush(); + + // ID should now be derived from Person + Assertions.assertNotNull(details.getId()); + assertThat(details.getId()).isEqualTo(personId); + }); + + // Test retrieval using derived ID + doInJPA(em -> { + var details = em.find(BigIntPersonDetailsEntity.class, personId); + Assertions.assertNotNull(details); + assertThat(details.getEmail()).isEqualTo("john.doe@example.com"); + assertThat(details.getId()).isEqualTo(personId); + }); + } + + @Test + public void testCompositeKeyWithEmbeddedId() + { + var customId = BigIntCompositePK.Id.random(); + var compositePK = new BigIntCompositePK(customId, "part-A"); + + // Create and persist entity with composite key + doInJPA(em -> { + var entity = new BigIntCompositeEmbeddedIdEntity(compositePK, "Test Data"); + em.persist(entity); + em.flush(); + }); + + // Test retrieval by composite key + doInJPA(em -> { + var retrieved = em.find(BigIntCompositeEmbeddedIdEntity.class, compositePK); + Assertions.assertNotNull(retrieved); + assertThat(retrieved.getData()).isEqualTo("Test Data"); + assertThat(retrieved.getId()).isEqualTo(compositePK); + }); + + // Test first-level cache integrity (critical test) + doInJPA(em -> { + var entity1 = em.find(BigIntCompositeEmbeddedIdEntity.class, compositePK); + var entity2 = em.find(BigIntCompositeEmbeddedIdEntity.class, compositePK); + + // Should be the exact same instance due to persistence context caching + Assertions.assertSame(entity1, entity2, + "First-level cache should return same instance for same composite key"); + }); + } + + @Test + public void testCompositeKeyWithIdClass() + { + var customId = BigIntIdClassPK.Id.random(); + var idClassPK = new BigIntIdClassPK(customId, "part-B"); + + // Create and persist entity with @IdClass + doInJPA(em -> { + var entity = new BigIntCompositeIdClassEntity(customId, "part-B", "IdClass Test Data"); + em.persist(entity); + em.flush(); + }); + + // Test retrieval by composite key + doInJPA(em -> { + var retrieved = em.find(BigIntCompositeIdClassEntity.class, idClassPK); + Assertions.assertNotNull(retrieved); + assertThat(retrieved.getData()).isEqualTo("IdClass Test Data"); + assertThat(retrieved.getCustomIdPart()).isEqualTo(customId); + assertThat(retrieved.getStringPart()).isEqualTo("part-B"); + }); + + // Test first-level cache integrity (critical test) + doInJPA(em -> { + var entity1 = em.find(BigIntCompositeIdClassEntity.class, idClassPK); + var entity2 = em.find(BigIntCompositeIdClassEntity.class, idClassPK); + + // Should be the exact same instance due to persistence context caching + Assertions.assertSame(entity1, entity2, + "First-level cache should return same instance for same @IdClass composite key"); + }); + } + + @Test + public void testCompositeKeyEqualityAndHashCode() + { + var customId1 = BigIntCompositePK.Id.random(); + var customId2 = BigIntCompositePK.Id.random(); + + var pk1a = new BigIntCompositePK(customId1, "test"); + var pk1b = new BigIntCompositePK(customId1, "test"); + var pk2 = new BigIntCompositePK(customId2, "test"); + var pk3 = new BigIntCompositePK(customId1, "different"); + + // Test equals + assertThat(pk1a).isEqualTo(pk1b); + assertThat(pk1a).isNotEqualTo(pk2); + assertThat(pk1a).isNotEqualTo(pk3); + + // Test hashCode consistency + assertThat(pk1a.hashCode()).isEqualTo(pk1b.hashCode()); + + var customId3 = BigIntIdClassPK.Id.random(); + var customId4 = BigIntIdClassPK.Id.random(); + + // Similar tests for IdClass + var idPk1a = new BigIntIdClassPK(customId3, "test"); + var idPk1b = new BigIntIdClassPK(customId3, "test"); + var idPk2 = new BigIntIdClassPK(customId4, "test"); + + assertThat(idPk1a).isEqualTo(idPk1b); + assertThat(idPk1a).isNotEqualTo(idPk2); + assertThat(idPk1a.hashCode()).isEqualTo(idPk1b.hashCode()); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntInheritanceHierarchyTest.java b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntInheritanceHierarchyTest.java new file mode 100644 index 0000000..fb1cd15 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/BigIntInheritanceHierarchyTest.java @@ -0,0 +1,253 @@ +package org.framefork.typedIds.bigint.hibernate; + +import org.framefork.typedIds.bigint.hibernate.inheritance.joined.BigIntAnimal; +import org.framefork.typedIds.bigint.hibernate.inheritance.joined.BigIntCat; +import org.framefork.typedIds.bigint.hibernate.inheritance.joined.BigIntDog; +import org.framefork.typedIds.bigint.hibernate.inheritance.singletable.BigIntCar; +import org.framefork.typedIds.bigint.hibernate.inheritance.singletable.BigIntTruck; +import org.framefork.typedIds.bigint.hibernate.inheritance.singletable.BigIntVehicle; +import org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass.BigIntBook; +import org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass.BigIntMagazine; +import org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass.BigIntPublication; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +final class BigIntInheritanceHierarchyTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + // Single table inheritance + BigIntVehicle.class, + BigIntCar.class, + BigIntTruck.class, + // Joined inheritance + BigIntAnimal.class, + BigIntCat.class, + BigIntDog.class, + // Table per class inheritance + BigIntPublication.class, + BigIntBook.class, + BigIntMagazine.class, + }; + } + + @Test + public void testMappedSuperclassIdInheritance() + { + // Test that all subclasses inherit the ID generation strategy from BaseEntity + doInJPA(em -> { + var car = new BigIntCar("Toyota", "Camry", 4); + var cat = new BigIntCat("Whiskers", true); + var book = new BigIntBook("Java Guide", "Tech Press", "123-456", 500); + + // IDs should be null before persist + Assertions.assertNull(car.getId()); + Assertions.assertNull(cat.getId()); + Assertions.assertNull(book.getId()); + + em.persist(car); + em.persist(cat); + em.persist(book); + em.flush(); + + // All should have IDs generated + Assertions.assertNotNull(car.getId()); + Assertions.assertNotNull(cat.getId()); + Assertions.assertNotNull(book.getId()); + + // All should have IDs generated - values may not be unique due to different generation strategies + var carId = car.getId(); + var catId = cat.getId(); + var bookId = book.getId(); + + Assertions.assertNotNull(carId); + Assertions.assertNotNull(catId); + Assertions.assertNotNull(bookId); + + // Verify IDs are positive (basic sanity check) + assertThat(carId.toLong()).isPositive(); + assertThat(catId.toLong()).isPositive(); + assertThat(bookId.toLong()).isPositive(); + }); + } + + @Test + public void testSingleTableInheritanceStrategy() + { + doInJPA(em -> { + var car = new BigIntCar("Honda", "Civic", 4); + var truck = new BigIntTruck("Ford", "F-150", 2000.0); + + em.persist(car); + em.persist(truck); + em.flush(); + + Assertions.assertNotNull(car.getId()); + Assertions.assertNotNull(truck.getId()); + }); + + // Test polymorphic query + doInJPA(em -> { + List vehicles = em.createQuery("FROM BigIntVehicle", BigIntVehicle.class) + .getResultList(); + + assertThat(vehicles).hasSize(2); + + // Should return both Car and Truck instances + var carCount = vehicles.stream().mapToInt(v -> v instanceof BigIntCar ? 1 : 0).sum(); + var truckCount = vehicles.stream().mapToInt(v -> v instanceof BigIntTruck ? 1 : 0).sum(); + + assertThat(carCount).isEqualTo(1); + assertThat(truckCount).isEqualTo(1); + }); + + // Test specific subclass queries + doInJPA(em -> { + List cars = em.createQuery("FROM BigIntCar", BigIntCar.class).getResultList(); + List trucks = em.createQuery("FROM BigIntTruck", BigIntTruck.class).getResultList(); + + assertThat(cars).hasSize(1); + assertThat(trucks).hasSize(1); + + assertThat(cars.get(0).getModel()).isEqualTo("Civic"); + assertThat(trucks.get(0).getModel()).isEqualTo("F-150"); + }); + } + + @Test + public void testJoinedInheritanceStrategy() + { + doInJPA(em -> { + var cat = new BigIntCat("Luna", true); + var dog = new BigIntDog("Max", "Golden Retriever"); + + em.persist(cat); + em.persist(dog); + em.flush(); + + Assertions.assertNotNull(cat.getId()); + Assertions.assertNotNull(dog.getId()); + }); + + // Test polymorphic query with JOINED strategy + doInJPA(em -> { + List animals = em.createQuery("FROM BigIntAnimal", BigIntAnimal.class) + .getResultList(); + + assertThat(animals).hasSize(2); + + // Should return both Cat and Dog instances + var catCount = animals.stream().mapToInt(a -> a instanceof BigIntCat ? 1 : 0).sum(); + var dogCount = animals.stream().mapToInt(a -> a instanceof BigIntDog ? 1 : 0).sum(); + + assertThat(catCount).isEqualTo(1); + assertThat(dogCount).isEqualTo(1); + }); + + // Test that both base and subclass properties are accessible + doInJPA(em -> { + List cats = em.createQuery("FROM BigIntCat", BigIntCat.class).getResultList(); + assertThat(cats).hasSize(1); + + BigIntCat cat = cats.get(0); + assertThat(cat.getName()).isEqualTo("Luna"); + assertThat(cat.getSpecies()).isEqualTo("Felis catus"); + assertThat(cat.getIsIndoor()).isTrue(); + }); + } + + @Test + public void testTablePerClassInheritanceStrategy() + { + doInJPA(em -> { + var book = new BigIntBook("Spring Boot Guide", "Tech Books", "978-123", 350); + var magazine = new BigIntMagazine("Tech Monthly", "Monthly Publications", 42, "Monthly"); + + em.persist(book); + em.persist(magazine); + em.flush(); + + Assertions.assertNotNull(book.getId()); + Assertions.assertNotNull(magazine.getId()); + }); + + // Test polymorphic query with TABLE_PER_CLASS strategy + // This typically generates UNION ALL queries + doInJPA(em -> { + List publications = em.createQuery("FROM BigIntPublication", BigIntPublication.class) + .getResultList(); + + assertThat(publications).hasSize(2); + + // Should return both Book and Magazine instances + var bookCount = publications.stream().mapToInt(p -> p instanceof BigIntBook ? 1 : 0).sum(); + var magazineCount = publications.stream().mapToInt(p -> p instanceof BigIntMagazine ? 1 : 0).sum(); + + assertThat(bookCount).isEqualTo(1); + assertThat(magazineCount).isEqualTo(1); + }); + + // Test specific subclass queries + doInJPA(em -> { + List books = em.createQuery("FROM BigIntBook", BigIntBook.class).getResultList(); + List magazines = em.createQuery("FROM BigIntMagazine", BigIntMagazine.class).getResultList(); + + assertThat(books).hasSize(1); + assertThat(magazines).hasSize(1); + + assertThat(books.get(0).getTitle()).isEqualTo("Spring Boot Guide"); + assertThat(magazines.get(0).getTitle()).isEqualTo("Tech Monthly"); + }); + } + + @Test + public void testCrossHierarchyIdUniqueness() + { + doInJPA(em -> { + var car = new BigIntCar("Tesla", "Model 3", 4); + var cat = new BigIntCat("Shadow", false); + var book = new BigIntBook("Hibernate Guide", "ORM Press", "978-456", 600); + + em.persist(car); + em.persist(cat); + em.persist(book); + em.flush(); + + return List.of(car.getId(), cat.getId(), book.getId()); + }); + + // Retrieve the entities by their IDs to verify uniqueness + doInJPA(em -> { + // Find all entities across different hierarchies + List cars = em.createQuery("FROM BigIntCar WHERE model = 'Model 3'", BigIntCar.class).getResultList(); + List cats = em.createQuery("FROM BigIntCat WHERE name = 'Shadow'", BigIntCat.class).getResultList(); + List books = em.createQuery("FROM BigIntBook WHERE title = 'Hibernate Guide'", BigIntBook.class).getResultList(); + + assertThat(cars).hasSize(1); + assertThat(cats).hasSize(1); + assertThat(books).hasSize(1); + + // Verify all entities have IDs (comparing long values since they have different types) + var carId = cars.get(0).getId(); + var catId = cats.get(0).getId(); + var bookId = books.get(0).getId(); // This is TablePerClassBaseEntity.Id + + Assertions.assertNotNull(carId); + Assertions.assertNotNull(catId); + Assertions.assertNotNull(bookId); + + // Verify IDs are positive (basic sanity check) - they may have same values due to different generation strategies + assertThat(List.of(carId, catId, bookId)) + .allMatch(id -> id.toLong() > 0); + }); + } + +} 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..be7b952 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypeMySQLTest.java @@ -0,0 +1,244 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntAppGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbAutoGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +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..7b48398 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/bigint/hibernate/ObjectBigIntIdTypePostgreSQLTest.java @@ -0,0 +1,244 @@ +package org.framefork.typedIds.bigint.hibernate; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntAppGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbAutoGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +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)).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"); + 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..4915070 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypeMySQLTest.java @@ -0,0 +1,129 @@ +package org.framefork.typedIds.uuid.hibernate; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.hibernate.id.IdentifierGenerationException; +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; +import static org.junit.jupiter.api.Assertions.assertThrows; + +final class ObjectUuidTypeMySQLTest extends AbstractMySQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + UuidAppGeneratedExplicitMappingEntity.class, + UuidNullIdEntity.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()); + }); + } + + @Test + public void testNullIdRejectionForAssignedStrategy() + { + // Test that attempting to persist an entity with null UUID ID throws appropriate exception + var exception = assertThrows( + IdentifierGenerationException.class, + () -> doInJPA(em -> { + var entity = new UuidNullIdEntity("test-data"); + // ID is null by default + Assertions.assertNull(entity.getId()); + + em.persist(entity); + em.flush(); // This should trigger the exception + }) + ); + + // Verify the exception message indicates the problem + assertThat(exception.getMessage()).contains("id"); + } + + @Test + public void testAssignedStrategyWithValidUuidId() + { + // Test successful persistence when UUID ID is properly assigned + var assignedId = UuidNullIdEntity.Id.random(); + + doInJPA(em -> { + var entity = new UuidNullIdEntity("test-data"); + entity.setId(assignedId); + + em.persist(entity); + em.flush(); + + // Verify ID was preserved + Assertions.assertEquals(assignedId, entity.getId()); + }); + + // Verify entity can be retrieved by assigned UUID ID + doInJPA(em -> { + var retrieved = em.find(UuidNullIdEntity.class, assignedId); + Assertions.assertNotNull(retrieved); + Assertions.assertEquals("test-data", retrieved.getData()); + Assertions.assertEquals(assignedId, retrieved.getId()); + }); + } + +} 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..88ef8c9 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/test/java/org/framefork/typedIds/uuid/hibernate/ObjectUuidTypePostgreSQLTest.java @@ -0,0 +1,128 @@ +package org.framefork.typedIds.uuid.hibernate; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.hibernate.id.IdentifierGenerationException; +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; +import static org.junit.jupiter.api.Assertions.assertThrows; + +final class ObjectUuidTypePostgreSQLTest extends AbstractPostgreSQLIntegrationTest +{ + + @Override + protected Class[] entities() + { + return new Class[]{ + UuidAppGeneratedExplicitMappingEntity.class, + UuidNullIdEntity.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()); + }); + } + + @Test + public void testNullIdRejectionForAssignedStrategy() + { + // Test that attempting to persist an entity with null UUID ID throws appropriate exception + var exception = assertThrows( + IdentifierGenerationException.class, + () -> doInJPA(em -> { + var entity = new UuidNullIdEntity("test-data"); + // ID is null by default + Assertions.assertNull(entity.getId()); + + em.persist(entity); + em.flush(); // This should trigger the exception + }) + ); + + // Verify the exception message indicates the problem + assertThat(exception.getMessage()).contains("id"); + } + + @Test + public void testAssignedStrategyWithValidUuidId() + { + // Test successful persistence when UUID ID is properly assigned + var assignedId = UuidNullIdEntity.Id.random(); + + doInJPA(em -> { + var entity = new UuidNullIdEntity("test-data"); + entity.setId(assignedId); + + em.persist(entity); + em.flush(); + + // Verify ID was preserved + Assertions.assertEquals(assignedId, entity.getId()); + }); + + // Verify entity can be retrieved by assigned UUID ID + doInJPA(em -> { + var retrieved = em.find(UuidNullIdEntity.class, assignedId); + Assertions.assertNotNull(retrieved); + Assertions.assertEquals("test-data", retrieved.getData()); + Assertions.assertEquals(assignedId, retrieved.getId()); + }); + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntAppGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntAppGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..130eba1 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntAppGeneratedExplicitMappingEntity.java @@ -0,0 +1,71 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbAutoGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbAutoGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..f318cb7 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbAutoGeneratedExplicitMappingEntity.java @@ -0,0 +1,76 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbIdentityGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbIdentityGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..8e413c0 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbIdentityGeneratedExplicitMappingEntity.java @@ -0,0 +1,76 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbSequenceGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbSequenceGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..1bf0455 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntDbSequenceGeneratedExplicitMappingEntity.java @@ -0,0 +1,76 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntNullIdEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntNullIdEntity.java new file mode 100644 index 0000000..15878e4 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntNullIdEntity.java @@ -0,0 +1,78 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntNullIdEntity.TABLE_NAME) +public class BigIntNullIdEntity +{ + + public static final String TABLE_NAME = "bigint_null_id_test"; + + @jakarta.persistence.Id + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String data; + + public BigIntNullIdEntity(final String data) + { + this.data = data; + } + + @SuppressWarnings("NullAway") + protected BigIntNullIdEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public void setId(@Nullable final Id id) + { + this.id = id; + } + + public String getData() + { + return data; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntPooledLoMismatchEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntPooledLoMismatchEntity.java new file mode 100644 index 0000000..978655a --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntPooledLoMismatchEntity.java @@ -0,0 +1,88 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Parameter; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@SuppressWarnings({"removal","UnnecessarilyFullyQualified"}) +@Entity +@Table(name = BigIntPooledLoMismatchEntity.TABLE_NAME) +public class BigIntPooledLoMismatchEntity +{ + + public static final String TABLE_NAME = "bigint_pooled_lo_mismatch"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pooled_lo_mismatch_gen") + @org.hibernate.annotations.GenericGenerator( + name = "pooled_lo_mismatch_gen", + strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", + parameters = { + @Parameter(name = "sequence_name", value = "pooled_lo_mismatch_seq"), + @Parameter(name = "initial_value", value = "1"), + @Parameter(name = "increment_size", value = "20"), + @Parameter(name = "optimizer", value = "pooled-lo") + } + ) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String data; + + public BigIntPooledLoMismatchEntity(final String data) + { + this.data = data; + } + + @SuppressWarnings("NullAway") + protected BigIntPooledLoMismatchEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getData() + { + return data; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntPooledLoOptimizerEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntPooledLoOptimizerEntity.java new file mode 100644 index 0000000..bf6c588 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/basic/BigIntPooledLoOptimizerEntity.java @@ -0,0 +1,88 @@ +package org.framefork.typedIds.bigint.hibernate.basic; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Parameter; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@SuppressWarnings({"removal","UnnecessarilyFullyQualified"}) +@Entity +@Table(name = BigIntPooledLoOptimizerEntity.TABLE_NAME) +public class BigIntPooledLoOptimizerEntity +{ + + public static final String TABLE_NAME = "bigint_pooled_lo_optimizer"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pooled_lo_gen") + @org.hibernate.annotations.GenericGenerator( + name = "pooled_lo_gen", + strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", + parameters = { + @Parameter(name = "sequence_name", value = "pooled_lo_seq"), + @Parameter(name = "initial_value", value = "1"), + @Parameter(name = "increment_size", value = "20"), + @Parameter(name = "optimizer", value = "pooled-lo") + } + ) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String data; + + public BigIntPooledLoOptimizerEntity(final String data) + { + this.data = data; + } + + @SuppressWarnings("NullAway") + protected BigIntPooledLoOptimizerEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getData() + { + return data; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositeEmbeddedIdEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositeEmbeddedIdEntity.java new file mode 100644 index 0000000..c381ccc --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositeEmbeddedIdEntity.java @@ -0,0 +1,42 @@ +package org.framefork.typedIds.bigint.hibernate.composite; + +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = BigIntCompositeEmbeddedIdEntity.TABLE_NAME) +public class BigIntCompositeEmbeddedIdEntity +{ + + public static final String TABLE_NAME = "bigint_composite_embedded_id_entity"; + + @EmbeddedId + private BigIntCompositePK id; + + @Column(nullable = false) + private String data; + + public BigIntCompositeEmbeddedIdEntity(final BigIntCompositePK id, final String data) + { + this.id = id; + this.data = data; + } + + @SuppressWarnings("NullAway") + protected BigIntCompositeEmbeddedIdEntity() + { + } + + public BigIntCompositePK getId() + { + return id; + } + + public String getData() + { + return data; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositeIdClassEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositeIdClassEntity.java new file mode 100644 index 0000000..3f4f5ac --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositeIdClassEntity.java @@ -0,0 +1,57 @@ +package org.framefork.typedIds.bigint.hibernate.composite; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; + +@Entity +@Table(name = BigIntCompositeIdClassEntity.TABLE_NAME) +@IdClass(BigIntIdClassPK.class) +public class BigIntCompositeIdClassEntity +{ + + public static final String TABLE_NAME = "bigint_composite_idclass_entity"; + + @jakarta.persistence.Id + @Type(ObjectBigIntIdType.class) + @Column(name = "custom_id_part") + private BigIntIdClassPK.Id customIdPart; + + @jakarta.persistence.Id + @Column(name = "string_part") + private String stringPart; + + @Column(nullable = false) + private String data; + + public BigIntCompositeIdClassEntity(final BigIntIdClassPK.Id customIdPart, final String stringPart, final String data) + { + this.customIdPart = customIdPart; + this.stringPart = stringPart; + this.data = data; + } + + @SuppressWarnings("NullAway") + protected BigIntCompositeIdClassEntity() + { + } + + public BigIntIdClassPK.Id getCustomIdPart() + { + return customIdPart; + } + + public String getStringPart() + { + return stringPart; + } + + public String getData() + { + return data; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositePK.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositePK.java new file mode 100644 index 0000000..73af53c --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntCompositePK.java @@ -0,0 +1,101 @@ +package org.framefork.typedIds.bigint.hibernate.composite; + +import jakarta.persistence.Embeddable; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; + +import java.io.Serializable; +import java.util.Objects; + +@Embeddable +public class BigIntCompositePK implements Serializable +{ + + @Type(ObjectBigIntIdType.class) + private Id customIdPart; + + private String stringPart; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntCompositePK() + { + } + + public BigIntCompositePK(final Id customIdPart, final String stringPart) + { + this.customIdPart = customIdPart; + this.stringPart = stringPart; + } + + public Id getCustomIdPart() + { + return customIdPart; + } + + public void setCustomIdPart(final Id customIdPart) + { + this.customIdPart = customIdPart; + } + + public String getStringPart() + { + return stringPart; + } + + public void setStringPart(final String stringPart) + { + this.stringPart = stringPart; + } + + @Override + public boolean equals(final Object o) + { + if (!(o instanceof BigIntCompositePK that)) { + return false; + } + return Objects.equals(customIdPart, that.customIdPart) + && Objects.equals(stringPart, that.stringPart); + } + + @Override + public int hashCode() + { + return Objects.hash(customIdPart, stringPart); + } + + @Override + public String toString() + { + return "CompositePK{" + + "customIdPart=" + customIdPart + + ", stringPart='" + stringPart + '\'' + + '}'; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntIdClassPK.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntIdClassPK.java new file mode 100644 index 0000000..8d1caa9 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/composite/BigIntIdClassPK.java @@ -0,0 +1,98 @@ +package org.framefork.typedIds.bigint.hibernate.composite; + +import org.framefork.typedIds.bigint.ObjectBigIntId; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Separate ID class for @IdClass approach (not @Embeddable) + */ +public class BigIntIdClassPK implements Serializable +{ + + private Id customIdPart; + private String stringPart; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntIdClassPK() + { + } + + public BigIntIdClassPK(final Id customIdPart, final String stringPart) + { + this.customIdPart = customIdPart; + this.stringPart = stringPart; + } + + public Id getCustomIdPart() + { + return customIdPart; + } + + public void setCustomIdPart(final Id customIdPart) + { + this.customIdPart = customIdPart; + } + + public String getStringPart() + { + return stringPart; + } + + public void setStringPart(final String stringPart) + { + this.stringPart = stringPart; + } + + @Override + public boolean equals(final Object o) + { + if (!(o instanceof BigIntIdClassPK idClassPK)) { + return false; + } + return Objects.equals(customIdPart, idClassPK.customIdPart) + && Objects.equals(stringPart, idClassPK.stringPart); + } + + @Override + public int hashCode() + { + return Objects.hash(customIdPart, stringPart); + } + + @Override + public String toString() + { + return "IdClassPK{" + + "customIdPart=" + customIdPart + + ", stringPart='" + stringPart + '\'' + + '}'; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/BigIntBaseEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/BigIntBaseEntity.java new file mode 100644 index 0000000..0ff1354 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/BigIntBaseEntity.java @@ -0,0 +1,72 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance; + +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.MappedSuperclass; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@MappedSuperclass +public abstract class BigIntBaseEntity +{ + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + protected Id id; + + @Column(nullable = false) + protected String createdBy = "system"; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntBaseEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getCreatedBy() + { + return createdBy; + } + + public void setCreatedBy(final String createdBy) + { + this.createdBy = createdBy; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntAnimal.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntAnimal.java new file mode 100644 index 0000000..9b79cf4 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntAnimal.java @@ -0,0 +1,55 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.joined; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.hibernate.inheritance.BigIntBaseEntity; + +@Entity +@Table(name = BigIntAnimal.TABLE_NAME) +@Inheritance(strategy = InheritanceType.JOINED) +public abstract class BigIntAnimal extends BigIntBaseEntity +{ + + public static final String TABLE_NAME = "bigint_joined_animal"; + + @Column(nullable = false) + private String species; + + @Column(nullable = false) + private String name; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntAnimal() + { + } + + public BigIntAnimal(final String species, final String name) + { + this.species = species; + this.name = name; + } + + public String getSpecies() + { + return species; + } + + public void setSpecies(final String species) + { + this.species = species; + } + + public String getName() + { + return name; + } + + public void setName(final String name) + { + this.name = name; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntCat.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntCat.java new file mode 100644 index 0000000..025b638 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntCat.java @@ -0,0 +1,38 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.joined; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = BigIntCat.TABLE_NAME) +public class BigIntCat extends BigIntAnimal +{ + + public static final String TABLE_NAME = "bigint_joined_cat"; + + @Column + private Boolean isIndoor; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntCat() + { + } + + public BigIntCat(final String name, final Boolean isIndoor) + { + super("Felis catus", name); + this.isIndoor = isIndoor; + } + + public Boolean getIsIndoor() + { + return isIndoor; + } + + public void setIsIndoor(final Boolean isIndoor) + { + this.isIndoor = isIndoor; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntDog.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntDog.java new file mode 100644 index 0000000..227b1db --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/joined/BigIntDog.java @@ -0,0 +1,38 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.joined; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = BigIntDog.TABLE_NAME) +public class BigIntDog extends BigIntAnimal +{ + + public static final String TABLE_NAME = "bigint_joined_dog"; + + @Column + private String breed; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntDog() + { + } + + public BigIntDog(final String name, final String breed) + { + super("Canis lupus familiaris", name); + this.breed = breed; + } + + public String getBreed() + { + return breed; + } + + public void setBreed(final String breed) + { + this.breed = breed; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntCar.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntCar.java new file mode 100644 index 0000000..52f1f2f --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntCar.java @@ -0,0 +1,36 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.singletable; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; + +@Entity +@DiscriminatorValue("CAR") +public class BigIntCar extends BigIntVehicle +{ + + @Column + private Integer numberOfDoors; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntCar() + { + } + + public BigIntCar(final String manufacturer, final String model, final Integer numberOfDoors) + { + super(manufacturer, model); + this.numberOfDoors = numberOfDoors; + } + + public Integer getNumberOfDoors() + { + return numberOfDoors; + } + + public void setNumberOfDoors(final Integer numberOfDoors) + { + this.numberOfDoors = numberOfDoors; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntTruck.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntTruck.java new file mode 100644 index 0000000..1a31d65 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntTruck.java @@ -0,0 +1,36 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.singletable; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; + +@Entity +@DiscriminatorValue("TRUCK") +public class BigIntTruck extends BigIntVehicle +{ + + @Column + private Double payloadCapacity; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntTruck() + { + } + + public BigIntTruck(final String manufacturer, final String model, final Double payloadCapacity) + { + super(manufacturer, model); + this.payloadCapacity = payloadCapacity; + } + + public Double getPayloadCapacity() + { + return payloadCapacity; + } + + public void setPayloadCapacity(final Double payloadCapacity) + { + this.payloadCapacity = payloadCapacity; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntVehicle.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntVehicle.java new file mode 100644 index 0000000..18e4c5d --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/singletable/BigIntVehicle.java @@ -0,0 +1,57 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.singletable; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.hibernate.inheritance.BigIntBaseEntity; + +@Entity +@Table(name = BigIntVehicle.TABLE_NAME) +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "vehicle_type") +public abstract class BigIntVehicle extends BigIntBaseEntity +{ + + public static final String TABLE_NAME = "bigint_single_table_vehicle"; + + @Column(nullable = false) + private String manufacturer; + + @Column(nullable = false) + private String model; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntVehicle() + { + } + + public BigIntVehicle(final String manufacturer, final String model) + { + this.manufacturer = manufacturer; + this.model = model; + } + + public String getManufacturer() + { + return manufacturer; + } + + public void setManufacturer(final String manufacturer) + { + this.manufacturer = manufacturer; + } + + public String getModel() + { + return model; + } + + public void setModel(final String model) + { + this.model = model; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntBook.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntBook.java new file mode 100644 index 0000000..7568159 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntBook.java @@ -0,0 +1,52 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = BigIntBook.TABLE_NAME) +public class BigIntBook extends BigIntPublication +{ + + public static final String TABLE_NAME = "bigint_tableperclass_book"; + + @Column + private String isbn; + + @Column + private Integer pageCount; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntBook() + { + } + + public BigIntBook(final String title, final String publisher, final String isbn, final Integer pageCount) + { + super(title, publisher); + this.isbn = isbn; + this.pageCount = pageCount; + } + + public String getIsbn() + { + return isbn; + } + + public void setIsbn(final String isbn) + { + this.isbn = isbn; + } + + public Integer getPageCount() + { + return pageCount; + } + + public void setPageCount(final Integer pageCount) + { + this.pageCount = pageCount; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntMagazine.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntMagazine.java new file mode 100644 index 0000000..72801b3 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntMagazine.java @@ -0,0 +1,52 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +@Entity +@Table(name = BigIntMagazine.TABLE_NAME) +public class BigIntMagazine extends BigIntPublication +{ + + public static final String TABLE_NAME = "bigint_tableperclass_magazine"; + + @Column + private Integer issueNumber; + + @Column + private String frequency; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntMagazine() + { + } + + public BigIntMagazine(final String title, final String publisher, final Integer issueNumber, final String frequency) + { + super(title, publisher); + this.issueNumber = issueNumber; + this.frequency = frequency; + } + + public Integer getIssueNumber() + { + return issueNumber; + } + + public void setIssueNumber(final Integer issueNumber) + { + this.issueNumber = issueNumber; + } + + public String getFrequency() + { + return frequency; + } + + public void setFrequency(final String frequency) + { + this.frequency = frequency; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntPublication.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntPublication.java new file mode 100644 index 0000000..b3a14a2 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntPublication.java @@ -0,0 +1,50 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public abstract class BigIntPublication extends BigIntTablePerClassBaseEntity +{ + + @Column(nullable = false) + private String title; + + @Column(nullable = false) + private String publisher; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntPublication() + { + } + + public BigIntPublication(final String title, final String publisher) + { + this.title = title; + this.publisher = publisher; + } + + public String getTitle() + { + return title; + } + + public void setTitle(final String title) + { + this.title = title; + } + + public String getPublisher() + { + return publisher; + } + + public void setPublisher(final String publisher) + { + this.publisher = publisher; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntTablePerClassBaseEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntTablePerClassBaseEntity.java new file mode 100644 index 0000000..0a5047d --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/inheritance/tableperclass/BigIntTablePerClassBaseEntity.java @@ -0,0 +1,72 @@ +package org.framefork.typedIds.bigint.hibernate.inheritance.tableperclass; + +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.MappedSuperclass; +import org.framefork.typedIds.bigint.ObjectBigIntId; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@MappedSuperclass +public abstract class BigIntTablePerClassBaseEntity +{ + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + protected Id id; + + @Column(nullable = false) + protected String createdBy = "system"; + + @SuppressWarnings({"unused", "NullAway"}) + protected BigIntTablePerClassBaseEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getCreatedBy() + { + return createdBy; + } + + public void setCreatedBy(final String createdBy) + { + this.createdBy = createdBy; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntChildEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntChildEntity.java new file mode 100644 index 0000000..9b64a22 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntChildEntity.java @@ -0,0 +1,65 @@ +package org.framefork.typedIds.bigint.hibernate.relationships; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntChildEntity.TABLE_NAME) +public class BigIntChildEntity +{ + + public static final String TABLE_NAME = "bigint_rel_child_entity"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @Column(nullable = false) + private String data; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id") + @Nullable + private BigIntParentEntity parent; + + @SuppressWarnings("NullAway") + public BigIntChildEntity(final String data) + { + this.data = data; + } + + @SuppressWarnings("NullAway") + protected BigIntChildEntity() + { + } + + public Long getId() + { + return id; + } + + public String getData() + { + return data; + } + + @Nullable + public BigIntParentEntity getParent() + { + return parent; + } + + public void setParent(@Nullable final BigIntParentEntity parent) + { + this.parent = parent; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntParentEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntParentEntity.java new file mode 100644 index 0000000..4134d1c --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntParentEntity.java @@ -0,0 +1,76 @@ +package org.framefork.typedIds.bigint.hibernate.relationships; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntParentEntity.TABLE_NAME) +public class BigIntParentEntity +{ + + public static final String TABLE_NAME = "bigint_rel_parent_entity"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String name; + + public BigIntParentEntity(final String name) + { + this.name = name; + } + + @SuppressWarnings("NullAway") + protected BigIntParentEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getName() + { + return name; + } + + 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/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntPersonDetailsEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntPersonDetailsEntity.java new file mode 100644 index 0000000..a9881d7 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntPersonDetailsEntity.java @@ -0,0 +1,77 @@ +package org.framefork.typedIds.bigint.hibernate.relationships; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import org.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntPersonDetailsEntity.TABLE_NAME) +public class BigIntPersonDetailsEntity +{ + + public static final String TABLE_NAME = "bigint_rel_person_details_entity"; + + @jakarta.persistence.Id + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + private BigIntPersonEntity.@Nullable Id id; + + @Column(nullable = false) + private String email; + + @Column + @Nullable + private String phoneNumber; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId + @JoinColumn(name = "id") + @Nullable + private BigIntPersonEntity person; + + public BigIntPersonDetailsEntity(final String email, @Nullable final String phoneNumber) + { + this.email = email; + this.phoneNumber = phoneNumber; + } + + @SuppressWarnings("NullAway") + protected BigIntPersonDetailsEntity() + { + } + + public BigIntPersonEntity.@Nullable Id getId() + { + return id; + } + + public String getEmail() + { + return email; + } + + @Nullable + public String getPhoneNumber() + { + return phoneNumber; + } + + @Nullable + public BigIntPersonEntity getPerson() + { + return person; + } + + public void setPerson(@Nullable final BigIntPersonEntity person) + { + this.person = person; + } + +} diff --git a/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntPersonEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntPersonEntity.java new file mode 100644 index 0000000..c72179c --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/bigint/hibernate/relationships/BigIntPersonEntity.java @@ -0,0 +1,85 @@ +package org.framefork.typedIds.bigint.hibernate.relationships; + +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.framefork.typedIds.bigint.hibernate.ObjectBigIntIdType; +import org.hibernate.annotations.Type; +import org.jspecify.annotations.Nullable; + +@Entity +@Table(name = BigIntPersonEntity.TABLE_NAME) +public class BigIntPersonEntity +{ + + public static final String TABLE_NAME = "bigint_rel_person_entity"; + + @jakarta.persistence.Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + @Type(ObjectBigIntIdType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String firstName; + + @Column(nullable = false) + private String lastName; + + public BigIntPersonEntity(final String firstName, final String lastName) + { + this.firstName = firstName; + this.lastName = lastName; + } + + @SuppressWarnings("NullAway") + protected BigIntPersonEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public String getFirstName() + { + return firstName; + } + + public String getLastName() + { + return lastName; + } + + 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/testFixtures/java/org/framefork/typedIds/uuid/hibernate/UuidAppGeneratedExplicitMappingEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/uuid/hibernate/UuidAppGeneratedExplicitMappingEntity.java new file mode 100644 index 0000000..16ee30e --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/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/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/uuid/hibernate/UuidNullIdEntity.java b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/uuid/hibernate/UuidNullIdEntity.java new file mode 100644 index 0000000..22ae3d9 --- /dev/null +++ b/modules/typed-ids-hibernate-70/src/testFixtures/java/org/framefork/typedIds/uuid/hibernate/UuidNullIdEntity.java @@ -0,0 +1,79 @@ +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 org.jspecify.annotations.Nullable; + +import java.util.UUID; + +@Entity +@Table(name = UuidNullIdEntity.TABLE_NAME) +public class UuidNullIdEntity +{ + + public static final String TABLE_NAME = "uuid_null_id_test"; + + @jakarta.persistence.Id + @Column(nullable = false) + @Type(ObjectUuidType.class) + @Nullable + private Id id; + + @Column(nullable = false) + private String data; + + public UuidNullIdEntity(final String data) + { + this.data = data; + } + + @SuppressWarnings("NullAway") + protected UuidNullIdEntity() + { + } + + @Nullable + public Id getId() + { + return id; + } + + public void setId(@Nullable final Id id) + { + this.id = id; + } + + public String getData() + { + return data; + } + + 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..9d1b4a5 --- /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.v70) + } +} 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..d7f8b08 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70MySQLTest.java @@ -0,0 +1,236 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntAppGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbAutoGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +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..4726546 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v70/ObjectBigIntIdTypeIndexedHibernate70PostgreSQLTest.java @@ -0,0 +1,232 @@ +package org.framefork.typedIds.bigint.hibernate.v70; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntAppGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbAutoGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +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..3ce223f --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70MySQLTest.java @@ -0,0 +1,81 @@ +package org.framefork.typedIds.uuid.hibernate.v70; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.framefork.typedIds.uuid.hibernate.UuidAppGeneratedExplicitMappingEntity; +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..cf920a9 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-70-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v70/ObjectUuidTypeIndexedHibernate70PostgreSQLTest.java @@ -0,0 +1,80 @@ +package org.framefork.typedIds.uuid.hibernate.v70; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.framefork.typedIds.uuid.hibernate.UuidAppGeneratedExplicitMappingEntity; +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-71-indexed/build.gradle.kts b/testing/testing-typed-ids-hibernate-71-indexed/build.gradle.kts new file mode 100644 index 0000000..2f2b63b --- /dev/null +++ b/testing/testing-typed-ids-hibernate-71-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.v71) + } +} diff --git a/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v71/ObjectBigIntIdTypeIndexedHibernate71MySQLTest.java b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v71/ObjectBigIntIdTypeIndexedHibernate71MySQLTest.java new file mode 100644 index 0000000..5cef24b --- /dev/null +++ b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v71/ObjectBigIntIdTypeIndexedHibernate71MySQLTest.java @@ -0,0 +1,236 @@ +package org.framefork.typedIds.bigint.hibernate.v71; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntAppGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbAutoGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +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 ObjectBigIntIdTypeIndexedHibernate71MySQLTest 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-71-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v71/ObjectBigIntIdTypeIndexedHibernate71PostgreSQLTest.java b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v71/ObjectBigIntIdTypeIndexedHibernate71PostgreSQLTest.java new file mode 100644 index 0000000..0cbf611 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/bigint/hibernate/v71/ObjectBigIntIdTypeIndexedHibernate71PostgreSQLTest.java @@ -0,0 +1,232 @@ +package org.framefork.typedIds.bigint.hibernate.v71; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntAppGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbAutoGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbIdentityGeneratedExplicitMappingEntity; +import org.framefork.typedIds.bigint.hibernate.basic.BigIntDbSequenceGeneratedExplicitMappingEntity; +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 ObjectBigIntIdTypeIndexedHibernate71PostgreSQLTest 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-71-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v71/ObjectUuidTypeIndexedHibernate71MySQLTest.java b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v71/ObjectUuidTypeIndexedHibernate71MySQLTest.java new file mode 100644 index 0000000..125a4c4 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v71/ObjectUuidTypeIndexedHibernate71MySQLTest.java @@ -0,0 +1,81 @@ +package org.framefork.typedIds.uuid.hibernate.v71; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractMySQLIntegrationTest; +import org.framefork.typedIds.uuid.hibernate.UuidAppGeneratedExplicitMappingEntity; +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 ObjectUuidTypeIndexedHibernate71MySQLTest 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-71-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v71/ObjectUuidTypeIndexedHibernate71PostgreSQLTest.java b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v71/ObjectUuidTypeIndexedHibernate71PostgreSQLTest.java new file mode 100644 index 0000000..4b48539 --- /dev/null +++ b/testing/testing-typed-ids-hibernate-71-indexed/src/test/java/org/framefork/typedIds/uuid/hibernate/v71/ObjectUuidTypeIndexedHibernate71PostgreSQLTest.java @@ -0,0 +1,80 @@ +package org.framefork.typedIds.uuid.hibernate.v71; + +import jakarta.persistence.Tuple; +import org.framefork.typedIds.hibernate.tests.AbstractPostgreSQLIntegrationTest; +import org.framefork.typedIds.uuid.hibernate.UuidAppGeneratedExplicitMappingEntity; +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 ObjectUuidTypeIndexedHibernate71PostgreSQLTest 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()); + }); + } + +}