- * Every implementor of {@code UserType} must be immutable and must - * declare a public default constructor. + * Every implementor of {@code UserType} must be immutable. + *
+ * A custom type may receive parameters from its type annotation. + * The custom type may either: + *
* A custom type implemented as a {@code UserType} is treated as a
* non-composite value, and does not have persistent attributes which
@@ -242,6 +257,7 @@
*
* @see org.hibernate.type.Type
* @see org.hibernate.type.CustomType
+ * @see org.hibernate.usertype.AnnotationBasedUserType
*
* @see org.hibernate.annotations.Type
* @see org.hibernate.annotations.TypeRegistration
diff --git a/hibernate-core/src/main/java/org/hibernate/usertype/UserTypeCreationContext.java b/hibernate-core/src/main/java/org/hibernate/usertype/UserTypeCreationContext.java
new file mode 100644
index 000000000000..d30b90cd112d
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/usertype/UserTypeCreationContext.java
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.usertype;
+
+import org.hibernate.Incubating;
+import org.hibernate.annotations.Type;
+import org.hibernate.boot.spi.MetadataBuildingContext;
+import org.hibernate.models.spi.MemberDetails;
+import org.hibernate.service.ServiceRegistry;
+
+import java.util.Properties;
+
+/**
+ * Access to information useful during {@linkplain UserType} creation and initialization.
+ *
+ * @author Yanming Zhou
+ * @see AnnotationBasedUserType
+ *
+ * @since 7.3
+ */
+@Incubating
+public interface UserTypeCreationContext {
+ /**
+ * Access to the {@link MetadataBuildingContext}.
+ */
+ MetadataBuildingContext getBuildingContext();
+
+ /**
+ * Access to available services.
+ */
+ ServiceRegistry getServiceRegistry();
+
+ /**
+ * Access to the {@link MemberDetails}.
+ */
+ MemberDetails getMemberDetails();
+
+ /**
+ * Access to the parameters.
+ *
+ * @see Type#parameters()
+ */
+ Properties getParameters();
+
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/bitset/MetaUserTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/bitset/MetaUserTypeTest.java
index 254752ae1c0a..910549823d14 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/bitset/MetaUserTypeTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/bitset/MetaUserTypeTest.java
@@ -8,16 +8,20 @@
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
+import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.usertype.AnnotationBasedUserType;
import org.hibernate.usertype.UserType;
+import org.hibernate.usertype.UserTypeCreationContext;
import org.junit.jupiter.api.Test;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
+import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -33,7 +37,8 @@
import static java.sql.Types.VARCHAR;
import static org.junit.jupiter.api.Assertions.assertEquals;
-@Jpa(annotatedClasses = {MetaUserTypeTest.Thing.class, MetaUserTypeTest.Things.class})
+@Jpa(annotatedClasses = {MetaUserTypeTest.Thing.class, MetaUserTypeTest.SecondThing.class,
+ MetaUserTypeTest.ThirdThing.class, MetaUserTypeTest.FourthThing.class, MetaUserTypeTest.Things.class})
public class MetaUserTypeTest {
@Test void test(EntityManagerFactoryScope scope) {
@@ -48,6 +53,42 @@ public class MetaUserTypeTest {
assertEquals( Period.of( 1, 2, 3 ), thing.period );
assertEquals( Period.ofDays( 42 ), thing.days );
} );
+
+ scope.inTransaction( em -> {
+ SecondThing thing = new SecondThing();
+ thing.period = Period.of( 1, 2, 3 );
+ thing.days = Period.ofDays( 42 );
+ em.persist( thing );
+ } );
+ scope.inTransaction( em -> {
+ SecondThing thing = em.find( SecondThing.class, 1 );
+ assertEquals( Period.of( 1, 2, 3 ), thing.period );
+ assertEquals( Period.ofDays( 42 ), thing.days );
+ } );
+
+ scope.inTransaction( em -> {
+ ThirdThing thing = new ThirdThing();
+ thing.period = Period.of( 1, 2, 3 );
+ thing.days = Period.ofDays( 42 );
+ em.persist( thing );
+ } );
+ scope.inTransaction( em -> {
+ ThirdThing thing = em.find( ThirdThing.class, 1 );
+ assertEquals( Period.of( 1, 2, 3 ), thing.period );
+ assertEquals( Period.ofDays( 42 ), thing.days );
+ } );
+
+ scope.inTransaction( em -> {
+ FourthThing thing = new FourthThing();
+ thing.period = Period.of( 1, 2, 3 );
+ thing.days = Period.ofDays( 42 );
+ em.persist( thing );
+ } );
+ scope.inTransaction( em -> {
+ FourthThing thing = em.find( FourthThing.class, 1 );
+ assertEquals( Period.of( 1, 2, 3 ), thing.period );
+ assertEquals( Period.ofDays( 42 ), thing.days );
+ } );
}
@Test void testCollection(EntityManagerFactoryScope scope) {
@@ -73,6 +114,33 @@ public class MetaUserTypeTest {
Period days;
}
+ @Entity static class SecondThing {
+ @Id @GeneratedValue
+ long id;
+ @SecondTimePeriod
+ Period period;
+ @SecondTimePeriod(days = true)
+ Period days;
+ }
+
+ @Entity static class ThirdThing {
+ @Id @GeneratedValue
+ long id;
+ @ThirdTimePeriod
+ Period period;
+ @ThirdTimePeriod(days = true)
+ Period days;
+ }
+
+ @Entity static class FourthThing {
+ @Id @GeneratedValue
+ long id;
+ @FourthTimePeriod
+ Period period;
+ @FourthTimePeriod(days = true)
+ Period days;
+ }
+
@Entity static class Things {
@Id @GeneratedValue
long id;
@@ -89,11 +157,76 @@ public class MetaUserTypeTest {
boolean days() default false;
}
- static class PeriodType implements UserType