Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
1 change: 1 addition & 0 deletions modules/typed-ids-hibernate-70-testing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions modules/typed-ids-hibernate-70/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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<ObjectBigIntId<?>>, DynamicParameterizedType, Serializable
{

private final LongJavaType inner;

@Nullable
private Class<ObjectBigIntId<?>> 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<ObjectBigIntId<?>> 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> X unwrap(
@Nullable final ObjectBigIntId<?> value,
@NonNull final Class<X> 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 <X> 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<ObjectBigIntId<?>> 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");
}

}
Original file line number Diff line number Diff line change
@@ -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<ObjectBigIntId<?>, 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()));
}

}
Original file line number Diff line number Diff line change
@@ -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));
}
}

}
Original file line number Diff line number Diff line change
@@ -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
}
}

}
Loading