HHH-15834 add @TypeRegistration annotation
This commit is contained in:
parent
60468dadf0
commit
a7a455c39a
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.userguide.mapping.basic.bitset;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ColumnResult;
|
||||
import jakarta.persistence.ConstructorResult;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.NamedNativeQuery;
|
||||
import jakarta.persistence.SqlResultSetMapping;
|
||||
import org.hibernate.annotations.TypeRegistration;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class BitSetRegisteredUserTypeTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Product.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
BitSet bitSet = BitSet.valueOf(new long[] {1, 2, 3});
|
||||
|
||||
doInHibernate(this::sessionFactory, session -> {
|
||||
Product product = new Product();
|
||||
product.setId(1);
|
||||
product.setBitSet(bitSet);
|
||||
session.persist(product);
|
||||
});
|
||||
|
||||
doInHibernate(this::sessionFactory, session -> {
|
||||
Product product = session.get(Product.class, 1);
|
||||
assertEquals(bitSet, product.getBitSet());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeQuery() {
|
||||
BitSet bitSet = BitSet.valueOf(new long[] {1, 2, 3});
|
||||
|
||||
doInHibernate(this::sessionFactory, session -> {
|
||||
Product product = new Product();
|
||||
product.setId(1);
|
||||
product.setBitSet(bitSet);
|
||||
session.persist(product);
|
||||
});
|
||||
|
||||
doInHibernate(this::sessionFactory, session -> {
|
||||
Product product = (Product) session.getNamedNativeQuery(
|
||||
"find_person_by_bitset")
|
||||
.setParameter("id", 1L)
|
||||
.getSingleResult();
|
||||
|
||||
assertEquals(bitSet, product.getBitSet());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NamedNativeQuery(
|
||||
name = "find_person_by_bitset",
|
||||
query =
|
||||
"SELECT " +
|
||||
" pr.id AS \"pr.id\", " +
|
||||
" pr.bitset_col AS \"pr.bitset\" " +
|
||||
"FROM Product pr " +
|
||||
"WHERE pr.id = :id",
|
||||
resultSetMapping = "Person"
|
||||
)
|
||||
@SqlResultSetMapping(
|
||||
name = "Person",
|
||||
classes = @ConstructorResult(
|
||||
targetClass = Product.class,
|
||||
columns = {
|
||||
@ColumnResult(name = "pr.id"),
|
||||
@ColumnResult(name = "pr.bitset", type = BitSetUserType.class)
|
||||
}
|
||||
)
|
||||
)
|
||||
//tag::basic-custom-type-registered-BitSetUserType-mapping-example[]
|
||||
@Entity(name = "Product")
|
||||
@TypeRegistration(basicClass = BitSet.class, userType = BitSetUserType.class)
|
||||
public static class Product {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "bitset_col")
|
||||
private BitSet bitSet;
|
||||
|
||||
//Constructors, getters, and setters are omitted for brevity
|
||||
//end::basic-custom-type-registered-BitSetUserType-mapping-example[]
|
||||
public Product() {
|
||||
}
|
||||
|
||||
public Product(Number id, BitSet bitSet) {
|
||||
this.id = id.intValue();
|
||||
this.bitSet = bitSet;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BitSet getBitSet() {
|
||||
return bitSet;
|
||||
}
|
||||
|
||||
public void setBitSet(BitSet bitSet) {
|
||||
this.bitSet = bitSet;
|
||||
}
|
||||
//tag::basic-custom-type-BitSetUserType-mapping-example[]
|
||||
}
|
||||
//end::basic-custom-type-BitSetUserType-mapping-example[]
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
|
||||
|
@ -16,11 +17,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
|
||||
/**
|
||||
* Applies a custom {@link CompositeUserType} for the attribute mapping.
|
||||
*
|
||||
* @see CompositeUserType
|
||||
* @see CompositeTypeRegistration
|
||||
*/
|
||||
@java.lang.annotation.Target({METHOD, FIELD})
|
||||
@Target({METHOD, FIELD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface CompositeType {
|
||||
|
||||
/**
|
||||
* The custom type implementor class
|
||||
*/
|
||||
|
|
|
@ -18,10 +18,16 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Registers a custom composite user type implementation to be used
|
||||
* for all references to a particular {@link jakarta.persistence.Embeddable}.
|
||||
* Registers a custom {@linkplain CompositeUserType composite user type}
|
||||
* implementation to be used by default for all references to a particular
|
||||
* {@linkplain jakarta.persistence.Embeddable embeddable} class.
|
||||
* <p>
|
||||
* May be overridden for a specific embedded using {@link org.hibernate.annotations.CompositeType}
|
||||
* May be overridden for a specific entity field or property using
|
||||
* {@link CompositeType}.
|
||||
*
|
||||
* @see CompositeUserType
|
||||
* @see CompositeType
|
||||
* @see TypeRegistration
|
||||
*/
|
||||
@Target( {TYPE, ANNOTATION_TYPE, PACKAGE} )
|
||||
@Retention( RUNTIME )
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
|
@ -19,11 +20,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
* <p>
|
||||
* This is usually mutually exclusive with the compositional approach of
|
||||
* {@link JavaType}, {@link JdbcType}, etc.
|
||||
*
|
||||
* @see UserType
|
||||
* @see TypeRegistration
|
||||
*/
|
||||
@java.lang.annotation.Target({METHOD, FIELD})
|
||||
@Target({METHOD, FIELD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface Type {
|
||||
|
||||
/**
|
||||
* The implementation class which implements {@link UserType}.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.PACKAGE;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Registers a custom {@linkplain UserType user type} implementation
|
||||
* to be used by default for all references to a particular basic type.
|
||||
* <p>
|
||||
* May be overridden for a specific entity field or property using
|
||||
* {@link Type}.
|
||||
*
|
||||
* @see UserType
|
||||
* @see Type
|
||||
* @see CompositeTypeRegistration
|
||||
*
|
||||
* @author Gavin King
|
||||
*
|
||||
* @since 6.2
|
||||
*/
|
||||
@Target( {TYPE, ANNOTATION_TYPE, PACKAGE} )
|
||||
@Retention( RUNTIME )
|
||||
@Repeatable( TypeRegistrations.class )
|
||||
public @interface TypeRegistration {
|
||||
Class<?> basicClass();
|
||||
Class<? extends UserType<?>> userType();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.PACKAGE;
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Grouping of {@link TypeRegistration}
|
||||
*
|
||||
* @author Gavin King
|
||||
*
|
||||
* @since 6.2
|
||||
*/
|
||||
@Target( {TYPE, ANNOTATION_TYPE, PACKAGE} )
|
||||
@Retention( RUNTIME )
|
||||
public @interface TypeRegistrations {
|
||||
TypeRegistration[] value();
|
||||
}
|
|
@ -113,6 +113,7 @@ import jakarta.persistence.AttributeConverter;
|
|||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.MapsId;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
/**
|
||||
* The implementation of the {@linkplain InFlightMetadataCollector in-flight
|
||||
|
@ -429,8 +430,25 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
|||
return registeredCompositeUserTypes.get( embeddableType );
|
||||
}
|
||||
|
||||
private Map<CollectionClassification, CollectionTypeRegistrationDescriptor> collectionTypeRegistrations;
|
||||
private Map<Class<?>, Class<? extends UserType<?>>> registeredUserTypes;
|
||||
@Override
|
||||
public void registerUserType(Class<?> basicType, Class<? extends UserType<?>> userType) {
|
||||
if ( registeredUserTypes == null ) {
|
||||
registeredUserTypes = new HashMap<>();
|
||||
}
|
||||
registeredUserTypes.put( basicType, userType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends UserType<?>> findRegisteredUserType(Class<?> basicType) {
|
||||
if ( registeredUserTypes == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return registeredUserTypes.get( basicType );
|
||||
}
|
||||
|
||||
private Map<CollectionClassification, CollectionTypeRegistrationDescriptor> collectionTypeRegistrations;
|
||||
|
||||
@Override
|
||||
public void addCollectionTypeRegistration(CollectionTypeRegistration registrationAnnotation) {
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.hibernate.usertype.CompositeUserType;
|
|||
import org.hibernate.usertype.UserCollectionType;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
/**
|
||||
* An in-flight representation of {@link org.hibernate.boot.Metadata} while it is being built.
|
||||
|
@ -306,6 +307,9 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
|
|||
void registerCompositeUserType(Class<?> embeddableType, Class<? extends CompositeUserType<?>> userType);
|
||||
Class<? extends CompositeUserType<?>> findRegisteredCompositeUserType(Class<?> embeddableType);
|
||||
|
||||
void registerUserType(Class<?> embeddableType, Class<? extends UserType<?>> userType);
|
||||
Class<? extends UserType<?>> findRegisteredUserType(Class<?> basicType);
|
||||
|
||||
void addCollectionTypeRegistration(CollectionTypeRegistration registrationAnnotation);
|
||||
void addCollectionTypeRegistration(CollectionClassification classification, CollectionTypeRegistrationDescriptor descriptor);
|
||||
CollectionTypeRegistrationDescriptor findCollectionTypeRegistration(CollectionClassification classification);
|
||||
|
|
|
@ -56,6 +56,8 @@ import org.hibernate.annotations.ParamDef;
|
|||
import org.hibernate.annotations.Parameter;
|
||||
import org.hibernate.annotations.Parent;
|
||||
import org.hibernate.annotations.TimeZoneStorage;
|
||||
import org.hibernate.annotations.TypeRegistration;
|
||||
import org.hibernate.annotations.TypeRegistrations;
|
||||
import org.hibernate.annotations.ValueGenerationType;
|
||||
import org.hibernate.annotations.common.reflection.ReflectionManager;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
|
@ -307,6 +309,7 @@ public final class AnnotationBinder {
|
|||
|
||||
handleTypeDescriptorRegistrations( annotatedPackage, context );
|
||||
bindEmbeddableInstantiatorRegistrations( annotatedPackage, context );
|
||||
bindUserTypeRegistrations( annotatedPackage, context );
|
||||
bindCompositeUserTypeRegistrations( annotatedPackage, context );
|
||||
handleConverterRegistrations( annotatedPackage, context );
|
||||
|
||||
|
@ -536,6 +539,7 @@ public final class AnnotationBinder {
|
|||
final Map<String, IdentifierGeneratorDefinition> generators = buildGenerators( annotatedClass, context );
|
||||
handleTypeDescriptorRegistrations( annotatedClass, context );
|
||||
bindEmbeddableInstantiatorRegistrations( annotatedClass, context );
|
||||
bindUserTypeRegistrations( annotatedClass, context );
|
||||
bindCompositeUserTypeRegistrations( annotatedClass, context );
|
||||
handleConverterRegistrations( annotatedClass, context );
|
||||
|
||||
|
@ -686,6 +690,35 @@ public final class AnnotationBinder {
|
|||
}
|
||||
}
|
||||
|
||||
private static void bindUserTypeRegistrations(
|
||||
XAnnotatedElement annotatedElement,
|
||||
MetadataBuildingContext context) {
|
||||
final TypeRegistration typeRegistration =
|
||||
annotatedElement.getAnnotation( TypeRegistration.class );
|
||||
if ( typeRegistration != null ) {
|
||||
handleUserTypeRegistration( context, typeRegistration );
|
||||
}
|
||||
else {
|
||||
final TypeRegistrations typeRegistrations =
|
||||
annotatedElement.getAnnotation( TypeRegistrations.class );
|
||||
if ( typeRegistrations != null ) {
|
||||
final TypeRegistration[] registrations = typeRegistrations.value();
|
||||
for ( TypeRegistration registration : registrations ) {
|
||||
handleUserTypeRegistration( context, registration );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleUserTypeRegistration(
|
||||
MetadataBuildingContext context,
|
||||
TypeRegistration compositeTypeRegistration) {
|
||||
context.getMetadataCollector().registerUserType(
|
||||
compositeTypeRegistration.basicClass(),
|
||||
compositeTypeRegistration.userType()
|
||||
);
|
||||
}
|
||||
|
||||
private static void handleCompositeUserTypeRegistration(
|
||||
MetadataBuildingContext context,
|
||||
CompositeTypeRegistration compositeTypeRegistration) {
|
||||
|
|
|
@ -324,6 +324,17 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
// An explicit custom UserType has top precedence when we get to BasicValue resolution.
|
||||
return;
|
||||
}
|
||||
else if ( modelTypeXClass != null ) {
|
||||
final Class<?> basicClass = buildingContext.getBootstrapContext()
|
||||
.getReflectionManager()
|
||||
.toClass( modelTypeXClass );
|
||||
final Class<? extends UserType<?>> registeredUserTypeImpl =
|
||||
buildingContext.getMetadataCollector().findRegisteredUserType( basicClass );
|
||||
if ( registeredUserTypeImpl != null ) {
|
||||
applyExplicitType( registeredUserTypeImpl, new Parameter[0] );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ( kind ) {
|
||||
case ATTRIBUTE: {
|
||||
|
|
|
@ -44,6 +44,18 @@ import org.hibernate.metamodel.spi.ValueAccess;
|
|||
* <p>
|
||||
* Every implementor of {@code CompositeUserType} must be immutable
|
||||
* and must declare a public default constructor.
|
||||
* <p>
|
||||
* A custom type may be applied to an attribute of an entity either:
|
||||
* <ul>
|
||||
* <li>explicitly, using
|
||||
* {@link org.hibernate.annotations.CompositeType @CompositeType},
|
||||
* or
|
||||
* <li>implicitly, using
|
||||
* {@link org.hibernate.annotations.CompositeTypeRegistration @CompositeTypeRegistration}.
|
||||
* </ul>
|
||||
*
|
||||
* @see org.hibernate.annotations.CompositeType
|
||||
* @see org.hibernate.annotations.CompositeTypeRegistration
|
||||
*/
|
||||
@Incubating
|
||||
public interface CompositeUserType<J> extends EmbeddableInstantiator {
|
||||
|
|
|
@ -52,10 +52,21 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
* may be used in queries. If a custom type does have attributes, and
|
||||
* can be thought of as something more like an embeddable object, it
|
||||
* might be better to implement {@link CompositeUserType}.
|
||||
* <p>
|
||||
* A custom type may be applied to an attribute of an entity either:
|
||||
* <ul>
|
||||
* <li>explicitly, using {@link org.hibernate.annotations.Type @Type},
|
||||
* or
|
||||
* <li>implicitly, using
|
||||
* {@link org.hibernate.annotations.TypeRegistration @TypeRegistration}.
|
||||
* </ul>
|
||||
*
|
||||
* @see org.hibernate.type.Type
|
||||
* @see org.hibernate.type.CustomType
|
||||
*
|
||||
* @see org.hibernate.annotations.Type
|
||||
* @see org.hibernate.annotations.TypeRegistration
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface UserType<J> {
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.orm.test.mapping.embeddable.strategy.usertype.registered;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.annotations.CompositeType;
|
||||
import org.hibernate.annotations.CompositeTypeRegistration;
|
||||
import org.hibernate.orm.test.mapping.embeddable.strategy.usertype.embedded.Name;
|
||||
import org.hibernate.orm.test.mapping.embeddable.strategy.usertype.embedded.NameCompositeUserType;
|
||||
|
|
Loading…
Reference in New Issue