Skip to content

Commit

Permalink
add typed-ids-hibernate-62
Browse files Browse the repository at this point in the history
  • Loading branch information
fprochazka committed Nov 15, 2024
1 parent 1b602fc commit 96d6ab7
Show file tree
Hide file tree
Showing 15 changed files with 1,148 additions and 5 deletions.
8 changes: 3 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ hibernate-orm-v63 = { group = "org.hibernate.orm", name = "hibernate-core", vers
hibernate-orm-v64 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.4.10.Final" }
hibernate-orm-v65 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.5.3.Final" }
hibernate-orm-v66 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.6.2.Final" }
hypersistence-utils-hibernate55 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.8.3" }
hypersistence-utils-hibernate60 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.8.3" }
hypersistence-utils-hibernate62 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.8.3" }
hypersistence-utils-hibernate55 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-55", version = "3.8.3" }
hypersistence-utils-hibernate60 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-60", version = "3.8.3" }
hypersistence-utils-hibernate62 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-62", version = "3.8.3" }
hypersistence-utils-hibernate63 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.8.3" }


20 changes: 20 additions & 0 deletions modules/typed-ids-hibernate-62/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id("framefork.java-public")
}

dependencies {
api(project(":typed-ids"))
api(libs.hibernate.orm.v62)
api(libs.hypersistence.utils.hibernate62)
api(libs.ateoClassindex)

compileOnly(libs.jetbrains.annotations)

compileOnly(libs.autoService.annotations)
annotationProcessor(libs.autoService.processor)

testImplementation(project(":typed-ids-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,127 @@
package org.framefork.typedIds.uuid.hibernate;

import io.hypersistence.utils.hibernate.type.array.internal.AbstractArrayType;
import io.hypersistence.utils.hibernate.type.array.internal.AbstractArrayTypeDescriptor;
import io.hypersistence.utils.hibernate.type.array.internal.ArrayUtil;
import io.hypersistence.utils.hibernate.type.util.ParameterizedParameterType;
import org.framefork.typedIds.uuid.ObjectUuid;
import org.hibernate.HibernateException;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.usertype.DynamicParameterizedType;

import java.sql.Array;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Properties;
import java.util.UUID;

public class ObjectUuidArrayType<T extends ObjectUuid<T>> extends AbstractArrayType<T[]>
{

public ObjectUuidArrayType(
final Class<T[]> arrayClass,
final ObjectUuidType objectUuidType
)
{
super(
new ObjectUUIDArrayTypeDescriptor<>(arrayClass, objectUuidType)
);
var parameters = new Properties();
parameters.put(DynamicParameterizedType.PARAMETER_TYPE, new ParameterizedParameterType(arrayClass));
parameters.put(SQL_ARRAY_TYPE, getJavaTypeDescriptor().getSqlArrayType());
setParameterValues(parameters);
}

public String[] getRegistrationKeys()
{
return new String[]{
returnedClass().getName(),
returnedClass().getTypeName(),
returnedClass().getCanonicalName(),
};
}

@Override
public ObjectUUIDArrayTypeDescriptor<T> getJavaTypeDescriptor()
{
return (ObjectUUIDArrayTypeDescriptor<T>) super.getJavaTypeDescriptor();
}

public static <R extends ObjectUuid<R>> ObjectUuidArrayType<R> create(
final Class<R> elementClass,
final ObjectUuidType objectUuidType
)
{
return new ObjectUuidArrayType<>(
ArrayUtil.toArrayClass(elementClass),
objectUuidType
);
}

@SuppressWarnings("unchecked")
public static final class ObjectUUIDArrayTypeDescriptor<T extends ObjectUuid<T>> extends AbstractArrayTypeDescriptor<T[]>
{

private final ObjectUuidJavaType objectUuidJavaType;

public ObjectUUIDArrayTypeDescriptor(
final Class<T[]> arrayObjectClass,
final ObjectUuidType objectUuidType
)
{
super(arrayObjectClass);
this.objectUuidJavaType = objectUuidType.getExpressibleJavaType();
}

@Override
protected String getSqlArrayType()
{
return "uuid";
}

@Override
public <X> X unwrap(
final T[] value,
final Class<X> type,
final WrapperOptions options
)
{
if (value.length > 0) {
@SuppressWarnings("unchecked")
var result = (X) Arrays.stream(value)
.map(item -> objectUuidJavaType.unwrap(item, UUID.class, options))
.toArray(UUID[]::new);

return result;
}

return super.unwrap(value, type, options);
}

@Override
public <X> T[] wrap(
final X value,
final WrapperOptions options
)
{
if (value instanceof Array array) {
try {
var uuidsArray = ArrayUtil.unwrapArray((Object[]) array.getArray(), Object[].class);

var objectUuidsArray = Arrays.stream(uuidsArray)
.map(item -> objectUuidJavaType.wrap(item, options))
.toArray(Object[]::new);

return ArrayUtil.unwrapArray(objectUuidsArray, getJavaTypeClass());

} catch (SQLException e) {
throw new HibernateException(new IllegalArgumentException(e));
}
}

return super.wrap(value, options);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package org.framefork.typedIds.uuid.hibernate;

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.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.Properties;
import java.util.UUID;

public class ObjectUuidJavaType implements BasicJavaType<ObjectUuid<?>>, DynamicParameterizedType, Serializable
{

public static final int UUID_BYTE_LENGTH = 16;

private final UUIDJavaType inner;

@Nullable
private Class<ObjectUuid<?>> identifierClass;
@Nullable
private MethodHandle constructor;

public ObjectUuidJavaType()
{
this.inner = UUIDJavaType.INSTANCE;
}

@SuppressWarnings("unchecked")
@Override
public void setParameterValues(final Properties parameters)
{
@Nullable var parameterType = (ParameterType) parameters.get(PARAMETER_TYPE);
if (parameterType != null) {
this.identifierClass = (Class<ObjectUuid<?>>) parameterType.getReturnedClass();

} else {
String entityClass = Objects.requireNonNull(parameters.get(ENTITY), "parameters.get(ENTITY) must not be null").toString();
String propertyName = Objects.requireNonNull(parameters.get(PROPERTY), "parameters.get(PROPERTY) must not be null").toString();

this.identifierClass = ObjectUuidTypeUtils.readIdentifierClass(entityClass, propertyName);
}

if (!ObjectUuid.class.isAssignableFrom(identifierClass)) {
throw new IllegalArgumentException("Type %s is not a subtype of %s".formatted(identifierClass, ObjectUuid.class));
}

this.constructor = ObjectUuidTypeUtils.getMainConstructor(identifierClass);
}

@Override
public Type getJavaType()
{
return getJavaTypeClass();
}

@Override
public Class<ObjectUuid<?>> 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;
}

@Nullable
@Override
public <X> X unwrap(
@Nullable final ObjectUuid<?> value,
@NotNull final Class<X> type,
@NotNull final WrapperOptions options
)
{
if (value == null) {
return null;
}

return inner.unwrap(value.toNativeUuid(), type, options);
}

@Nullable
@Override
public <X> ObjectUuid<?> wrap(
@Nullable final X value,
@NotNull 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<ObjectUuid<?>> 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");
}

}
Loading

0 comments on commit 96d6ab7

Please sign in to comment.