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:
parent
8ab27a0ff0
commit
924c2b29c3
|
@ -1,6 +1,10 @@
|
|||
[[embeddables]]
|
||||
=== Embeddable types
|
||||
:rootProjectDir: ../../../../../../..
|
||||
: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
|
||||
|
||||
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.
|
||||
|
||||
[[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]]
|
||||
==== Overriding Embeddable types
|
||||
|
||||
Jakarta Persistence defines the `@AttributeOverride` annotation to handle this scenario.
|
||||
This way, the mapping conflict is resolved by setting up explicit name-based property-column type mappings.
|
||||
Although from an object-oriented perspective, it's much more convenient to work with embeddable types, when we reuse the same
|
||||
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
|
||||
{jpaJavadocUrlPrefix}AttributeOverride.html[`@AttributeOverride`] and
|
||||
{jpaJavadocUrlPrefix}AssociationOverride.html[`@AssociationOverride`] annotations
|
||||
to override the default column names defined by the Embeddable.
|
||||
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.
|
||||
|
||||
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
|
||||
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]]
|
||||
==== 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.
|
||||
|
||||
|
||||
[[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.
|
|
@ -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();
|
||||
}
|
|
@ -120,6 +120,7 @@ import org.hibernate.mapping.SingleTableSubclass;
|
|||
import org.hibernate.mapping.Subclass;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.mapping.UnionSubclass;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
@ -1158,6 +1159,7 @@ public final class AnnotationBinder {
|
|||
true,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
context,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
|
@ -2286,6 +2288,11 @@ public final class AnnotationBinder {
|
|||
}
|
||||
}
|
||||
|
||||
if ( ! isComponent ) {
|
||||
if ( property.isAnnotationPresent( Embedded.class ) ) {
|
||||
|
||||
}
|
||||
}
|
||||
isComponent = isComponent
|
||||
|| property.isAnnotationPresent( Embedded.class )
|
||||
|| property.isAnnotationPresent( EmbeddedId.class )
|
||||
|
@ -2300,7 +2307,10 @@ public final class AnnotationBinder {
|
|||
);
|
||||
referencedEntityName = mapsIdProperty.getClassOrElementName();
|
||||
}
|
||||
AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
|
||||
|
||||
final AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
|
||||
final Class<? extends EmbeddableInstantiator> customInstantiatorImpl = determineCustomInstantiator( property, returnedClass );
|
||||
|
||||
propertyBinder = bindComponent(
|
||||
inferredData,
|
||||
propertyHolder,
|
||||
|
@ -2312,6 +2322,7 @@ public final class AnnotationBinder {
|
|||
isId,
|
||||
inheritanceStatePerClass,
|
||||
referencedEntityName,
|
||||
customInstantiatorImpl,
|
||||
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) {
|
||||
return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled();
|
||||
}
|
||||
|
@ -2659,10 +2689,11 @@ public final class AnnotationBinder {
|
|||
boolean isId, //is an identifier
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass,
|
||||
String referencedEntityName, //is a component who is overridden by a @MapsId
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
Ejb3JoinColumn[] columns) {
|
||||
Component comp;
|
||||
if ( referencedEntityName != null ) {
|
||||
comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, buildingContext );
|
||||
comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper, customInstantiatorImpl, buildingContext );
|
||||
SecondPass sp = new CopyIdentifierComponentSecondPass(
|
||||
comp,
|
||||
referencedEntityName,
|
||||
|
@ -2675,7 +2706,7 @@ public final class AnnotationBinder {
|
|||
comp = fillComponent(
|
||||
propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
|
||||
isComponentEmbedded, isIdentifierMapper,
|
||||
false, buildingContext, inheritanceStatePerClass
|
||||
false, customInstantiatorImpl, buildingContext, inheritanceStatePerClass
|
||||
);
|
||||
}
|
||||
if ( isId ) {
|
||||
|
@ -2721,6 +2752,7 @@ public final class AnnotationBinder {
|
|||
boolean isComponentEmbedded,
|
||||
boolean isIdentifierMapper,
|
||||
boolean inSecondPass,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
MetadataBuildingContext buildingContext,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass) {
|
||||
return fillComponent(
|
||||
|
@ -2733,6 +2765,7 @@ public final class AnnotationBinder {
|
|||
isComponentEmbedded,
|
||||
isIdentifierMapper,
|
||||
inSecondPass,
|
||||
customInstantiatorImpl,
|
||||
buildingContext,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
|
@ -2748,6 +2781,7 @@ public final class AnnotationBinder {
|
|||
boolean isComponentEmbedded,
|
||||
boolean isIdentifierMapper,
|
||||
boolean inSecondPass,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
MetadataBuildingContext buildingContext,
|
||||
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
|
||||
* 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 );
|
||||
LOG.tracev( "Binding component with path: {0}", subpath );
|
||||
PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(
|
||||
|
@ -2899,6 +2934,7 @@ public final class AnnotationBinder {
|
|||
PropertyData inferredData,
|
||||
boolean isComponentEmbedded,
|
||||
boolean isIdentifierMapper,
|
||||
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
|
||||
MetadataBuildingContext context) {
|
||||
Component comp = new Component( context, propertyHolder.getPersistentClass() );
|
||||
comp.setEmbedded( isComponentEmbedded );
|
||||
|
@ -2911,6 +2947,7 @@ public final class AnnotationBinder {
|
|||
else {
|
||||
comp.setComponentClassName( inferredData.getClassOrElementName() );
|
||||
}
|
||||
comp.setCustomInstantiator( customInstantiatorImpl );
|
||||
return comp;
|
||||
}
|
||||
|
||||
|
@ -2954,6 +2991,7 @@ public final class AnnotationBinder {
|
|||
isEmbedded,
|
||||
isIdentifierMapper,
|
||||
false,
|
||||
null,
|
||||
buildingContext,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
|
|
|
@ -93,6 +93,7 @@ import org.hibernate.mapping.Property;
|
|||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -1597,6 +1598,7 @@ public abstract class CollectionBinder {
|
|||
inferredData = new PropertyPreloadedData( AccessType.PROPERTY, "collection&&element", elementClass );
|
||||
}
|
||||
}
|
||||
|
||||
//TODO be smart with isNullable
|
||||
boolean isNullable = true;
|
||||
Component component = AnnotationBinder.fillComponent(
|
||||
|
@ -1608,6 +1610,7 @@ public abstract class CollectionBinder {
|
|||
false,
|
||||
false,
|
||||
true,
|
||||
resolveCustomInstantiator( property, elementClass ),
|
||||
buildingContext,
|
||||
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) {
|
||||
if ( jpaOrderBy != null ) {
|
||||
return jpaOrderBy.value(); // Null not possible. In case of empty expression, apply default ordering.
|
||||
|
|
|
@ -296,6 +296,7 @@ public class MapBinder extends CollectionBinder {
|
|||
false,
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
buildingContext,
|
||||
inheritanceStatePerClass
|
||||
);
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.hibernate.mapping.RootClass;
|
|||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.property.access.spi.PropertyAccessStrategy;
|
||||
import org.hibernate.tuple.AnnotationValueGeneration;
|
||||
import org.hibernate.tuple.GenerationTiming;
|
||||
|
@ -222,6 +223,7 @@ public class PropertyBinder {
|
|||
new PropertyPreloadedData(null, null, null),
|
||||
true,
|
||||
false,
|
||||
resolveCustomInstantiator( property, returnedClass ),
|
||||
buildingContext
|
||||
);
|
||||
rootClass.setIdentifier( identifier );
|
||||
|
@ -260,6 +262,20 @@ public class PropertyBinder {
|
|||
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
|
||||
public Property makeProperty() {
|
||||
validateMake();
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.id.factory.IdentifierGeneratorFactory;
|
|||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.internal.util.collections.JoinedIterator;
|
||||
import org.hibernate.metamodel.RepresentationMode;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
import org.hibernate.tuple.component.ComponentMetamodel;
|
||||
import org.hibernate.type.ComponentType;
|
||||
|
@ -54,6 +55,7 @@ public class Component extends SimpleValue implements MetaAttributable {
|
|||
private boolean isKey;
|
||||
private String roleName;
|
||||
|
||||
private Class<? extends EmbeddableInstantiator> customInstantiator;
|
||||
private Map<RepresentationMode,String> tuplizerImpls;
|
||||
|
||||
// cache the status of the type
|
||||
|
@ -576,4 +578,11 @@ public class Component extends SimpleValue implements MetaAttributable {
|
|||
return this.originalPropertyOrder = originalPropertyOrder;
|
||||
}
|
||||
|
||||
public Class<? extends EmbeddableInstantiator> getCustomInstantiator() {
|
||||
return customInstantiator;
|
||||
}
|
||||
|
||||
public void setCustomInstantiator(Class<? extends EmbeddableInstantiator> customInstantiator) {
|
||||
this.customInstantiator = customInstantiator;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.function.Supplier;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
|
||||
/**
|
||||
* Support for instantiating embeddables as dynamic-map representation
|
||||
|
@ -21,7 +20,7 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
|||
*/
|
||||
public class EmbeddableInstantiatorDynamicMap
|
||||
extends AbstractDynamicMapInstantiator
|
||||
implements EmbeddableInstantiator {
|
||||
implements StandardEmbeddableInstantiator {
|
||||
private final Supplier<EmbeddableMappingType> runtimeDescriptorAccess;
|
||||
|
||||
public EmbeddableInstantiatorDynamicMap(
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
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
|
||||
* using bytecode optimizer
|
||||
*/
|
||||
public class EmbeddableInstantiatorPojoOptimized extends AbstractPojoInstantiator implements EmbeddableInstantiator {
|
||||
public class EmbeddableInstantiatorPojoOptimized extends AbstractPojoInstantiator implements StandardEmbeddableInstantiator {
|
||||
private final Supplier<EmbeddableMappingType> embeddableMappingAccess;
|
||||
private final InstantiationOptimizer instantiationOptimizer;
|
||||
|
||||
|
|
|
@ -16,13 +16,12 @@ import org.hibernate.internal.CoreLogging;
|
|||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
* 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 final Supplier<EmbeddableMappingType> embeddableMappingAccess;
|
||||
|
|
|
@ -10,12 +10,11 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.hibernate.bytecode.spi.BasicProxyFactory;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
|
||||
/**
|
||||
* EmbeddableInstantiator used for instantiating "proxies" of an embeddable.
|
||||
*/
|
||||
public class EmbeddableInstantiatorProxied implements EmbeddableInstantiator {
|
||||
public class EmbeddableInstantiatorProxied implements StandardEmbeddableInstantiator {
|
||||
private final Class<?> proxiedClass;
|
||||
private final BasicProxyFactory factory;
|
||||
|
||||
|
|
|
@ -31,9 +31,12 @@ public class EmbeddableRepresentationStrategyMap implements EmbeddableRepresenta
|
|||
public EmbeddableRepresentationStrategyMap(
|
||||
Component bootDescriptor,
|
||||
Supplier<EmbeddableMappingType> runtimeDescriptorAccess,
|
||||
EmbeddableInstantiator customInstantiator,
|
||||
RuntimeModelCreationContext creationContext) {
|
||||
this.mapJtd = creationContext.getTypeConfiguration().getJavaTypeDescriptorRegistry().getDescriptor( Map.class );
|
||||
this.instantiator = new EmbeddableInstantiatorDynamicMap( bootDescriptor, runtimeDescriptorAccess );
|
||||
this.instantiator = customInstantiator != null
|
||||
? customInstantiator
|
||||
: new EmbeddableInstantiatorDynamicMap( bootDescriptor, runtimeDescriptorAccess );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -44,6 +44,7 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
|
|||
public EmbeddableRepresentationStrategyPojo(
|
||||
Component bootDescriptor,
|
||||
Supplier<EmbeddableMappingType> runtimeDescriptorAccess,
|
||||
EmbeddableInstantiator customInstantiator,
|
||||
RuntimeModelCreationContext creationContext) {
|
||||
super(
|
||||
bootDescriptor,
|
||||
|
@ -69,7 +70,9 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
|
|||
false
|
||||
);
|
||||
|
||||
this.instantiator = determineInstantiator( bootDescriptor, runtimeDescriptorAccess, creationContext );
|
||||
this.instantiator = customInstantiator != null
|
||||
? customInstantiator
|
||||
: determineInstantiator( bootDescriptor, runtimeDescriptorAccess, creationContext );
|
||||
}
|
||||
|
||||
private EmbeddableInstantiator determineInstantiator(
|
||||
|
|
|
@ -114,6 +114,8 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
|
|||
mapsIdRepresentationStrategy = new EmbeddableRepresentationStrategyPojo(
|
||||
bootDescriptor.getIdentifierMapper(),
|
||||
() -> ( ( CompositeTypeImplementor) bootDescriptor.getIdentifierMapper().getType() ).getMappingModelPart().getEmbeddableTypeDescriptor(),
|
||||
// we currently do not support custom instantiators for identifiers
|
||||
null,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
@ -121,6 +123,8 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
|
|||
mapsIdRepresentationStrategy = new EmbeddableRepresentationStrategyPojo(
|
||||
(Component) bootDescriptorIdentifier,
|
||||
() -> ( ( CompositeTypeImplementor) bootDescriptor.getIdentifierMapper().getType() ).getMappingModelPart().getEmbeddableTypeDescriptor(),
|
||||
// we currently do not support custom instantiators for identifiers
|
||||
null,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ import org.hibernate.mapping.Component;
|
|||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.metamodel.RepresentationMode;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||
|
||||
/**
|
||||
* @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 ) {
|
||||
return new EmbeddableRepresentationStrategyMap( bootDescriptor, runtimeDescriptorAccess, creationContext );
|
||||
return new EmbeddableRepresentationStrategyMap(
|
||||
bootDescriptor,
|
||||
runtimeDescriptorAccess,
|
||||
customInstantiator,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
else {
|
||||
// todo (6.0) : fix this
|
||||
|
@ -84,7 +104,12 @@ public class ManagedTypeRepresentationResolverStandard implements ManagedTypeRep
|
|||
//
|
||||
// instead, resolve ReflectionOptimizer once - here - and pass along to
|
||||
// StandardPojoRepresentationStrategy
|
||||
return new EmbeddableRepresentationStrategyPojo( bootDescriptor, runtimeDescriptorAccess, creationContext );
|
||||
return new EmbeddableRepresentationStrategyPojo(
|
||||
bootDescriptor,
|
||||
runtimeDescriptorAccess,
|
||||
customInstantiator,
|
||||
creationContext
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.hibernate.mapping.Property;
|
|||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.metamodel.UnsupportedMappingException;
|
||||
import org.hibernate.metamodel.internal.StandardEmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
|
||||
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.MappingModelCreationLogger;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NonTransientException;
|
||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
|
@ -129,6 +131,9 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if ( e instanceof NonTransientException ) {
|
||||
throw e;
|
||||
}
|
||||
MappingModelCreationLogger.LOGGER.debugf(
|
||||
e,
|
||||
"(DEBUG) Error finalizing EmbeddableMappingType(%s)",
|
||||
|
@ -550,6 +555,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
|
|||
"EmbeddableMappingType(" + getEmbeddedValueMapping().getNavigableRole().getFullPath() + ")#initColumnMappings",
|
||||
this::initColumnMappings
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.metamodel.RepresentationMode;
|
||||
import org.hibernate.metamodel.internal.StandardEmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
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 EntityInstantiator entityInstantiator;
|
||||
|
||||
|
|
|
@ -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" );
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -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[]
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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[]
|
||||
|
|
@ -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;
|
|
@ -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" );
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -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[]
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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[]
|
|
@ -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;
|
Loading…
Reference in New Issue