HHH-14885 - New composite user-type
`@EmbeddableInstantiatorRegistration`
This commit is contained in:
parent
924c2b29c3
commit
61d178ef1b
|
@ -288,6 +288,20 @@ include::{instantiatorTestDir}/embeddable/Person.java[tags=embeddable-instantiat
|
|||
====
|
||||
|
||||
|
||||
Additionally, instantiators can be registered:
|
||||
|
||||
[[embeddable-instantiator-registration-ex]]
|
||||
.`@EmbeddableInstantiatorRegistration`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{instantiatorTestDir}/registered/Name.java[tags=embeddable-instantiator-registration]
|
||||
include::{instantiatorTestDir}/registered/Person.java[tags=embeddable-instantiator-registration]
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
|
||||
[[embeddable-multiple-namingstrategy]]
|
||||
==== Embeddables and ImplicitNamingStrategy
|
||||
|
|
|
@ -16,7 +16,7 @@ import static java.lang.annotation.ElementType.TYPE;
|
|||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* Allows supplying a custom instantiator implementation
|
||||
* Specifies a custom instantiator implementation
|
||||
*/
|
||||
@Target( {TYPE, FIELD, METHOD, ANNOTATION_TYPE} )
|
||||
@Retention( RUNTIME )
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
|
||||
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 instantiator implementation
|
||||
*/
|
||||
@Target( {TYPE, ANNOTATION_TYPE, PACKAGE} )
|
||||
@Retention( RUNTIME )
|
||||
@Repeatable( EmbeddableInstantiatorRegistrations.class )
|
||||
public @interface EmbeddableInstantiatorRegistration {
|
||||
Class<?> embeddableClass();
|
||||
Class<? extends EmbeddableInstantiator> instantiator();
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Target( {TYPE, ANNOTATION_TYPE, PACKAGE} )
|
||||
@Retention( RUNTIME )
|
||||
public @interface EmbeddableInstantiatorRegistrations {
|
||||
EmbeddableInstantiatorRegistration[] value();
|
||||
}
|
|
@ -98,6 +98,7 @@ import org.hibernate.mapping.RootClass;
|
|||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.query.named.NamedObjectRepository;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -399,6 +400,26 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
|||
getTypeConfiguration().getJdbcTypeDescriptorRegistry().addDescriptor( typeCode, jdbcType );
|
||||
}
|
||||
|
||||
private Map<Class<?>, Class<? extends EmbeddableInstantiator>> registeredInstantiators;
|
||||
|
||||
@Override
|
||||
public void registerEmbeddableInstantiator(Class<?> embeddableType, Class<? extends EmbeddableInstantiator> instantiator) {
|
||||
if ( registeredInstantiators == null ) {
|
||||
registeredInstantiators = new HashMap<>();
|
||||
}
|
||||
registeredInstantiators.put( embeddableType, instantiator );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends EmbeddableInstantiator> findRegisteredEmbeddableInstantiator(Class<?> embeddableType) {
|
||||
if ( registeredInstantiators == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return registeredInstantiators.get( embeddableType );
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// attribute converters
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.hibernate.mapping.Join;
|
|||
import org.hibernate.mapping.MappedSuperclass;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
|
@ -319,6 +320,9 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
|
|||
void addJavaTypeRegistration(Class<?> javaType, JavaType<?> jtd);
|
||||
void addJdbcTypeRegistration(int typeCode, JdbcType jdbcType);
|
||||
|
||||
void registerEmbeddableInstantiator(Class<?> embeddableType, Class<? extends EmbeddableInstantiator> instantiator);
|
||||
Class<? extends EmbeddableInstantiator> findRegisteredEmbeddableInstantiator(Class<?> embeddableType);
|
||||
|
||||
interface DelayedPropertyReferenceHandler extends Serializable {
|
||||
void process(InFlightMetadataCollector metadataCollector);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ import org.hibernate.annotations.Columns;
|
|||
import org.hibernate.annotations.Comment;
|
||||
import org.hibernate.annotations.DiscriminatorFormula;
|
||||
import org.hibernate.annotations.DiscriminatorOptions;
|
||||
import org.hibernate.annotations.EmbeddableInstantiatorRegistration;
|
||||
import org.hibernate.annotations.EmbeddableInstantiatorRegistrations;
|
||||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchProfile;
|
||||
import org.hibernate.annotations.FetchProfiles;
|
||||
|
@ -350,6 +352,7 @@ public final class AnnotationBinder {
|
|||
}
|
||||
|
||||
handleTypeDescriptorRegistrations( pckg, context );
|
||||
bindEmbeddableInstantiatorRegistrations( pckg, context );
|
||||
|
||||
bindGenericGenerators( pckg, context );
|
||||
bindQueries( pckg, context );
|
||||
|
@ -782,6 +785,7 @@ public final class AnnotationBinder {
|
|||
// try to find class level generators
|
||||
HashMap<String, IdentifierGeneratorDefinition> classGenerators = buildGenerators( clazzToProcess, context );
|
||||
handleTypeDescriptorRegistrations( clazzToProcess, context );
|
||||
bindEmbeddableInstantiatorRegistrations( clazzToProcess, context );
|
||||
|
||||
// check properties
|
||||
final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
|
||||
|
@ -910,6 +914,36 @@ public final class AnnotationBinder {
|
|||
context.getMetadataCollector().addJavaTypeRegistration( annotation.javaType(), jtd );
|
||||
}
|
||||
|
||||
private static void bindEmbeddableInstantiatorRegistrations(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
|
||||
final ManagedBeanRegistry managedBeanRegistry = context.getBootstrapContext()
|
||||
.getServiceRegistry()
|
||||
.getService( ManagedBeanRegistry.class );
|
||||
|
||||
final EmbeddableInstantiatorRegistration instantiatorReg = annotatedElement.getAnnotation( EmbeddableInstantiatorRegistration.class );
|
||||
if ( instantiatorReg != null ) {
|
||||
handleEmbeddableInstantiatorRegistration( context, managedBeanRegistry, instantiatorReg );
|
||||
}
|
||||
else {
|
||||
final EmbeddableInstantiatorRegistrations annotation = annotatedElement.getAnnotation( EmbeddableInstantiatorRegistrations.class );
|
||||
if ( annotation != null ) {
|
||||
final EmbeddableInstantiatorRegistration[] registrations = annotation.value();
|
||||
for ( int i = 0; i < registrations.length; i++ ) {
|
||||
handleEmbeddableInstantiatorRegistration( context, managedBeanRegistry, registrations[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleEmbeddableInstantiatorRegistration(
|
||||
MetadataBuildingContext context,
|
||||
ManagedBeanRegistry managedBeanRegistry,
|
||||
EmbeddableInstantiatorRegistration annotation) {
|
||||
context.getMetadataCollector().registerEmbeddableInstantiator(
|
||||
annotation.embeddableClass(),
|
||||
annotation.instantiator()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all discriminator-related metadata per rules for "single table" inheritance
|
||||
*/
|
||||
|
@ -2309,7 +2343,7 @@ public final class AnnotationBinder {
|
|||
}
|
||||
|
||||
final AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
|
||||
final Class<? extends EmbeddableInstantiator> customInstantiatorImpl = determineCustomInstantiator( property, returnedClass );
|
||||
final Class<? extends EmbeddableInstantiator> customInstantiatorImpl = determineCustomInstantiator( property, returnedClass, context );
|
||||
|
||||
propertyBinder = bindComponent(
|
||||
inferredData,
|
||||
|
@ -2457,7 +2491,10 @@ public final class AnnotationBinder {
|
|||
}
|
||||
}
|
||||
|
||||
private static Class<? extends EmbeddableInstantiator> determineCustomInstantiator(XProperty property, XClass returnedClass) {
|
||||
private static Class<? extends EmbeddableInstantiator> determineCustomInstantiator(
|
||||
XProperty property,
|
||||
XClass returnedClass,
|
||||
MetadataBuildingContext context) {
|
||||
if ( property.isAnnotationPresent( EmbeddedId.class ) ) {
|
||||
// we don't allow custom instantiators for composite ids
|
||||
return null;
|
||||
|
@ -2473,6 +2510,11 @@ public final class AnnotationBinder {
|
|||
return classAnnotation.value();
|
||||
}
|
||||
|
||||
final Class embeddableClass = context.getBootstrapContext().getReflectionManager().toClass( returnedClass );
|
||||
if ( embeddableClass != null ) {
|
||||
return context.getMetadataCollector().findRegisteredEmbeddableInstantiator( embeddableClass );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2912,6 +2954,7 @@ public final class AnnotationBinder {
|
|||
buildingContext.getMetadataCollector().addSecondPass( secondPass );
|
||||
|
||||
handleTypeDescriptorRegistrations( property, buildingContext );
|
||||
bindEmbeddableInstantiatorRegistrations( property, buildingContext );
|
||||
}
|
||||
else {
|
||||
Map<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<>( buildGenerators( property, buildingContext ) );
|
||||
|
|
|
@ -1610,7 +1610,7 @@ public abstract class CollectionBinder {
|
|||
false,
|
||||
false,
|
||||
true,
|
||||
resolveCustomInstantiator( property, elementClass ),
|
||||
resolveCustomInstantiator( property, elementClass, buildingContext ),
|
||||
buildingContext,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
|
@ -1672,17 +1672,25 @@ public abstract class CollectionBinder {
|
|||
|
||||
}
|
||||
|
||||
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) {
|
||||
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(
|
||||
XProperty property,
|
||||
XClass propertyClass,
|
||||
MetadataBuildingContext context) {
|
||||
final org.hibernate.annotations.EmbeddableInstantiator propertyAnnotation = property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
|
||||
if ( propertyAnnotation != null ) {
|
||||
return propertyAnnotation.value();
|
||||
}
|
||||
|
||||
final org.hibernate.annotations.EmbeddableInstantiator classAnnotation = embeddableClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
|
||||
final org.hibernate.annotations.EmbeddableInstantiator classAnnotation = propertyClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
|
||||
if ( classAnnotation != null ) {
|
||||
return classAnnotation.value();
|
||||
}
|
||||
|
||||
final Class<?> embeddableClass = context.getBootstrapContext().getReflectionManager().toClass( propertyClass );
|
||||
if ( embeddableClass != null ) {
|
||||
return context.getMetadataCollector().findRegisteredEmbeddableInstantiator( embeddableClass );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.orm.test.mapping.embeddable.strategy.instantiator.registered;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@DomainModel( annotatedClasses = { Person.class, Name.class } )
|
||||
@SessionFactory
|
||||
public class InstantiationTests {
|
||||
@Test
|
||||
public void basicTest(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (session) -> {
|
||||
final Person mick = new Person( 1, new Name( "Mick", "Jagger" ) );
|
||||
session.persist( mick );
|
||||
|
||||
final Person john = new Person( 2, new Name( "John", "Doe" ) );
|
||||
john.addAlias( new Name( "Jon", "Doe" ) );
|
||||
session.persist( john );
|
||||
} );
|
||||
scope.inTransaction( (session) -> {
|
||||
final Person mick = session.createQuery( "from Person where id = 1", Person.class ).uniqueResult();
|
||||
assertThat( mick.getName().getFirstName() ).isEqualTo( "Mick" );
|
||||
} );
|
||||
scope.inTransaction( (session) -> {
|
||||
final Person john = session.createQuery( "from Person p join fetch p.aliases where p.id = 2", Person.class ).uniqueResult();
|
||||
assertThat( john.getName().getFirstName() ).isEqualTo( "John" );
|
||||
assertThat( john.getAliases() ).hasSize( 1 );
|
||||
final Name alias = john.getAliases().iterator().next();
|
||||
assertThat( alias.getFirstName() ).isEqualTo( "Jon" );
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.orm.test.mapping.embeddable.strategy.instantiator.registered;
|
||||
|
||||
//tag::embeddable-instantiator-registration[]
|
||||
public class Name {
|
||||
private final String first;
|
||||
private final String last;
|
||||
|
||||
private Name() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Name(String first, String last) {
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return last;
|
||||
}
|
||||
}
|
||||
//end::embeddable-instantiator-registration[]
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.orm.test.mapping.embeddable.strategy.instantiator.registered;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class NameInstantiator implements EmbeddableInstantiator {
|
||||
@Override
|
||||
public Object instantiate(Supplier<Object[]> valuesAccess, SessionFactoryImplementor sessionFactory) {
|
||||
final Object[] values = valuesAccess.get();
|
||||
// alphabetical
|
||||
final String first = (String) values[0];
|
||||
final String last = (String) values[1];
|
||||
return new Name( first, last );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstance(Object object, SessionFactoryImplementor sessionFactory) {
|
||||
return object instanceof Name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameClass(Object object, SessionFactoryImplementor sessionFactory) {
|
||||
return object.getClass().equals( Name.class );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.orm.test.mapping.embeddable.strategy.instantiator.registered; /**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.annotations.EmbeddableInstantiatorRegistration;
|
||||
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
|
||||
//tag::embeddable-instantiator-registration[]
|
||||
@Entity(name = "Person")
|
||||
@Table(name = "persons")
|
||||
@EmbeddableInstantiatorRegistration( embeddableClass = Name.class, instantiator = NameInstantiator.class )
|
||||
public class Person {
|
||||
@Id
|
||||
public Integer id;
|
||||
@Embedded
|
||||
public Name name;
|
||||
@ElementCollection
|
||||
@Embedded
|
||||
public Set<Name> aliases;
|
||||
|
||||
//end::embeddable-instantiator-registration[]
|
||||
|
||||
private Person() {
|
||||
// for Hibernate use
|
||||
}
|
||||
|
||||
public Person(Integer id, Name name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Name getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(Name name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Set<Name> getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public void setAliases(Set<Name> aliases) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
||||
public void addAlias(Name alias) {
|
||||
if ( aliases == null ) {
|
||||
aliases = new HashSet<>();
|
||||
}
|
||||
aliases.add( alias );
|
||||
}
|
||||
|
||||
//tag::embeddable-instantiator-registration[]
|
||||
}
|
||||
//end::embeddable-instantiator-registration[]
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for custom {@link org.hibernate.metamodel.spi.EmbeddableInstantiator} usage
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.embeddable.strategy.instantiator.registered;
|
Loading…
Reference in New Issue