Skip to content

Commit 5024e01

Browse files
committed
HHH-19993 Introduce AnnotationBasedUserType
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
1 parent 14dec96 commit 5024e01

File tree

5 files changed

+145
-2
lines changed

5 files changed

+145
-2
lines changed

hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.hibernate.Internal;
1717
import org.hibernate.MappingException;
1818
import org.hibernate.models.spi.MemberDetails;
19+
import org.hibernate.service.ServiceRegistry;
1920
import org.hibernate.type.TimeZoneStorageStrategy;
2021
import org.hibernate.annotations.SoftDelete;
2122
import org.hibernate.annotations.SoftDeleteType;
@@ -66,13 +67,15 @@
6667
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
6768
import org.hibernate.type.spi.TypeConfiguration;
6869
import org.hibernate.type.spi.TypeConfigurationAware;
70+
import org.hibernate.usertype.AnnotationBasedUserType;
6971
import org.hibernate.usertype.DynamicParameterizedType;
7072
import org.hibernate.usertype.UserType;
7173

7274
import com.fasterxml.classmate.ResolvedType;
7375
import jakarta.persistence.AttributeConverter;
7476
import jakarta.persistence.EnumType;
7577
import jakarta.persistence.TemporalType;
78+
import org.hibernate.usertype.UserTypeCreationContext;
7679

7780
import static java.lang.Boolean.parseBoolean;
7881
import static org.hibernate.boot.model.convert.spi.ConverterDescriptor.TYPE_NAME_PREFIX;
@@ -1106,10 +1109,30 @@ private Properties getCustomTypeProperties() {
11061109
return properties;
11071110
}
11081111

1112+
@SuppressWarnings( { "unchecked", "rawtypes" } )
11091113
private UserType<?> getConfiguredUserTypeBean(
11101114
Class<? extends UserType<?>> explicitCustomType, Properties properties, Annotation typeAnnotation, MemberDetails memberDetails) {
11111115
final var typeInstance = instantiateUserType( explicitCustomType, properties, typeAnnotation, memberDetails );
11121116

1117+
if ( typeInstance instanceof AnnotationBasedUserType annotationBased && typeAnnotation != null ) {
1118+
annotationBased.initialize( typeAnnotation, new UserTypeCreationContext() {
1119+
@Override
1120+
public MetadataBuildingContext getBuildingContext() {
1121+
return BasicValue.this.getBuildingContext();
1122+
}
1123+
1124+
@Override
1125+
public ServiceRegistry getServiceRegistry() {
1126+
return BasicValue.this.getServiceRegistry();
1127+
}
1128+
1129+
@Override
1130+
public MemberDetails getMemberDetails() {
1131+
return memberDetails;
1132+
}
1133+
} );
1134+
}
1135+
11131136
if ( typeInstance instanceof TypeConfigurationAware configurationAware ) {
11141137
configurationAware.setTypeConfiguration( getTypeConfiguration() );
11151138
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.usertype;
6+
7+
import org.hibernate.Incubating;
8+
import org.hibernate.models.spi.MemberDetails;
9+
10+
import java.lang.annotation.Annotation;
11+
12+
13+
/**
14+
* A {@link UserType} which receives parameters from a custom annotation.
15+
*
16+
* @param <A> The user type annotation type supported by an implementation
17+
* @param <J> The java type
18+
*
19+
* @author Yanming Zhou
20+
*
21+
* @since 7.3
22+
*/
23+
@Incubating
24+
public interface AnnotationBasedUserType<A extends Annotation, J> extends UserType<J> {
25+
/**
26+
* Initializes this generation strategy for the given annotation instance.
27+
*
28+
* @param annotation an instance of the user type annotation type. Typically,
29+
* implementations will retrieve the annotation's attribute
30+
* values and store them in fields.
31+
* @param context a {@link UserTypeCreationContext}.
32+
* @throws org.hibernate.HibernateException in case an error occurred during initialization, e.g. if
33+
* an implementation can't create a value for the given property type.
34+
*/
35+
void initialize(A annotation, UserTypeCreationContext context);
36+
}

hibernate-core/src/main/java/org/hibernate/usertype/UserType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@
244244
*
245245
* @see org.hibernate.type.Type
246246
* @see org.hibernate.type.CustomType
247+
* @see org.hibernate.usertype.AnnotationBasedUserType
247248
*
248249
* @see org.hibernate.annotations.Type
249250
* @see org.hibernate.annotations.TypeRegistration
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.usertype;
6+
7+
import org.hibernate.Incubating;
8+
import org.hibernate.boot.spi.MetadataBuildingContext;
9+
import org.hibernate.models.spi.MemberDetails;
10+
import org.hibernate.service.ServiceRegistry;
11+
12+
/**
13+
* Access to information useful during {@linkplain UserType} creation and initialization.
14+
*
15+
* @author Yanming Zhou
16+
* @see AnnotationBasedUserType
17+
*
18+
* @since 7.3
19+
*/
20+
@Incubating
21+
public interface UserTypeCreationContext {
22+
/**
23+
* Access to the {@link MetadataBuildingContext}.
24+
*/
25+
MetadataBuildingContext getBuildingContext();
26+
27+
/**
28+
* Access to available services.
29+
*/
30+
ServiceRegistry getServiceRegistry();
31+
32+
/**
33+
* Access to the {@link MemberDetails}.
34+
*/
35+
MemberDetails getMemberDetails();
36+
37+
}

hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/bitset/MetaUserTypeTest.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
1414
import org.hibernate.testing.orm.junit.Jpa;
1515
import org.hibernate.type.descriptor.WrapperOptions;
16+
import org.hibernate.usertype.AnnotationBasedUserType;
1617
import org.hibernate.usertype.UserType;
18+
import org.hibernate.usertype.UserTypeCreationContext;
1719
import org.junit.jupiter.api.Test;
1820

1921
import java.io.Serializable;
@@ -36,7 +38,7 @@
3638
import static org.junit.jupiter.api.Assertions.assertEquals;
3739

3840
@Jpa(annotatedClasses = {MetaUserTypeTest.Thing.class, MetaUserTypeTest.SecondThing.class,
39-
MetaUserTypeTest.ThirdThing.class, MetaUserTypeTest.Things.class})
41+
MetaUserTypeTest.ThirdThing.class, MetaUserTypeTest.FourthThing.class, MetaUserTypeTest.Things.class})
4042
public class MetaUserTypeTest {
4143

4244
@Test void test(EntityManagerFactoryScope scope) {
@@ -75,6 +77,18 @@ public class MetaUserTypeTest {
7577
assertEquals( Period.of( 1, 2, 3 ), thing.period );
7678
assertEquals( Period.ofDays( 42 ), thing.days );
7779
} );
80+
81+
scope.inTransaction( em -> {
82+
FourthThing thing = new FourthThing();
83+
thing.period = Period.of( 1, 2, 3 );
84+
thing.days = Period.ofDays( 42 );
85+
em.persist( thing );
86+
} );
87+
scope.inTransaction( em -> {
88+
FourthThing thing = em.find( FourthThing.class, 1 );
89+
assertEquals( Period.of( 1, 2, 3 ), thing.period );
90+
assertEquals( Period.ofDays( 42 ), thing.days );
91+
} );
7892
}
7993

8094
@Test void testCollection(EntityManagerFactoryScope scope) {
@@ -118,6 +132,15 @@ public class MetaUserTypeTest {
118132
Period days;
119133
}
120134

135+
@Entity static class FourthThing {
136+
@Id @GeneratedValue
137+
long id;
138+
@FourthTimePeriod
139+
Period period;
140+
@FourthTimePeriod(days = true)
141+
Period days;
142+
}
143+
121144
@Entity static class Things {
122145
@Id @GeneratedValue
123146
long id;
@@ -148,6 +171,13 @@ public class MetaUserTypeTest {
148171
boolean days() default false;
149172
}
150173

174+
@Type(FourthPeriodType.class)
175+
@Target({METHOD, FIELD})
176+
@Retention(RUNTIME)
177+
public @interface FourthTimePeriod {
178+
boolean days() default false;
179+
}
180+
151181
static class PeriodType extends AbstractPeriodType {
152182

153183
PeriodType(TimePeriod timePeriod) {
@@ -175,8 +205,24 @@ static class ThirdPeriodType extends AbstractPeriodType {
175205

176206
}
177207

208+
static class FourthPeriodType extends AbstractPeriodType implements AnnotationBasedUserType<FourthTimePeriod, Period> {
209+
210+
FourthPeriodType() {
211+
super(false);
212+
}
213+
214+
@Override
215+
public void initialize(FourthTimePeriod timePeriod, UserTypeCreationContext context) {
216+
days = timePeriod.days();
217+
if ( !timePeriod.equals( ( (Field) context.getMemberDetails().toJavaMember() ).getAnnotation( FourthTimePeriod.class ) )) {
218+
// only for validation
219+
throw new IllegalArgumentException(context.getMemberDetails().toJavaMember() + " should be annotated with " + timePeriod);
220+
}
221+
}
222+
}
223+
178224
static abstract class AbstractPeriodType implements UserType<Period> {
179-
private final boolean days;
225+
boolean days;
180226

181227
AbstractPeriodType(boolean days) {
182228
this.days = days;

0 commit comments

Comments
 (0)