Skip to content

Commit 2851646

Browse files
authored
Merge pull request #12 from framefork/fp-hibernate-70
add basic support for Hibernate ORM 7.0
2 parents 3fd53c1 + d40fc31 commit 2851646

65 files changed

Lines changed: 6427 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ For seamless type support in Hibernate ORM, you should pick one of the following
1414

1515
| Hibernate Version | Artifact |
1616
|-------------------------------|----------------------------------------------------------------------------------------------------------------------|
17+
| 7.0, 7.1 | [org.framefork:typed-ids-hibernate-70](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-70) |
1718
| 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) |
1819
| 6.2 | [org.framefork:typed-ids-hibernate-62](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-62) |
1920
| 6.1 | [org.framefork:typed-ids-hibernate-61](https://central.sonatype.com/artifact/org.framefork/typed-ids-hibernate-61) |

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ hibernate-orm-v65 = { group = "org.hibernate.orm", name = "hibernate-core", vers
3535
hibernate-orm-v66 = { group = "org.hibernate.orm", name = "hibernate-core", version = "6.6.31.Final" }
3636
hibernate-orm-v70 = { group = "org.hibernate.orm", name = "hibernate-core", version = "7.0.10.Final" }
3737
hibernate-orm-v71 = { group = "org.hibernate.orm", name = "hibernate-core", version = "7.1.2.Final" }
38+
hibernate-models-v70 = { group = "org.hibernate.models", name = "hibernate-models", version = "1.0.0" }
3839
hypersistence-utils-hibernate61 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-60", version = "3.9.4" }
3940
hypersistence-utils-hibernate62 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-62", version = "3.9.4" }
4041
hypersistence-utils-hibernate63 = { group = "io.hypersistence", name = "hypersistence-utils-hibernate-63", version = "3.11.0" }

modules/typed-ids-hibernate-70-testing/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44

55
dependencies {
66
api(project(":typed-ids-testing"))
7+
api(testFixtures(project(":typed-ids-hibernate-70")))
78

89
api(libs.hibernate.orm.v70)
910
api(libs.hypersistence.utils.hibernate70)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
plugins {
2+
id("framefork.java-public")
3+
id("java-test-fixtures")
4+
}
5+
6+
dependencies {
7+
api(project(":typed-ids"))
8+
api(libs.hibernate.orm.v70)
9+
compileOnly(libs.hibernate.models.v70) // this is really a runtime dependency, but in runtime the version from hibernate is provided
10+
11+
compileOnly(libs.jetbrains.annotations)
12+
13+
compileOnly(libs.autoService.annotations)
14+
annotationProcessor(libs.autoService.processor)
15+
16+
testImplementation(project(":typed-ids-hibernate-70-testing"))
17+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
18+
}
19+
20+
project.description = "TypeIds seamless integration into Hibernate ORMs type system"
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package org.framefork.typedIds.bigint.hibernate;
2+
3+
import org.framefork.typedIds.bigint.ObjectBigIntId;
4+
import org.framefork.typedIds.bigint.ObjectBigIntIdTypeUtils;
5+
import org.framefork.typedIds.common.ReflectionHacks;
6+
import org.framefork.typedIds.common.hibernate.ParameterizedTypeUtils;
7+
import org.hibernate.type.SqlTypes;
8+
import org.hibernate.type.descriptor.WrapperOptions;
9+
import org.hibernate.type.descriptor.java.BasicJavaType;
10+
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
11+
import org.hibernate.type.descriptor.java.LongJavaType;
12+
import org.hibernate.type.descriptor.java.MutabilityPlan;
13+
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
14+
import org.hibernate.type.descriptor.jdbc.JdbcType;
15+
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
16+
import org.hibernate.usertype.DynamicParameterizedType;
17+
import org.jetbrains.annotations.Contract;
18+
import org.jspecify.annotations.NonNull;
19+
import org.jspecify.annotations.Nullable;
20+
21+
import java.io.ObjectInputStream;
22+
import java.io.ObjectOutputStream;
23+
import java.io.Serializable;
24+
import java.lang.invoke.MethodHandle;
25+
import java.lang.reflect.Type;
26+
import java.util.Objects;
27+
import java.util.Properties;
28+
29+
@SuppressWarnings("removal") // DynamicParameterizedType usage
30+
public class ObjectBigIntIdJavaType implements BasicJavaType<ObjectBigIntId<?>>, DynamicParameterizedType, Serializable
31+
{
32+
33+
private final LongJavaType inner;
34+
35+
@Nullable
36+
private Class<ObjectBigIntId<?>> identifierClass;
37+
@Nullable
38+
private MethodHandle constructor;
39+
40+
public ObjectBigIntIdJavaType()
41+
{
42+
this.inner = LongJavaType.INSTANCE;
43+
}
44+
45+
@Override
46+
public void setParameterValues(final Properties parameters)
47+
{
48+
this.identifierClass = ParameterizedTypeUtils.getReturnedClass(parameters, ObjectBigIntId.class);
49+
50+
if (!ObjectBigIntId.class.isAssignableFrom(identifierClass)) {
51+
throw new IllegalArgumentException("Type %s is not a subtype of %s".formatted(identifierClass, ObjectBigIntId.class));
52+
}
53+
54+
this.constructor = ReflectionHacks.getConstructor(identifierClass, long.class);
55+
}
56+
57+
@Override
58+
public Type getJavaType()
59+
{
60+
return getJavaTypeClass();
61+
}
62+
63+
@Override
64+
public Class<ObjectBigIntId<?>> getJavaTypeClass()
65+
{
66+
return Objects.requireNonNull(identifierClass, "identifierClass must not be null");
67+
}
68+
69+
@Override
70+
public int extractHashCode(final ObjectBigIntId<?> value)
71+
{
72+
return Objects.hashCode(value);
73+
}
74+
75+
@Override
76+
public boolean areEqual(
77+
final ObjectBigIntId<?> one,
78+
final ObjectBigIntId<?> another
79+
)
80+
{
81+
return Objects.equals(one, another);
82+
}
83+
84+
@Override
85+
public JdbcType getRecommendedJdbcType(final JdbcTypeIndicators indicators)
86+
{
87+
final JdbcType descriptor = indicators.getJdbcType(indicators.resolveJdbcTypeCode(SqlTypes.BIGINT));
88+
return descriptor instanceof AdjustableJdbcType
89+
? ((AdjustableJdbcType) descriptor).resolveIndicatedType(indicators, this)
90+
: descriptor;
91+
}
92+
93+
@Contract("null, _, _ -> null; !null, _, _ -> !null")
94+
@Nullable
95+
@Override
96+
public <X> X unwrap(
97+
@Nullable final ObjectBigIntId<?> value,
98+
@NonNull final Class<X> type,
99+
@Nullable final WrapperOptions options
100+
)
101+
{
102+
if (value == null) {
103+
return null;
104+
}
105+
106+
return inner.unwrap(value.toLong(), type, options);
107+
}
108+
109+
@Contract("null, _ -> null; !null, _ -> !null")
110+
@Nullable
111+
@Override
112+
public <X> ObjectBigIntId<?> wrap(
113+
@Nullable final X value,
114+
@Nullable final WrapperOptions options
115+
)
116+
{
117+
if (value == null) {
118+
return null;
119+
}
120+
121+
return wrapBigIntToIdentifier(inner.wrap(value, options));
122+
}
123+
124+
@Nullable
125+
@Override
126+
public ObjectBigIntId<?> fromString(@Nullable final CharSequence string)
127+
{
128+
return (string == null) ? null : wrapBigIntToIdentifier(Long.parseLong(string.toString()));
129+
}
130+
131+
@Override
132+
public MutabilityPlan<ObjectBigIntId<?>> getMutabilityPlan()
133+
{
134+
return ImmutableMutabilityPlan.instance();
135+
}
136+
137+
private ObjectBigIntId<?> wrapBigIntToIdentifier(final long id)
138+
{
139+
return ObjectBigIntIdTypeUtils.wrapBigIntToIdentifier(
140+
id,
141+
Objects.requireNonNull(constructor, "constructor was not yet initialized")
142+
);
143+
}
144+
145+
@Override
146+
public String toString()
147+
{
148+
return "object-bigint-id(%s)".formatted(identifierClass != null ? identifierClass.getName() : "???");
149+
}
150+
151+
@SuppressWarnings("unused")
152+
private void writeObject(final ObjectOutputStream stream)
153+
{
154+
throw new UnsupportedOperationException("Serialization not supported");
155+
}
156+
157+
@SuppressWarnings("unused")
158+
private void readObject(final ObjectInputStream stream)
159+
{
160+
throw new UnsupportedOperationException("Serialization not supported");
161+
}
162+
163+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.framefork.typedIds.bigint.hibernate;
2+
3+
import org.framefork.typedIds.bigint.ObjectBigIntId;
4+
import org.framefork.typedIds.common.hibernate.ImmutableType;
5+
import org.framefork.typedIds.common.hibernate.ParameterizedTypeUtils;
6+
import org.hibernate.HibernateException;
7+
import org.hibernate.type.SqlTypes;
8+
import org.hibernate.type.descriptor.jdbc.JdbcType;
9+
import org.jspecify.annotations.Nullable;
10+
11+
public class ObjectBigIntIdType extends ImmutableType<ObjectBigIntId<?>, ObjectBigIntIdJavaType>
12+
{
13+
14+
public static final String NAME = "object-bigint-id";
15+
16+
public ObjectBigIntIdType()
17+
{
18+
this((JdbcType) null);
19+
}
20+
21+
public ObjectBigIntIdType(@Nullable final JdbcType longJdbcType)
22+
{
23+
super(new ObjectBigIntIdJavaType(), longJdbcType);
24+
}
25+
26+
public ObjectBigIntIdType(final Class<?> implClass)
27+
{
28+
this(implClass, null);
29+
}
30+
31+
@SuppressWarnings("this-escape")
32+
public ObjectBigIntIdType(final Class<?> implClass, @Nullable final JdbcType longJdbcType)
33+
{
34+
this(longJdbcType);
35+
this.setParameterValues(ParameterizedTypeUtils.forClass(implClass));
36+
}
37+
38+
@Override
39+
public String getName()
40+
{
41+
return NAME;
42+
}
43+
44+
@Override
45+
public int getSqlType()
46+
{
47+
return SqlTypes.BIGINT;
48+
}
49+
50+
public ObjectBigIntId<?> wrapJdbcValue(final Object value)
51+
{
52+
if (value instanceof Long longValue) {
53+
return getExpressibleJavaType().wrap(longValue, null);
54+
}
55+
if (value instanceof Number numberValue) {
56+
return wrapJdbcValue(numberValue.longValue());
57+
}
58+
if (getReturnedClass().isInstance(value)) {
59+
return getReturnedClass().cast(value);
60+
}
61+
62+
throw new HibernateException("Could not convert '%s' to '%s'".formatted(value.getClass().getName(), getReturnedClass()));
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.framefork.typedIds.bigint.hibernate;
2+
3+
import com.google.auto.service.AutoService;
4+
import org.framefork.typedIds.bigint.ObjectBigIntId;
5+
import org.framefork.typedIds.bigint.hibernate.id.ObjectBigIntIdGeneratorCreator;
6+
import org.hibernate.boot.ResourceStreamLocator;
7+
import org.hibernate.boot.spi.AdditionalMappingContributions;
8+
import org.hibernate.boot.spi.AdditionalMappingContributor;
9+
import org.hibernate.boot.spi.InFlightMetadataCollector;
10+
import org.hibernate.boot.spi.MetadataBuildingContext;
11+
import org.hibernate.mapping.PersistentClass;
12+
import org.hibernate.mapping.SimpleValue;
13+
14+
@SuppressWarnings("unused")
15+
@AutoService(AdditionalMappingContributor.class)
16+
public class ObjectBigIntIdTypeGenerationMetadataContributor implements AdditionalMappingContributor
17+
{
18+
19+
@Override
20+
public String getContributorName()
21+
{
22+
return this.getClass().getSimpleName();
23+
}
24+
25+
@Override
26+
public void contribute(
27+
final AdditionalMappingContributions contributions,
28+
final InFlightMetadataCollector metadata,
29+
final ResourceStreamLocator resourceStreamLocator,
30+
final MetadataBuildingContext buildingContext
31+
)
32+
{
33+
// TODO: handle mapped superclass?
34+
35+
for (PersistentClass entityBinding : metadata.getEntityBindings()) {
36+
var identifier = entityBinding.getIdentifier();
37+
if (identifier instanceof SimpleValue simpleValueIdentifier) {
38+
var identifierClass = simpleValueIdentifier.getType().getReturnedClass();
39+
if (!ObjectBigIntId.class.isAssignableFrom(identifierClass)) {
40+
continue;
41+
}
42+
43+
remapIdentifierCustomIdGeneratorCreator(simpleValueIdentifier);
44+
}
45+
}
46+
}
47+
48+
private void remapIdentifierCustomIdGeneratorCreator(final SimpleValue identifier)
49+
{
50+
var currentCreator = identifier.getCustomIdGeneratorCreator();
51+
if (currentCreator != null && !currentCreator.isAssigned()) {
52+
identifier.setCustomIdGeneratorCreator(new ObjectBigIntIdGeneratorCreator(currentCreator));
53+
}
54+
}
55+
56+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.framefork.typedIds.bigint.hibernate;
2+
3+
import com.google.auto.service.AutoService;
4+
import org.framefork.typedIds.TypedIdsRegistry;
5+
import org.hibernate.boot.model.TypeContributions;
6+
import org.hibernate.boot.model.TypeContributor;
7+
import org.hibernate.service.ServiceRegistry;
8+
import org.hibernate.type.SqlTypes;
9+
import org.hibernate.type.descriptor.jdbc.JdbcType;
10+
import org.hibernate.type.spi.TypeConfiguration;
11+
12+
@SuppressWarnings("rawtypes")
13+
@AutoService(TypeContributor.class)
14+
public class ObjectBigIntIdTypesContributor implements TypeContributor
15+
{
16+
17+
@Override
18+
public void contribute(
19+
final TypeContributions typeContributions,
20+
final ServiceRegistry serviceRegistry
21+
)
22+
{
23+
contributeIndexedTypes(typeContributions);
24+
}
25+
26+
private void contributeIndexedTypes(final TypeContributions typeContributions)
27+
{
28+
TypeConfiguration typeConfiguration = typeContributions.getTypeConfiguration();
29+
JdbcType bigintJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor(SqlTypes.BIGINT);
30+
31+
var idTypes = TypedIdsRegistry.getObjectBigIntIdClasses();
32+
for (var idType : idTypes) {
33+
var objectBigIntIdType = new ObjectBigIntIdType(idType, bigintJdbcType);
34+
35+
typeContributions.contributeType(objectBigIntIdType);
36+
37+
// todo: array type
38+
}
39+
}
40+
41+
}

0 commit comments

Comments
 (0)