Skip to content

Commit 3790eae

Browse files
committed
HHH-19993 Introduce AnnotationBasedUserType
Signed-off-by: Yanming Zhou <zhouyanming@gmail.com>
1 parent a79769b commit 3790eae

File tree

4 files changed

+89
-2
lines changed

4 files changed

+89
-2
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
6868
import org.hibernate.type.spi.TypeConfiguration;
6969
import org.hibernate.type.spi.TypeConfigurationAware;
70+
import org.hibernate.usertype.AnnotationBasedUserType;
7071
import org.hibernate.usertype.DynamicParameterizedType;
7172
import org.hibernate.usertype.UserType;
7273

@@ -1107,10 +1108,15 @@ private Properties getCustomTypeProperties() {
11071108
return properties;
11081109
}
11091110

1111+
@SuppressWarnings( { "unchecked", "rawtypes" } )
11101112
private UserType<?> getConfiguredUserTypeBean(
11111113
Class<? extends UserType<?>> explicitCustomType, Properties properties, Annotation typeAnnotation, MemberDetails memberDetails) {
11121114
final var typeInstance = instantiateUserType( explicitCustomType, properties, typeAnnotation, memberDetails );
11131115

1116+
if ( typeInstance instanceof AnnotationBasedUserType annotationBased && typeAnnotation != null ) {
1117+
annotationBased.initialize( typeAnnotation, memberDetails.toJavaMember() );
1118+
}
1119+
11141120
if ( typeInstance instanceof TypeConfigurationAware configurationAware ) {
11151121
configurationAware.setTypeConfiguration( getTypeConfiguration() );
11161122
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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 java.lang.annotation.Annotation;
8+
import java.lang.reflect.Member;
9+
import java.util.Properties;
10+
11+
12+
/**
13+
* A {@link UserType} which receives parameters from a custom annotation.
14+
*
15+
* @param <A> The user type annotation type supported by an implementation
16+
* @param <J> The java type
17+
*
18+
* @author Yanming Zhou
19+
*
20+
* @since 7.3
21+
*/
22+
public interface AnnotationBasedUserType<A extends Annotation, J> extends UserType<J> {
23+
/**
24+
* Initializes this generation strategy for the given annotation instance.
25+
*
26+
* @param annotation an instance of the user type annotation type. Typically,
27+
* implementations will retrieve the annotation's attribute
28+
* values and store them in fields.
29+
* @param member the Java member annotated with the user type annotation.
30+
* @throws org.hibernate.HibernateException in case an error occurred during initialization, e.g. if
31+
* an implementation can't create a value for the given property type.
32+
*/
33+
void initialize(A annotation, Member member);
34+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@
243243
*
244244
* @see org.hibernate.type.Type
245245
* @see org.hibernate.type.CustomType
246+
* @see org.hibernate.usertype.AnnotationBasedUserType
246247
*
247248
* @see org.hibernate.annotations.Type
248249
* @see org.hibernate.annotations.TypeRegistration

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
@@ -12,6 +12,7 @@
1212
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
1313
import org.hibernate.testing.orm.junit.Jpa;
1414
import org.hibernate.type.descriptor.WrapperOptions;
15+
import org.hibernate.usertype.AnnotationBasedUserType;
1516
import org.hibernate.usertype.UserType;
1617
import org.junit.jupiter.api.Test;
1718

@@ -36,7 +37,7 @@
3637
import static org.junit.jupiter.api.Assertions.assertEquals;
3738

3839
@Jpa(annotatedClasses = {MetaUserTypeTest.Thing.class, MetaUserTypeTest.SecondThing.class,
39-
MetaUserTypeTest.ThirdThing.class, MetaUserTypeTest.Things.class})
40+
MetaUserTypeTest.ThirdThing.class, MetaUserTypeTest.FourthThing.class, MetaUserTypeTest.Things.class})
4041
public class MetaUserTypeTest {
4142

4243
@Test void test(EntityManagerFactoryScope scope) {
@@ -75,6 +76,18 @@ public class MetaUserTypeTest {
7576
assertEquals( Period.of( 1, 2, 3 ), thing.period );
7677
assertEquals( Period.ofDays( 42 ), thing.days );
7778
} );
79+
80+
scope.inTransaction( em -> {
81+
FourthThing thing = new FourthThing();
82+
thing.period = Period.of( 1, 2, 3 );
83+
thing.days = Period.ofDays( 42 );
84+
em.persist( thing );
85+
} );
86+
scope.inTransaction( em -> {
87+
FourthThing thing = em.find( FourthThing.class, 1 );
88+
assertEquals( Period.of( 1, 2, 3 ), thing.period );
89+
assertEquals( Period.ofDays( 42 ), thing.days );
90+
} );
7891
}
7992

8093
@Test void testCollection(EntityManagerFactoryScope scope) {
@@ -118,6 +131,15 @@ public class MetaUserTypeTest {
118131
Period days;
119132
}
120133

134+
@Entity static class FourthThing {
135+
@Id @GeneratedValue
136+
long id;
137+
@FourthTimePeriod
138+
Period period;
139+
@FourthTimePeriod(days = true)
140+
Period days;
141+
}
142+
121143
@Entity static class Things {
122144
@Id @GeneratedValue
123145
long id;
@@ -148,6 +170,13 @@ public class MetaUserTypeTest {
148170
boolean days() default false;
149171
}
150172

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

153182
PeriodType(TimePeriod timePeriod) {
@@ -169,14 +198,31 @@ static class ThirdPeriodType extends AbstractPeriodType {
169198
ThirdPeriodType(ThirdTimePeriod timePeriod, Member member) {
170199
super(timePeriod.days());
171200
if ( !timePeriod.equals( ( (Field) member ).getAnnotation( ThirdTimePeriod.class ) )) {
201+
// only for validation
172202
throw new IllegalArgumentException(member + " should be annotated with " + timePeriod);
173203
}
174204
}
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, Member member) {
216+
days = timePeriod.days();
217+
if ( !timePeriod.equals( ( (Field) member ).getAnnotation( FourthTimePeriod.class ) )) {
218+
// only for validation
219+
throw new IllegalArgumentException(member + " 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)