HHH-14885 - New composite user-type

Working support for `@EmbeddableInstantiator` on either the embedded site or on the embeddable class.
This commit is contained in:
Steve Ebersole 2021-12-01 17:35:43 -06:00
parent 8ab27a0ff0
commit 924c2b29c3
28 changed files with 676 additions and 75 deletions

View File

@ -1,6 +1,10 @@
[[embeddables]] [[embeddables]]
=== Embeddable types === Embeddable types
:rootProjectDir: ../../../../../../..
:sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping/embeddable :sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping/embeddable
:coreProjectDir: {rootProjectDir}/hibernate-core
:coreTestSrcDir: {rootProjectDir}/hibernate-core/src/test/java
:instantiatorTestDir: {coreTestSrcDir}/org/hibernate/orm/test/mapping/embeddable/strategy/instantiator
:extrasdir: extras :extrasdir: extras
Historically Hibernate called these components. Historically Hibernate called these components.
@ -76,28 +80,22 @@ include::{sourcedir}/SimpleEmbeddableEquivalentTest.java[tag=embeddable-type-map
The composition form is certainly more object-oriented, and that becomes more evident as we work with multiple embeddable types. The composition form is certainly more object-oriented, and that becomes more evident as we work with multiple embeddable types.
[[embeddable-multiple]]
==== Multiple embeddable types
Although from an object-oriented perspective, it's much more convenient to work with embeddable types, this example doesn't work as-is.
When the same embeddable type is included multiple times in the same parent entity type, the Jakarta Persistence specification demands to set the associated column names explicitly.
This requirement is due to how object properties are mapped to database columns.
By default, Jakarta Persistence expects a database column having the same name with its associated object property.
When including multiple embeddables, the implicit name-based mapping rule doesn't work anymore because multiple object properties could end-up being mapped to the same database column.
We have a few options to handle this issue.
[[embeddable-override]] [[embeddable-override]]
==== Overriding Embeddable types ==== Overriding Embeddable types
Jakarta Persistence defines the `@AttributeOverride` annotation to handle this scenario. Although from an object-oriented perspective, it's much more convenient to work with embeddable types, when we reuse the same
This way, the mapping conflict is resolved by setting up explicit name-based property-column type mappings. embeddable multiple times on the same class, the Jakarta Persistence specification requires to set the associated column names explicitly.
If an Embeddable type is used multiple times in some entity, you need to use the This requirement is due to how object properties are mapped to database columns.
{jpaJavadocUrlPrefix}AttributeOverride.html[`@AttributeOverride`] and By default, Jakarta Persistence expects a database column having the same name with its associated object property.
{jpaJavadocUrlPrefix}AssociationOverride.html[`@AssociationOverride`] annotations When including multiple embeddables, the implicit name-based mapping rule doesn't work anymore because multiple object
to override the default column names defined by the Embeddable. properties could end-up being mapped to the same database column.
When an embeddable type is used multiple times, Jakarta Persistence defines the `@AttributeOverride`
and `@AssociationOverride` annotations to handle this scenario to override the default column names defined
by the Embeddable.
NOTE: See <<embeddable-multiple-namingstrategy>> for an alternative to using `@AttributeOverride` and `@AssociationOverride`
Considering you have the following `Publisher` embeddable type Considering you have the following `Publisher` embeddable type
which defines a `@ManyToOne` association with the `Country` entity: which defines a `@ManyToOne` association with the `Country` entity:
@ -135,46 +133,6 @@ include::{extrasdir}/embeddable/embeddable-type-override-mapping-example.sql[]
---- ----
==== ====
[[embeddable-multiple-namingstrategy]]
==== Embeddables and ImplicitNamingStrategy
[IMPORTANT]
====
The `ImplicitNamingStrategyComponentPathImpl` is a Hibernate-specific feature.
Users concerned with Jakarta Persistence provider portability should instead prefer explicit column naming with `@AttributeOverride`.
====
Hibernate naming strategies are covered in detail in <<chapters/domain/naming.adoc#naming,Naming>>.
However, for the purposes of this discussion, Hibernate has the capability to interpret implicit column names in a way that is safe for use with multiple embeddable types.
[[embeddable-multiple-namingstrategy-entity-mapping]]
.Implicit multiple embeddable type mapping
====
[source,java]
----
include::{sourcedir}/EmbeddableImplicitOverrideTest.java[tag=embeddable-multiple-namingstrategy-entity-mapping, indent=0]
----
====
To make it work, you need to use the `ImplicitNamingStrategyComponentPathImpl` naming strategy.
[[embeddable-multiple-ImplicitNamingStrategyComponentPathImpl]]
.Enabling implicit embeddable type mapping using the component path naming strategy
====
[source,java]
----
include::{sourcedir}/EmbeddableImplicitOverrideTest.java[tag=embeddable-multiple-ImplicitNamingStrategyComponentPathImpl, indent=0]
----
====
Now the "path" to attributes are used in the implicit column naming:
[source,sql]
----
include::{extrasdir}/embeddable/embeddable-multiple-namingstrategy-entity-mapping.sql[]
----
You could even develop your own naming strategy to do other types of implicit naming strategies.
[[embeddable-collections]] [[embeddable-collections]]
==== Collections of embeddable types ==== Collections of embeddable types
@ -293,3 +251,81 @@ include::{sourcedir}/ParentTest.java[tags=embeddable-Parent-fetching-example]
==== ====
Therefore, the `@Parent` annotation is used to define the association between an embeddable type and the owning entity. Therefore, the `@Parent` annotation is used to define the association between an embeddable type and the owning entity.
[[embeddable-instantiator]]
==== Custom instantiation
Jakarta Persistence requires embeddable classes to follow Java Bean conventions. Part of this is the
definition of a non-arg constructor. However, not all value compositions applications might map as embeddable
values follow Java Bean conventions - e.g. a struct or Java 15 record.
Hibernate allows the use of a custom instantiator for creating the embeddable instances through the
`org.hibernate.metamodel.spi.EmbeddableInstantiator` contract. The custom instantiator is specified
using the `@org.hibernate.annotations.EmbeddableInstantiator` annotation. Which can either be defined
on the property:
[[embeddable-instantiator-property-ex]]
.`@EmbeddableInstantiator` on attribute
====
[source, JAVA, indent=0]
----
include::{instantiatorTestDir}/embedded/Name.java[tags=embeddable-instantiator-property]
include::{instantiatorTestDir}/embedded/Person.java[tags=embeddable-instantiator-property]
----
====
or on the embeddable class:
[[embeddable-instantiator-class-ex]]
.`@EmbeddableInstantiator` on class
====
[source, JAVA, indent=0]
----
include::{instantiatorTestDir}/embeddable/Name.java[tags=embeddable-instantiator-class]
include::{instantiatorTestDir}/embeddable/Person.java[tags=embeddable-instantiator-class]
----
====
[[embeddable-multiple-namingstrategy]]
==== Embeddables and ImplicitNamingStrategy
[IMPORTANT]
====
The `ImplicitNamingStrategyComponentPathImpl` is a Hibernate-specific feature.
Users concerned with Jakarta Persistence provider portability should instead prefer explicit column naming with `@AttributeOverride`.
====
Hibernate naming strategies are covered in detail in <<chapters/domain/naming.adoc#naming,Naming>>.
However, for the purposes of this discussion, Hibernate has the capability to interpret implicit column names in a way that is safe for use with multiple embeddable types.
[[embeddable-multiple-namingstrategy-entity-mapping]]
.Implicit multiple embeddable type mapping
====
[source,java]
----
include::{sourcedir}/EmbeddableImplicitOverrideTest.java[tag=embeddable-multiple-namingstrategy-entity-mapping, indent=0]
----
====
To make it work, you need to use the `ImplicitNamingStrategyComponentPathImpl` naming strategy.
[[embeddable-multiple-ImplicitNamingStrategyComponentPathImpl]]
.Enabling implicit embeddable type mapping using the component path naming strategy
====
[source,java]
----
include::{sourcedir}/EmbeddableImplicitOverrideTest.java[tag=embeddable-multiple-ImplicitNamingStrategyComponentPathImpl, indent=0]
----
====
Now the "path" to attributes are used in the implicit column naming:
[source,sql]
----
include::{extrasdir}/embeddable/embeddable-multiple-namingstrategy-entity-mapping.sql[]
----
You could even develop your own naming strategy to do other types of implicit naming strategies.

View File

@ -0,0 +1,25 @@
/*
* 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.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Allows supplying a custom instantiator implementation
*/
@Target( {TYPE, FIELD, METHOD, ANNOTATION_TYPE} )
@Retention( RUNTIME )
public @interface EmbeddableInstantiator {
Class<? extends org.hibernate.metamodel.spi.EmbeddableInstantiator> value();
}

View File

@ -120,6 +120,7 @@ import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.ToOne; import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass; import org.hibernate.mapping.UnionSubclass;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.descriptor.java.BasicJavaType; import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
@ -1158,6 +1159,7 @@ public final class AnnotationBinder {
true, true,
true, true,
false, false,
null,
context, context,
inheritanceStatePerClass inheritanceStatePerClass
); );
@ -2286,6 +2288,11 @@ public final class AnnotationBinder {
} }
} }
if ( ! isComponent ) {
if ( property.isAnnotationPresent( Embedded.class ) ) {
}
}
isComponent = isComponent isComponent = isComponent
|| property.isAnnotationPresent( Embedded.class ) || property.isAnnotationPresent( Embedded.class )
|| property.isAnnotationPresent( EmbeddedId.class ) || property.isAnnotationPresent( EmbeddedId.class )
@ -2300,7 +2307,10 @@ public final class AnnotationBinder {
); );
referencedEntityName = mapsIdProperty.getClassOrElementName(); referencedEntityName = mapsIdProperty.getClassOrElementName();
} }
AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
final AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
final Class<? extends EmbeddableInstantiator> customInstantiatorImpl = determineCustomInstantiator( property, returnedClass );
propertyBinder = bindComponent( propertyBinder = bindComponent(
inferredData, inferredData,
propertyHolder, propertyHolder,
@ -2312,6 +2322,7 @@ public final class AnnotationBinder {
isId, isId,
inheritanceStatePerClass, inheritanceStatePerClass,
referencedEntityName, referencedEntityName,
customInstantiatorImpl,
isOverridden ? ( Ejb3JoinColumn[] ) columns : null isOverridden ? ( Ejb3JoinColumn[] ) columns : null
); );
} }
@ -2446,6 +2457,25 @@ public final class AnnotationBinder {
} }
} }
private static Class<? extends EmbeddableInstantiator> determineCustomInstantiator(XProperty property, XClass returnedClass) {
if ( property.isAnnotationPresent( EmbeddedId.class ) ) {
// we don't allow custom instantiators for composite ids
return null;
}
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 = returnedClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
if ( classAnnotation != null ) {
return classAnnotation.value();
}
return null;
}
private static boolean isGlobalGeneratorNameGlobal(MetadataBuildingContext context) { private static boolean isGlobalGeneratorNameGlobal(MetadataBuildingContext context) {
return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled(); return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled();
} }
@ -2659,10 +2689,11 @@ public final class AnnotationBinder {
boolean isId, //is an identifier boolean isId, //is an identifier
Map<XClass, InheritanceState> inheritanceStatePerClass, Map<XClass, InheritanceState> inheritanceStatePerClass,
String referencedEntityName, //is a component who is overridden by a @MapsId String referencedEntityName, //is a component who is overridden by a @MapsId
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
Ejb3JoinColumn[] columns) { Ejb3JoinColumn[] columns) {
Component comp; Component comp;
if ( referencedEntityName != null ) { if ( referencedEntityName != null ) {
comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, buildingContext ); comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, customInstantiatorImpl, buildingContext );
SecondPass sp = new CopyIdentifierComponentSecondPass( SecondPass sp = new CopyIdentifierComponentSecondPass(
comp, comp,
referencedEntityName, referencedEntityName,
@ -2675,7 +2706,7 @@ public final class AnnotationBinder {
comp = fillComponent( comp = fillComponent(
propertyHolder, inferredData, propertyAccessor, !isId, entityBinder, propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
isComponentEmbedded, isIdentifierMapper, isComponentEmbedded, isIdentifierMapper,
false, buildingContext, inheritanceStatePerClass false, customInstantiatorImpl, buildingContext, inheritanceStatePerClass
); );
} }
if ( isId ) { if ( isId ) {
@ -2721,6 +2752,7 @@ public final class AnnotationBinder {
boolean isComponentEmbedded, boolean isComponentEmbedded,
boolean isIdentifierMapper, boolean isIdentifierMapper,
boolean inSecondPass, boolean inSecondPass,
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
MetadataBuildingContext buildingContext, MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) { Map<XClass, InheritanceState> inheritanceStatePerClass) {
return fillComponent( return fillComponent(
@ -2733,6 +2765,7 @@ public final class AnnotationBinder {
isComponentEmbedded, isComponentEmbedded,
isIdentifierMapper, isIdentifierMapper,
inSecondPass, inSecondPass,
customInstantiatorImpl,
buildingContext, buildingContext,
inheritanceStatePerClass inheritanceStatePerClass
); );
@ -2748,6 +2781,7 @@ public final class AnnotationBinder {
boolean isComponentEmbedded, boolean isComponentEmbedded,
boolean isIdentifierMapper, boolean isIdentifierMapper,
boolean inSecondPass, boolean inSecondPass,
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
MetadataBuildingContext buildingContext, MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) { Map<XClass, InheritanceState> inheritanceStatePerClass) {
/** /**
@ -2755,7 +2789,8 @@ public final class AnnotationBinder {
* Because it's a value type, there is no bidirectional association, hence second pass * Because it's a value type, there is no bidirectional association, hence second pass
* ordering does not matter * ordering does not matter
*/ */
Component comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, buildingContext ); Component comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, customInstantiatorImpl, buildingContext );
String subpath = BinderHelper.getPath( propertyHolder, inferredData ); String subpath = BinderHelper.getPath( propertyHolder, inferredData );
LOG.tracev( "Binding component with path: {0}", subpath ); LOG.tracev( "Binding component with path: {0}", subpath );
PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder( PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(
@ -2899,6 +2934,7 @@ public final class AnnotationBinder {
PropertyData inferredData, PropertyData inferredData,
boolean isComponentEmbedded, boolean isComponentEmbedded,
boolean isIdentifierMapper, boolean isIdentifierMapper,
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
MetadataBuildingContext context) { MetadataBuildingContext context) {
Component comp = new Component( context, propertyHolder.getPersistentClass() ); Component comp = new Component( context, propertyHolder.getPersistentClass() );
comp.setEmbedded( isComponentEmbedded ); comp.setEmbedded( isComponentEmbedded );
@ -2911,6 +2947,7 @@ public final class AnnotationBinder {
else { else {
comp.setComponentClassName( inferredData.getClassOrElementName() ); comp.setComponentClassName( inferredData.getClassOrElementName() );
} }
comp.setCustomInstantiator( customInstantiatorImpl );
return comp; return comp;
} }
@ -2954,6 +2991,7 @@ public final class AnnotationBinder {
isEmbedded, isEmbedded,
isIdentifierMapper, isIdentifierMapper,
false, false,
null,
buildingContext, buildingContext,
inheritanceStatePerClass inheritanceStatePerClass
); );

View File

@ -93,6 +93,7 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -1597,6 +1598,7 @@ public abstract class CollectionBinder {
inferredData = new PropertyPreloadedData( AccessType.PROPERTY, "collection&&element", elementClass ); inferredData = new PropertyPreloadedData( AccessType.PROPERTY, "collection&&element", elementClass );
} }
} }
//TODO be smart with isNullable //TODO be smart with isNullable
boolean isNullable = true; boolean isNullable = true;
Component component = AnnotationBinder.fillComponent( Component component = AnnotationBinder.fillComponent(
@ -1608,6 +1610,7 @@ public abstract class CollectionBinder {
false, false,
false, false,
true, true,
resolveCustomInstantiator( property, elementClass ),
buildingContext, buildingContext,
inheritanceStatePerClass inheritanceStatePerClass
); );
@ -1669,6 +1672,20 @@ public abstract class CollectionBinder {
} }
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) {
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 );
if ( classAnnotation != null ) {
return classAnnotation.value();
}
return null;
}
private String extractHqlOrderBy(jakarta.persistence.OrderBy jpaOrderBy) { private String extractHqlOrderBy(jakarta.persistence.OrderBy jpaOrderBy) {
if ( jpaOrderBy != null ) { if ( jpaOrderBy != null ) {
return jpaOrderBy.value(); // Null not possible. In case of empty expression, apply default ordering. return jpaOrderBy.value(); // Null not possible. In case of empty expression, apply default ordering.

View File

@ -296,6 +296,7 @@ public class MapBinder extends CollectionBinder {
false, false,
false, false,
true, true,
null,
buildingContext, buildingContext,
inheritanceStatePerClass inheritanceStatePerClass
); );

View File

@ -41,6 +41,7 @@ import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne; import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.property.access.spi.PropertyAccessStrategy;
import org.hibernate.tuple.AnnotationValueGeneration; import org.hibernate.tuple.AnnotationValueGeneration;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.GenerationTiming;
@ -222,6 +223,7 @@ public class PropertyBinder {
new PropertyPreloadedData(null, null, null), new PropertyPreloadedData(null, null, null),
true, true,
false, false,
resolveCustomInstantiator( property, returnedClass ),
buildingContext buildingContext
); );
rootClass.setIdentifier( identifier ); rootClass.setIdentifier( identifier );
@ -260,6 +262,20 @@ public class PropertyBinder {
return prop; return prop;
} }
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) {
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 );
if ( classAnnotation != null ) {
return classAnnotation.value();
}
return null;
}
//used when the value is provided and the binding is done elsewhere //used when the value is provided and the binding is done elsewhere
public Property makeProperty() { public Property makeProperty() {
validateMake(); validateMake();

View File

@ -29,6 +29,7 @@ import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.JoinedIterator; import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.metamodel.RepresentationMode; import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.component.ComponentMetamodel; import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.ComponentType; import org.hibernate.type.ComponentType;
@ -54,6 +55,7 @@ public class Component extends SimpleValue implements MetaAttributable {
private boolean isKey; private boolean isKey;
private String roleName; private String roleName;
private Class<? extends EmbeddableInstantiator> customInstantiator;
private Map<RepresentationMode,String> tuplizerImpls; private Map<RepresentationMode,String> tuplizerImpls;
// cache the status of the type // cache the status of the type
@ -576,4 +578,11 @@ public class Component extends SimpleValue implements MetaAttributable {
return this.originalPropertyOrder = originalPropertyOrder; return this.originalPropertyOrder = originalPropertyOrder;
} }
public Class<? extends EmbeddableInstantiator> getCustomInstantiator() {
return customInstantiator;
}
public void setCustomInstantiator(Class<? extends EmbeddableInstantiator> customInstantiator) {
this.customInstantiator = customInstantiator;
}
} }

View File

@ -12,7 +12,6 @@ import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
/** /**
* Support for instantiating embeddables as dynamic-map representation * Support for instantiating embeddables as dynamic-map representation
@ -21,7 +20,7 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator;
*/ */
public class EmbeddableInstantiatorDynamicMap public class EmbeddableInstantiatorDynamicMap
extends AbstractDynamicMapInstantiator extends AbstractDynamicMapInstantiator
implements EmbeddableInstantiator { implements StandardEmbeddableInstantiator {
private final Supplier<EmbeddableMappingType> runtimeDescriptorAccess; private final Supplier<EmbeddableMappingType> runtimeDescriptorAccess;
public EmbeddableInstantiatorDynamicMap( public EmbeddableInstantiatorDynamicMap(

View File

@ -10,7 +10,6 @@ import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import static org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimizer; import static org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimizer;
@ -19,7 +18,7 @@ import static org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimi
* Support for instantiating embeddables as POJO representation * Support for instantiating embeddables as POJO representation
* using bytecode optimizer * using bytecode optimizer
*/ */
public class EmbeddableInstantiatorPojoOptimized extends AbstractPojoInstantiator implements EmbeddableInstantiator { public class EmbeddableInstantiatorPojoOptimized extends AbstractPojoInstantiator implements StandardEmbeddableInstantiator {
private final Supplier<EmbeddableMappingType> embeddableMappingAccess; private final Supplier<EmbeddableMappingType> embeddableMappingAccess;
private final InstantiationOptimizer instantiationOptimizer; private final InstantiationOptimizer instantiationOptimizer;

View File

@ -16,13 +16,12 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
/** /**
* Support for instantiating embeddables as POJO representation * Support for instantiating embeddables as POJO representation
*/ */
public class EmbeddableInstantiatorPojoStandard extends AbstractPojoInstantiator implements EmbeddableInstantiator { public class EmbeddableInstantiatorPojoStandard extends AbstractPojoInstantiator implements StandardEmbeddableInstantiator {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( PojoInstantiatorImpl.class ); private static final CoreMessageLogger LOG = CoreLogging.messageLogger( PojoInstantiatorImpl.class );
private final Supplier<EmbeddableMappingType> embeddableMappingAccess; private final Supplier<EmbeddableMappingType> embeddableMappingAccess;

View File

@ -10,12 +10,11 @@ import java.util.function.Supplier;
import org.hibernate.bytecode.spi.BasicProxyFactory; import org.hibernate.bytecode.spi.BasicProxyFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
/** /**
* EmbeddableInstantiator used for instantiating "proxies" of an embeddable. * EmbeddableInstantiator used for instantiating "proxies" of an embeddable.
*/ */
public class EmbeddableInstantiatorProxied implements EmbeddableInstantiator { public class EmbeddableInstantiatorProxied implements StandardEmbeddableInstantiator {
private final Class<?> proxiedClass; private final Class<?> proxiedClass;
private final BasicProxyFactory factory; private final BasicProxyFactory factory;

View File

@ -31,9 +31,12 @@ public class EmbeddableRepresentationStrategyMap implements EmbeddableRepresenta
public EmbeddableRepresentationStrategyMap( public EmbeddableRepresentationStrategyMap(
Component bootDescriptor, Component bootDescriptor,
Supplier<EmbeddableMappingType> runtimeDescriptorAccess, Supplier<EmbeddableMappingType> runtimeDescriptorAccess,
EmbeddableInstantiator customInstantiator,
RuntimeModelCreationContext creationContext) { RuntimeModelCreationContext creationContext) {
this.mapJtd = creationContext.getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Map.class ); this.mapJtd = creationContext.getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Map.class );
this.instantiator = new EmbeddableInstantiatorDynamicMap( bootDescriptor, runtimeDescriptorAccess ); this.instantiator = customInstantiator != null
? customInstantiator
: new EmbeddableInstantiatorDynamicMap( bootDescriptor, runtimeDescriptorAccess );
} }
@Override @Override

View File

@ -44,6 +44,7 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
public EmbeddableRepresentationStrategyPojo( public EmbeddableRepresentationStrategyPojo(
Component bootDescriptor, Component bootDescriptor,
Supplier<EmbeddableMappingType> runtimeDescriptorAccess, Supplier<EmbeddableMappingType> runtimeDescriptorAccess,
EmbeddableInstantiator customInstantiator,
RuntimeModelCreationContext creationContext) { RuntimeModelCreationContext creationContext) {
super( super(
bootDescriptor, bootDescriptor,
@ -69,7 +70,9 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
false false
); );
this.instantiator = determineInstantiator( bootDescriptor, runtimeDescriptorAccess, creationContext ); this.instantiator = customInstantiator != null
? customInstantiator
: determineInstantiator( bootDescriptor, runtimeDescriptorAccess, creationContext );
} }
private EmbeddableInstantiator determineInstantiator( private EmbeddableInstantiator determineInstantiator(

View File

@ -114,6 +114,8 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
mapsIdRepresentationStrategy = new EmbeddableRepresentationStrategyPojo( mapsIdRepresentationStrategy = new EmbeddableRepresentationStrategyPojo(
bootDescriptor.getIdentifierMapper(), bootDescriptor.getIdentifierMapper(),
() -> ( ( CompositeTypeImplementor) bootDescriptor.getIdentifierMapper().getType() ).getMappingModelPart().getEmbeddableTypeDescriptor(), () -> ( ( CompositeTypeImplementor) bootDescriptor.getIdentifierMapper().getType() ).getMappingModelPart().getEmbeddableTypeDescriptor(),
// we currently do not support custom instantiators for identifiers
null,
creationContext creationContext
); );
} }
@ -121,6 +123,8 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
mapsIdRepresentationStrategy = new EmbeddableRepresentationStrategyPojo( mapsIdRepresentationStrategy = new EmbeddableRepresentationStrategyPojo(
(Component) bootDescriptorIdentifier, (Component) bootDescriptorIdentifier,
() -> ( ( CompositeTypeImplementor) bootDescriptor.getIdentifierMapper().getType() ).getMappingModelPart().getEmbeddableTypeDescriptor(), () -> ( ( CompositeTypeImplementor) bootDescriptor.getIdentifierMapper().getType() ).getMappingModelPart().getEmbeddableTypeDescriptor(),
// we currently do not support custom instantiators for identifiers
null,
creationContext creationContext
); );
} }

View File

@ -13,11 +13,13 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.RepresentationMode; import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver; import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -74,8 +76,26 @@ public class ManagedTypeRepresentationResolverStandard implements ManagedTypeRep
} }
} }
final EmbeddableInstantiator customInstantiator;
if ( bootDescriptor.getCustomInstantiator() != null ) {
final Class<? extends EmbeddableInstantiator> customInstantiatorImpl = bootDescriptor.getCustomInstantiator();
customInstantiator = creationContext.getBootstrapContext()
.getServiceRegistry()
.getService( ManagedBeanRegistry.class )
.getBean( customInstantiatorImpl )
.getBeanInstance();
}
else {
customInstantiator = null;
}
if ( representation == RepresentationMode.MAP ) { if ( representation == RepresentationMode.MAP ) {
return new EmbeddableRepresentationStrategyMap( bootDescriptor, runtimeDescriptorAccess, creationContext ); return new EmbeddableRepresentationStrategyMap(
bootDescriptor,
runtimeDescriptorAccess,
customInstantiator,
creationContext
);
} }
else { else {
// todo (6.0) : fix this // todo (6.0) : fix this
@ -84,7 +104,12 @@ public class ManagedTypeRepresentationResolverStandard implements ManagedTypeRep
// //
// instead, resolve ReflectionOptimizer once - here - and pass along to // instead, resolve ReflectionOptimizer once - here - and pass along to
// StandardPojoRepresentationStrategy // StandardPojoRepresentationStrategy
return new EmbeddableRepresentationStrategyPojo( bootDescriptor, runtimeDescriptorAccess, creationContext ); return new EmbeddableRepresentationStrategyPojo(
bootDescriptor,
runtimeDescriptorAccess,
customInstantiator,
creationContext
);
} }
} }
} }

View File

@ -0,0 +1,17 @@
/*
* 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.metamodel.internal;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
/**
* Marker interface for standard EmbeddableInstantiator implementations.
*
* This allows us to recognize custom instantiators
*/
public interface StandardEmbeddableInstantiator extends EmbeddableInstantiator {
}

View File

@ -37,6 +37,7 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.UnsupportedMappingException; import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.internal.StandardEmbeddableInstantiator;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
@ -45,6 +46,7 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingModelCreationLogger; import org.hibernate.metamodel.mapping.MappingModelCreationLogger;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonTransientException;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
@ -129,6 +131,9 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
} }
} }
catch (Exception e) { catch (Exception e) {
if ( e instanceof NonTransientException ) {
throw e;
}
MappingModelCreationLogger.LOGGER.debugf( MappingModelCreationLogger.LOGGER.debugf(
e, e,
"(DEBUG) Error finalizing EmbeddableMappingType(%s)", "(DEBUG) Error finalizing EmbeddableMappingType(%s)",
@ -550,6 +555,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
"EmbeddableMappingType(" + getEmbeddedValueMapping().getNavigableRole().getFullPath() + ")#initColumnMappings", "EmbeddableMappingType(" + getEmbeddedValueMapping().getNavigableRole().getFullPath() + ")#initColumnMappings",
this::initColumnMappings this::initColumnMappings
); );
return true; return true;
} }

View File

@ -12,6 +12,7 @@ import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.metamodel.RepresentationMode; import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.internal.StandardEmbeddableInstantiator;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
@ -60,7 +61,7 @@ public class VirtualIdRepresentationStrategy implements EmbeddableRepresentation
); );
} }
private static class InstantiatorAdapter implements EmbeddableInstantiator { private static class InstantiatorAdapter implements StandardEmbeddableInstantiator {
private final VirtualIdEmbeddable virtualIdEmbeddable; private final VirtualIdEmbeddable virtualIdEmbeddable;
private final EntityInstantiator entityInstantiator; private final EntityInstantiator entityInstantiator;

View File

@ -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.embeddable;
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" );
} );
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.embeddable;
import org.hibernate.annotations.EmbeddableInstantiator;
//tag::embeddable-instantiator-class[]
@EmbeddableInstantiator( NameInstantiator.class )
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-class[]

View File

@ -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.embeddable;
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 );
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.embeddable; /**
* @author Steve Ebersole
*/
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity(name = "Person")
@Table(name = "persons")
//tag::embeddable-instantiator-class[]
public class Person {
@Id
public Integer id;
@Embedded
public Name name;
@ElementCollection
@Embedded
public Set<Name> aliases;
//end::embeddable-instantiator-class[]
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-class[]
}
//end::embeddable-instantiator-class[]

View File

@ -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.embeddable;

View File

@ -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.embedded;
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" );
} );
}
}

View File

@ -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.embedded;
//tag::embeddable-instantiator-property[]
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-property[]

View File

@ -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.embedded;
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 );
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.embedded; /**
* @author Steve Ebersole
*/
import java.util.HashSet;
import java.util.Set;
import org.hibernate.annotations.EmbeddableInstantiator;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
//tag::embeddable-instantiator-property[]
@Entity(name = "Person")
@Table(name = "persons")
public class Person {
@Id
public Integer id;
@Embedded
@EmbeddableInstantiator( NameInstantiator.class )
public Name name;
@ElementCollection
@Embedded
@EmbeddableInstantiator( NameInstantiator.class )
public Set<Name> aliases;
//end::embeddable-instantiator-property[]
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-property[]
}
//end::embeddable-instantiator-property[]

View File

@ -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.embedded;