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]]
|
[[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.
|
|
@ -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.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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -296,6 +296,7 @@ public class MapBinder extends CollectionBinder {
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
null,
|
||||||
buildingContext,
|
buildingContext,
|
||||||
inheritanceStatePerClass
|
inheritanceStatePerClass
|
||||||
);
|
);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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