Skip to content

Commit e817147

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

File tree

5 files changed

+87
-3
lines changed

5 files changed

+87
-3
lines changed

documentation/src/main/asciidoc/userguide/chapters/domain/basic_types.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2563,7 +2563,7 @@ include::{example-dir-basic-mapping}/generated/GeneratedTest.java[tags=mapping-g
25632563
[[mapping-generated-custom]]
25642564
===== Custom generation strategy
25652565

2566-
Hibernate also supports value generation via a pluggable API using `@ValueGenerationType` and `AnnotationBasedGenerator`
2566+
Hibernate also supports value generation via a pluggable API using `@ValueGenerationType` and `AnnotationBasedUserType`
25672567
allowing users to define any generation strategy they wish.
25682568

25692569
Let's look at an example of generating UUID values. First the attribute mapping

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

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

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

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

1115+
if ( typeInstance instanceof AnnotationBasedUserType annotationBased && typeAnnotation != null ) {
1116+
annotationBased.initialize( typeAnnotation, member, properties );
1117+
}
1118+
11131119
if ( typeInstance instanceof TypeConfigurationAware configurationAware ) {
11141120
configurationAware.setTypeConfiguration( getTypeConfiguration() );
11151121
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
* @param properties user type properties
31+
* @throws org.hibernate.HibernateException in case an error occurred during initialization, e.g. if
32+
* an implementation can't create a value for the given property type.
33+
*/
34+
void initialize(A annotation, Member member, Properties properties);
35+
}

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: 44 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

@@ -27,6 +28,7 @@
2728
import java.util.ArrayList;
2829
import java.util.List;
2930
import java.util.Objects;
31+
import java.util.Properties;
3032

3133
import static java.lang.Integer.parseInt;
3234
import static java.lang.annotation.ElementType.FIELD;
@@ -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,20 @@ 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 annotation, Member member, Properties properties) {
216+
days = annotation.days();
217+
}
218+
}
219+
178220
static abstract class AbstractPeriodType implements UserType<Period> {
179-
private final boolean days;
221+
boolean days;
180222

181223
AbstractPeriodType(boolean days) {
182224
this.days = days;

0 commit comments

Comments
 (0)