HHH-1152 Discriminator based inheritance for embeddable types

This commit is contained in:
Marco Belladelli 2024-04-05 09:52:27 +02:00
parent 8b5cdba5bc
commit 1c11dea006
80 changed files with 3491 additions and 558 deletions

View File

@ -0,0 +1,8 @@
create table TestEntity (
id bigint not null,
embeddable_type varchar(31) not null,
parentProp varchar(255),
childOneProp integer,
subChildOneProp float(53),
primary key (id)
)

View File

@ -376,3 +376,57 @@ include::{example-dir-inheritance}/polymorphism/ExplicitPolymorphismTest.java[ta
Therefore, only the `Book` was fetched since the `Blog` entity was marked with the
`@Polymorphism(type = PolymorphismType.EXPLICIT)` annotation, which instructs Hibernate
to skip it when executing a polymorphic query against a non-mapped base class.
[[embeddable-inheritance]]
==== Embeddable inheritance
Hibernate also supports discriminator-based inheritance for *embeddable types*. This works similarly to
<<entity-inheritance-single-table, Single Table Entity inheritance>>: an `@Embeddable` class may be
extended by other `@Embeddable` classes, in which case the `@Embedded` properties using that type will
rely on an additional discriminator column to store information about the composite value's subtype.
When retrieving the inherited property, Hibernate will read the discriminator value and instantiate the
correct `@Embeddable` subtype with its corresponding properties.
By default, the discriminator column will be `STRING` typed and named like `<property_name>_DTYPE`,
where `property_name` is the name of the `@Embedded` property in the respective entity mapping.
It's possible to customize the discriminator column mapping:
* For the whole `@Embeddable` type, by using `@DiscriminatorColumn` or `@DiscriminatorFormula` on the *root* class of the inheritance hierarchy
(NOTE: if using the same inheritance-enabled embeddable type for two different properties in the same entity mapping,
this will cause a column name conflict);
* For a specific `@Embedded` property, by using the `@AttributeOverride` annotation with the special name `{discriminator}`.
Finally, to specify custom discriminator values for each subtype one can annotate the inheritance hierarchy's
classes with `@DiscriminatorValue`.
[IMPORTANT]
====
Embeddable inheritance *IS* also supported for components used in an `@ElementCollection`.
Embeddable inheritance is *NOT* supported for `@EmbeddedId`, embeddable types used as `@IdClass`
and embedded properties using a custom `@CompositeType`.
====
[[embeddable-inheritance-example]]
.Example mapping of an embeddable inheritance hierarchy
[source,java]
----
include::{example-dir-inheritance}/embeddable/ParentEmbeddable.java[tags=embeddable-inheritance-parent-example,indent=0]
----
[source,java]
----
include::{example-dir-inheritance}/embeddable/ChildOneEmbeddable.java[tags=embeddable-inheritance-child-one-example,indent=0]
----
[source,java]
----
include::{example-dir-inheritance}/embeddable/SubChildOneEmbeddable.java[tags=embeddable-inheritance-sub-child-one-example,indent=0]
----
[source,java]
----
include::{example-dir-inheritance}/embeddable/BasicEmbeddableInheritanceTest.java[tags=embeddable-inheritance-entity-example,indent=0]
----
This is the resulting table structure:
[source,sql]
----
include::{extrasdir}/embeddable-inheritance-create-table-example.sql[]
----

View File

@ -20,6 +20,7 @@ import org.hibernate.engine.spi.Status;
import org.hibernate.event.spi.EventSource;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.NaturalIdMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
@ -176,22 +177,25 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
Object object,
PersistenceContext persistenceContext) {
if ( object != null ) {
final AttributeMappingsList attributeMappings = attributeMapping.getEmbeddableTypeDescriptor().getAttributeMappings();
final EmbeddableMappingType descriptor = attributeMapping.getEmbeddableTypeDescriptor();
final AttributeMappingsList attributeMappings = descriptor.getAttributeMappings();
for ( int i = 0; i < attributeMappings.size(); i++ ) {
final AttributeMapping attribute = attributeMappings.get( i );
if ( attribute.isPluralAttributeMapping() ) {
addCollectionKey(
attribute.asPluralAttributeMapping(),
attribute.getPropertyAccess().getGetter().get( object ),
persistenceContext
);
}
else if ( attribute.isEmbeddedAttributeMapping() ) {
visitEmbeddedAttributeMapping(
attribute.asEmbeddedAttributeMapping(),
attribute.getPropertyAccess().getGetter().get( object ),
persistenceContext
);
if ( descriptor.declaresAttribute( object.getClass().getName(), attributeMapping ) ) {
if ( attribute.isPluralAttributeMapping() ) {
addCollectionKey(
attribute.asPluralAttributeMapping(),
attribute.getPropertyAccess().getGetter().get( object ),
persistenceContext
);
}
else if ( attribute.isEmbeddedAttributeMapping() ) {
visitEmbeddedAttributeMapping(
attribute.asEmbeddedAttributeMapping(),
attribute.getPropertyAccess().getGetter().get( object ),
persistenceContext
);
}
}
}
}

View File

@ -135,6 +135,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private final Map<String,PersistentClass> entityBindingMap = new HashMap<>();
private final List<Component> composites = new ArrayList<>();
private final Map<Class<?>, Component> genericComponentsMap = new HashMap<>();
private final Map<XClass, List<XClass>> embeddableSubtypes = new HashMap<>();
private final Map<String,Collection> collectionBindingMap = new HashMap<>();
private final Map<String, FilterDefinition> filterDefinitionMap = new HashMap<>();
@ -284,6 +285,17 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
return genericComponentsMap.get( componentClass );
}
@Override
public void registerEmbeddableSubclass(XClass superclass, XClass subclass) {
embeddableSubtypes.computeIfAbsent( superclass, c -> new ArrayList<>() ).add( subclass );
}
@Override
public List<XClass> getEmbeddableSubclasses(XClass superclass) {
final List<XClass> subclasses = embeddableSubtypes.get( superclass );
return subclasses != null ? subclasses : List.of();
}
@Override
public SessionFactoryBuilder getSessionFactoryBuilder() {
throw new UnsupportedOperationException(

View File

@ -65,6 +65,9 @@ public class AggregateComponentSecondPass implements SecondPass {
final Dialect dialect = database.getDialect();
final AggregateSupport aggregateSupport = dialect.getAggregateSupport();
// Sort the component properties early to ensure the aggregated
// columns respect the same order as the component's properties
final int[] originalOrder = component.sortProperties();
// Compute aggregated columns since we have to replace them in the table with the aggregate column
final List<Column> aggregatedColumns = component.getAggregatedColumns();
final AggregateColumn aggregateColumn = component.getAggregateColumn();
@ -97,7 +100,7 @@ public class AggregateComponentSecondPass implements SecondPass {
);
if ( registeredUdt == udt ) {
addAuxiliaryObjects = true;
orderColumns( registeredUdt );
orderColumns( registeredUdt, originalOrder );
}
else {
addAuxiliaryObjects = false;
@ -184,9 +187,8 @@ public class AggregateComponentSecondPass implements SecondPass {
propertyHolder.getTable().getColumns().removeAll( aggregatedColumns );
}
private void orderColumns(UserDefinedObjectType userDefinedType) {
private void orderColumns(UserDefinedObjectType userDefinedType, int[] originalOrder) {
final Class<?> componentClass = component.getComponentClass();
final int[] originalOrder = component.sortProperties();
final String[] structColumnNames = component.getStructColumnNames();
if ( structColumnNames == null || structColumnNames.length == 0 ) {
final int[] propertyMappingIndex;
@ -211,23 +213,27 @@ public class AggregateComponentSecondPass implements SecondPass {
else {
propertyMappingIndex = null;
}
final ArrayList<Column> orderedColumns = new ArrayList<>( userDefinedType.getColumnSpan() );
if ( propertyMappingIndex == null ) {
// If there is default ordering possible, assume alphabetical ordering
final ArrayList<Column> orderedColumns = new ArrayList<>( userDefinedType.getColumnSpan() );
final List<Property> properties = component.getProperties();
for ( Property property : properties ) {
addColumns( orderedColumns, property.getValue() );
}
userDefinedType.reorderColumns( orderedColumns );
if ( component.isPolymorphic() ) {
addColumns( orderedColumns, component.getDiscriminator() );
}
}
else {
final ArrayList<Column> orderedColumns = new ArrayList<>( userDefinedType.getColumnSpan() );
final List<Property> properties = component.getProperties();
for ( final int propertyIndex : propertyMappingIndex ) {
addColumns( orderedColumns, properties.get( propertyIndex ).getValue() );
}
userDefinedType.reorderColumns( orderedColumns );
}
final List<Column> reorderedColumn = context.getBuildingOptions()
.getColumnOrderingStrategy()
.orderUserDefinedTypeColumns( userDefinedType, context.getMetadataCollector() );
userDefinedType.reorderColumns( reorderedColumn != null ? reorderedColumn : orderedColumns );
}
else {
final ArrayList<Column> orderedColumns = new ArrayList<>( userDefinedType.getColumnSpan() );
@ -281,6 +287,13 @@ public class AggregateComponentSecondPass implements SecondPass {
}
}
}
if ( component.isPolymorphic() ) {
final Column column = component.getDiscriminator().getColumns().get( 0 );
if ( structColumnName.equals( column.getName() ) ) {
orderedColumns.add( column );
return true;
}
}
return false;
}

View File

@ -10,6 +10,8 @@ import org.hibernate.AssertionFailure;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.boot.spi.MetadataBuildingContext;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
@ -25,10 +27,10 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
private String discriminatorTypeName;
public AnnotatedDiscriminatorColumn() {
public AnnotatedDiscriminatorColumn(String defaultColumnName) {
//discriminator default value
super();
setLogicalColumnName( DEFAULT_DISCRIMINATOR_COLUMN_NAME );
setLogicalColumnName( defaultColumnName );
setNullable( false );
setDiscriminatorTypeName( DEFAULT_DISCRIMINATOR_TYPE );
setLength( DEFAULT_DISCRIMINATOR_LENGTH );
@ -45,10 +47,12 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
public static AnnotatedDiscriminatorColumn buildDiscriminatorColumn(
DiscriminatorColumn discriminatorColumn,
DiscriminatorFormula discriminatorFormula,
Column columnOverride,
String defaultColumnName,
MetadataBuildingContext context) {
final AnnotatedColumns parent = new AnnotatedColumns();
parent.setBuildingContext( context );
final AnnotatedDiscriminatorColumn column = new AnnotatedDiscriminatorColumn();
final AnnotatedDiscriminatorColumn column = new AnnotatedDiscriminatorColumn( defaultColumnName );
final DiscriminatorType discriminatorType;
if ( discriminatorFormula != null ) {
final DiscriminatorType type = discriminatorFormula.discriminatorType();
@ -76,7 +80,13 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
discriminatorType = DiscriminatorType.STRING;
column.setImplicit( true );
}
setDiscriminatorType( discriminatorType, discriminatorColumn, column );
if ( columnOverride != null ) {
column.setLogicalColumnName( columnOverride.name() );
if ( !columnOverride.columnDefinition().isEmpty() ) {
column.setSqlType( columnOverride.columnDefinition() );
}
}
setDiscriminatorType( discriminatorType, discriminatorColumn, columnOverride, column );
column.setParent( parent );
column.bind();
return column;
@ -85,6 +95,7 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
private static void setDiscriminatorType(
DiscriminatorType type,
DiscriminatorColumn discriminatorColumn,
Column columnOverride,
AnnotatedDiscriminatorColumn column) {
if ( type == null ) {
column.setDiscriminatorTypeName( "string" );
@ -102,7 +113,10 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
break;
case STRING:
column.setDiscriminatorTypeName( "string" );
if ( discriminatorColumn != null ) {
if ( columnOverride != null ) {
column.setLength( (long) columnOverride.length() );
}
else if ( discriminatorColumn != null ) {
column.setLength( (long) discriminatorColumn.length() );
}
break;

View File

@ -67,6 +67,7 @@ import jakarta.persistence.Table;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.TableGenerators;
import static org.hibernate.boot.model.internal.AnnotatedClassType.EMBEDDABLE;
import static org.hibernate.boot.model.internal.AnnotatedClassType.ENTITY;
import static org.hibernate.boot.model.internal.FilterDefBinder.bindFilterDefs;
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
@ -697,7 +698,15 @@ public final class AnnotationBinder {
superclassState.setHasSiblings( true );
final InheritanceState superEntityState =
getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
state.setHasParents( superEntityState != null );
if ( superEntityState != null ) {
state.setHasParents( true );
if ( buildingContext.getMetadataCollector().getClassType( clazz ) == EMBEDDABLE ) {
buildingContext.getMetadataCollector().registerEmbeddableSubclass(
superEntityState.getClazz(),
clazz
);
}
}
logMixedInheritance( clazz, superclassState, state );
if ( superclassState.getType() != null ) {
state.setType( superclassState.getType() );

View File

@ -308,7 +308,7 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
@Override
public void addProperty(Property prop, XClass declaringClass) {
component.addProperty( prop );
component.addProperty( prop, declaringClass );
}
@Override

View File

@ -13,8 +13,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.Instantiator;
import org.hibernate.annotations.TypeBinderType;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
@ -26,20 +28,25 @@ import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.property.access.internal.PropertyAccessStrategyCompositeUserTypeImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
import org.hibernate.property.access.spi.PropertyAccessStrategy;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.BasicType;
import org.hibernate.usertype.CompositeUserType;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
@ -51,6 +58,9 @@ import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.DEFAULT_DISCRIMINATOR_COLUMN_NAME;
import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.buildDiscriminatorColumn;
import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation;
import static org.hibernate.boot.model.internal.BinderHelper.getPath;
import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath;
@ -64,6 +74,9 @@ import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClas
import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations;
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
import static org.hibernate.internal.CoreLogging.messageLogger;
import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.unqualify;
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
/**
@ -362,6 +375,36 @@ public class EmbeddableBinder {
final XClass annotatedClass = inferredData.getPropertyClass();
final List<PropertyData> classElements =
collectClassElements( propertyAccessor, context, returnedClassOrElement, annotatedClass, isIdClass );
// Main entry point for binding embeddable inheritance
bindDiscriminator(
component,
returnedClassOrElement,
propertyHolder,
subholder,
inferredData,
inheritanceStatePerClass,
context
);
if ( component.isPolymorphic() ) {
validateInheritanceIsSupported( subholder, compositeUserType );
final BasicType<?> discriminatorType = (BasicType<?>) component.getDiscriminator().getType();
// Discriminator values are used to construct the embeddable domain
// type hierarchy so order of processing is important
final Map<Object, String> discriminatorValues = new TreeMap<>();
final Map<String, String> subclassToSuperclass = new HashMap<>();
collectDiscriminatorValue( returnedClassOrElement, discriminatorType, discriminatorValues );
collectSubclassElements(
propertyAccessor,
context,
returnedClassOrElement,
classElements,
discriminatorType,
discriminatorValues,
subclassToSuperclass
);
component.setDiscriminatorValues( discriminatorValues );
component.setSubclassToSuperclass( subclassToSuperclass );
}
final List<PropertyData> baseClassElements =
collectBaseClassElements( baseInferredData, propertyAccessor, context, annotatedClass );
if ( baseClassElements != null
@ -403,6 +446,7 @@ public class EmbeddableBinder {
if ( compositeUserType != null ) {
processCompositeUserType( component, compositeUserType );
}
AggregateComponentBinder.processAggregate(
component,
propertyHolder,
@ -428,6 +472,125 @@ public class EmbeddableBinder {
.getBeanInstance();
}
private static void bindDiscriminator(
Component component,
XClass componentClass,
PropertyHolder parentHolder,
PropertyHolder holder,
PropertyData propertyData,
Map<XClass, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( componentClass );
if ( inheritanceState == null ) {
return;
}
final AnnotatedDiscriminatorColumn discriminatorColumn = processEmbeddableDiscriminatorProperties(
componentClass,
propertyData,
parentHolder,
holder,
inheritanceState,
context
);
if ( discriminatorColumn != null ) {
bindDiscriminatorColumnToComponent( component, discriminatorColumn, holder, context );
}
}
private static AnnotatedDiscriminatorColumn processEmbeddableDiscriminatorProperties(
XClass annotatedClass,
PropertyData propertyData,
PropertyHolder parentHolder,
PropertyHolder holder,
InheritanceState inheritanceState,
MetadataBuildingContext context) {
final DiscriminatorColumn discriminatorColumn = annotatedClass.getAnnotation( DiscriminatorColumn.class );
final DiscriminatorFormula discriminatorFormula = getOverridableAnnotation(
annotatedClass,
DiscriminatorFormula.class,
context
);
if ( !inheritanceState.hasParents() ) {
if ( inheritanceState.hasSiblings() ) {
final String path = qualify( holder.getPath(), EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME );
final String columnPrefix;
final Column[] overrides;
if ( holder.isWithinElementCollection() ) {
columnPrefix = unqualify( parentHolder.getPath() );
overrides = parentHolder.getOverriddenColumn( path );
}
else {
columnPrefix = propertyData.getPropertyName();
overrides = holder.getOverriddenColumn( path );
}
return buildDiscriminatorColumn(
discriminatorColumn,
discriminatorFormula,
overrides == null ? null : overrides[0],
columnPrefix + DEFAULT_DISCRIMINATOR_COLUMN_NAME,
context
);
}
}
else {
// not a root entity
if ( discriminatorColumn != null ) {
throw new AnnotationException( String.format(
"Embeddable class '%s' is annotated '@DiscriminatorColumn' but it is not the root of the inheritance hierarchy",
annotatedClass.getName()
) );
}
if ( discriminatorFormula != null ) {
throw new AnnotationException( String.format(
"Embeddable class '%s' is annotated '@DiscriminatorFormula' but it is not the root of the inheritance hierarchy",
annotatedClass.getName()
) );
}
}
return null;
}
private static void bindDiscriminatorColumnToComponent(
Component component,
AnnotatedDiscriminatorColumn discriminatorColumn,
PropertyHolder holder,
MetadataBuildingContext context) {
assert component.getDiscriminator() == null;
LOG.tracev( "Setting discriminator for embeddable {0}", component.getComponentClassName() );
final AnnotatedColumns columns = new AnnotatedColumns();
columns.setPropertyHolder( holder );
columns.setBuildingContext( context );
discriminatorColumn.setParent( columns );
final BasicValue discriminatorColumnBinding = new BasicValue( context, component.getTable() );
component.setDiscriminator( discriminatorColumnBinding );
discriminatorColumn.linkWithValue( discriminatorColumnBinding );
discriminatorColumnBinding.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
}
private static void validateInheritanceIsSupported(
PropertyHolder holder,
CompositeUserType<?> compositeUserType) {
if ( holder.isOrWithinEmbeddedId() ) {
throw new AnnotationException( String.format(
"Embeddable class '%s' defines an inheritance hierarchy and cannot be used in an '@EmbeddedId'",
holder.getClassName()
) );
}
else if ( holder.isInIdClass() ) {
throw new AnnotationException( String.format(
"Embeddable class '%s' defines an inheritance hierarchy and cannot be used in an '@IdClass'",
holder.getClassName()
) );
}
else if ( compositeUserType != null ) {
throw new AnnotationException( String.format(
"Embeddable class '%s' defines an inheritance hierarchy and cannot be used with a custom '@CompositeType'",
holder.getClassName()
) );
}
}
private static List<PropertyData> collectClassElements(
AccessType propertyAccessor,
MetadataBuildingContext context,
@ -451,6 +614,75 @@ public class EmbeddableBinder {
return classElements;
}
private static void collectSubclassElements(
AccessType propertyAccessor,
MetadataBuildingContext context,
XClass superclass,
List<PropertyData> classElements,
BasicType<?> discriminatorType,
Map<Object, String> discriminatorValues,
Map<String, String> subclassToSuperclass) {
for ( final XClass subclass : context.getMetadataCollector().getEmbeddableSubclasses( superclass ) ) {
// collect the discriminator value details
final String old = collectDiscriminatorValue( subclass, discriminatorType, discriminatorValues );
if ( old != null ) {
throw new AnnotationException( String.format(
"Embeddable subclass '%s' defines the same discriminator value as '%s",
subclass.getName(),
old
) );
}
final String put = subclassToSuperclass.put( subclass.getName().intern(), superclass.getName().intern() );
assert put == null;
// collect property of subclass
final PropertyContainer superContainer = new PropertyContainer( subclass, superclass, propertyAccessor );
addElementsOfClass( classElements, superContainer, context );
// recursively do that same for all subclasses
collectSubclassElements(
propertyAccessor,
context,
subclass,
classElements,
discriminatorType,
discriminatorValues,
subclassToSuperclass
);
}
}
private static String collectDiscriminatorValue(
XClass annotatedClass,
BasicType<?> discriminatorType,
Map<Object, String> discriminatorValues) {
final String explicitValue = annotatedClass.isAnnotationPresent( DiscriminatorValue.class )
? annotatedClass.getAnnotation( DiscriminatorValue.class ).value()
: null;
final String discriminatorValue;
if ( isEmpty( explicitValue ) ) {
final String name = unqualify( annotatedClass.getName() );
if ( "character".equals( discriminatorType.getName() ) ) {
throw new AnnotationException( String.format(
"Embeddable '%s' has a discriminator of character type and must specify its '@DiscriminatorValue'",
name
) );
}
else if ( "integer".equals( discriminatorType.getName() ) ) {
discriminatorValue = String.valueOf( name.hashCode() );
}
else {
discriminatorValue = name;
}
}
else {
discriminatorValue = explicitValue;
}
return discriminatorValues.put(
discriminatorType.getJavaTypeDescriptor().fromString( discriminatorValue ),
annotatedClass.getName().intern()
);
}
private static boolean isValidSuperclass(XClass superClass, boolean isIdClass) {
if ( superClass == null ) {
return false;

View File

@ -139,6 +139,7 @@ import static jakarta.persistence.InheritanceType.SINGLE_TABLE;
import static org.hibernate.annotations.PolymorphismType.EXPLICIT;
import static org.hibernate.annotations.PolymorphismType.IMPLICIT;
import static org.hibernate.boot.model.internal.AnnotatedClassType.MAPPED_SUPERCLASS;
import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.DEFAULT_DISCRIMINATOR_COLUMN_NAME;
import static org.hibernate.boot.model.internal.AnnotatedDiscriminatorColumn.buildDiscriminatorColumn;
import static org.hibernate.boot.model.internal.AnnotatedJoinColumn.buildInheritanceJoinColumn;
import static org.hibernate.boot.model.internal.BinderHelper.getMappedSuperclassOrNull;
@ -976,7 +977,13 @@ public class EntityBinder {
getOverridableAnnotation( annotatedClass, DiscriminatorFormula.class, context );
if ( !inheritanceState.hasParents() || annotatedClass.isAnnotationPresent( Inheritance.class ) ) {
return buildDiscriminatorColumn( discriminatorColumn, discriminatorFormula, context );
return buildDiscriminatorColumn(
discriminatorColumn,
discriminatorFormula,
null,
DEFAULT_DISCRIMINATOR_COLUMN_NAME,
context
);
}
else {
// not a root entity
@ -1006,7 +1013,7 @@ public class EntityBinder {
final DiscriminatorColumn discriminatorColumn = annotatedClass.getAnnotation( DiscriminatorColumn.class );
if ( !inheritanceState.hasParents() || annotatedClass.isAnnotationPresent( Inheritance.class ) ) {
return useDiscriminatorColumnForJoined( discriminatorColumn )
? buildDiscriminatorColumn( discriminatorColumn, null, context )
? buildDiscriminatorColumn( discriminatorColumn, null, null, DEFAULT_DISCRIMINATOR_COLUMN_NAME, context )
: null;
}
else {

View File

@ -7,6 +7,7 @@
package org.hibernate.boot.spi;
import java.io.Serializable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
@ -83,6 +84,10 @@ public interface InFlightMetadataCollector extends MetadataImplementor {
void registerGenericComponent(Component component);
void registerEmbeddableSubclass(XClass superclass, XClass subclass);
List<XClass> getEmbeddableSubclasses(XClass superclass);
/**
* Adds an import (for use in HQL).
*

View File

@ -24,12 +24,12 @@ import java.util.ArrayList;
import java.util.TimeZone;
import org.hibernate.internal.util.CharSequenceHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
@ -45,6 +45,8 @@ import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.StructJdbcType;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
import static org.hibernate.dialect.StructHelper.instantiate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
@ -184,17 +186,14 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
array = values.toArray();
}
else {
array = new Object[embeddableMappingType.getJdbcValueCount()];
array = new Object[embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
end = deserializeStruct( string, 0, 0, array, returnEmbeddable, options );
}
assert end == string.length();
if ( returnEmbeddable ) {
final Object[] attributeValues = getAttributeValues( embeddableMappingType, orderMapping, array, options );
final StructAttributeValues attributeValues = getAttributeValues( embeddableMappingType, orderMapping, array, options );
//noinspection unchecked
return (X) embeddableMappingType.getRepresentationStrategy().getInstantiator().instantiate(
() -> attributeValues,
options.getSessionFactory()
);
return (X) instantiate( embeddableMappingType, attributeValues, options.getSessionFactory() );
}
else if ( inverseOrderMapping != null ) {
StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, array.clone(), array );
@ -467,15 +466,13 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
options
);
if ( returnEmbeddable ) {
final Object[] attributeValues = structJdbcType.getAttributeValues(
final StructAttributeValues attributeValues = structJdbcType.getAttributeValues(
structJdbcType.embeddableMappingType,
structJdbcType.orderMapping,
subValues,
options
);
final Object subValue = structJdbcType.embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate( () -> attributeValues, options.getSessionFactory() );
final Object subValue = instantiate( structJdbcType.embeddableMappingType, attributeValues, options.getSessionFactory() );
values[column] = subValue;
}
else {
@ -869,15 +866,13 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
options
);
if ( returnEmbeddable ) {
final Object[] attributeValues = structJdbcType.getAttributeValues(
final StructAttributeValues attributeValues = structJdbcType.getAttributeValues(
structJdbcType.embeddableMappingType,
structJdbcType.orderMapping,
subValues,
options
);
final Object subValue = structJdbcType.embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate( () -> attributeValues, options.getSessionFactory() );
final Object subValue = instantiate( structJdbcType.embeddableMappingType, attributeValues, options.getSessionFactory() );
values.add( subValue );
}
else {
@ -1010,10 +1005,11 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
private SelectableMapping getJdbcValueSelectable(int jdbcValueSelectableIndex) {
if ( orderMapping != null ) {
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
int count = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( orderMapping[i] );
final MappingType mappedType = attributeMapping.getMappedType();
for ( int i = 0; i < size; i++ ) {
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, orderMapping[i] );
final MappingType mappedType = modelPart.getMappedType();
if ( mappedType instanceof EmbeddableMappingType ) {
final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType;
final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping();
@ -1033,9 +1029,9 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
}
else {
if ( count == jdbcValueSelectableIndex ) {
return (SelectableMapping) attributeMapping;
return (SelectableMapping) modelPart;
}
count += attributeMapping.getJdbcTypeCount();
count += modelPart.getJdbcTypeCount();
}
}
return null;
@ -1192,8 +1188,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
}
private void serializeStructTo(PostgreSQLAppender appender, Object value, WrapperOptions options) {
final Object[] array = embeddableMappingType.getValues( value );
serializeValuesTo( appender, options, embeddableMappingType, array, '(' );
serializeValuesTo( appender, options, embeddableMappingType, value, '(' );
appender.append( ')' );
}
@ -1201,19 +1196,20 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
PostgreSQLAppender appender,
WrapperOptions options,
EmbeddableMappingType embeddableMappingType,
Object[] array,
Object domainValue,
char separator) {
final int end = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < end; i++ ) {
final AttributeMapping attributeMapping;
final Object[] array = embeddableMappingType.getValues( domainValue );
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < array.length; i++ ) {
final ValuedModelPart attributeMapping;
final Object attributeValue;
if ( orderMapping == null ) {
attributeMapping = embeddableMappingType.getAttributeMapping( i );
attributeValue = array == null ? null : array[i];
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i );
attributeValue = array[i];
}
else {
attributeMapping = embeddableMappingType.getAttributeMapping( orderMapping[i] );
attributeValue = array == null ? null : array[orderMapping[i]];
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, orderMapping[i] );
attributeValue = array[orderMapping[i]];
}
if ( attributeMapping instanceof BasicValuedMapping ) {
appender.append( separator );
@ -1232,7 +1228,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
appender,
options,
mappingType,
attributeValue == null ? null : mappingType.getValues( attributeValue ),
attributeValue,
separator
);
separator = ',';
@ -1398,9 +1394,8 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
if ( subValue != null ) {
final AbstractPostgreSQLStructJdbcType structJdbcType = (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType();
final EmbeddableMappingType subEmbeddableMappingType = structJdbcType.getEmbeddableMappingType();
final Object[] array = subEmbeddableMappingType.getValues( subValue );
appender.quoteStart();
structJdbcType.serializeValuesTo( appender, options, subEmbeddableMappingType, array, '(' );
structJdbcType.serializeValuesTo( appender, options, subEmbeddableMappingType, subValue, '(' );
appender.append( ')' );
appender.quoteEnd();
}
@ -1410,21 +1405,21 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
}
}
private Object[] getAttributeValues(
private StructAttributeValues getAttributeValues(
EmbeddableMappingType embeddableMappingType,
int[] orderMapping,
Object[] rawJdbcValues,
WrapperOptions options) throws SQLException {
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
final Object[] attributeValues;
if ( numberOfAttributeMappings != rawJdbcValues.length || orderMapping != null ) {
attributeValues = new Object[numberOfAttributeMappings];
}
else {
attributeValues = rawJdbcValues;
}
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final StructAttributeValues attributeValues = new StructAttributeValues(
numberOfAttributeMappings,
orderMapping != null ?
null :
rawJdbcValues
);
int jdbcIndex = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
for ( int i = 0; i < size; i++ ) {
final int attributeIndex;
if ( orderMapping == null ) {
attributeIndex = i;
@ -1432,9 +1427,9 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
else {
attributeIndex = orderMapping[i];
}
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( attributeIndex );
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, attributeIndex );
jdbcIndex += injectAttributeValue(
attributeMapping,
modelPart,
attributeValues,
attributeIndex,
rawJdbcValues,
@ -1446,45 +1441,46 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
}
private int injectAttributeValue(
AttributeMapping attributeMapping,
Object[] attributeValues,
ValuedModelPart modelPart,
StructAttributeValues attributeValues,
int attributeIndex,
Object[] rawJdbcValues,
int jdbcIndex,
WrapperOptions options) throws SQLException {
final MappingType mappedType = attributeMapping.getMappedType();
final MappingType mappedType = modelPart.getMappedType();
final int jdbcValueCount;
final Object rawJdbcValue = rawJdbcValues[jdbcIndex];
if ( mappedType instanceof EmbeddableMappingType ) {
final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType;
if ( embeddableMappingType.getAggregateMapping() != null ) {
jdbcValueCount = 1;
attributeValues[attributeIndex] = rawJdbcValue;
attributeValues.setAttributeValue( attributeIndex, rawJdbcValue );
}
else {
jdbcValueCount = embeddableMappingType.getJdbcValueCount();
final Object[] subJdbcValues = new Object[jdbcValueCount];
System.arraycopy( rawJdbcValues, jdbcIndex, subJdbcValues, 0, subJdbcValues.length );
final Object[] subValues = getAttributeValues( embeddableMappingType, null, subJdbcValues, options );
attributeValues[attributeIndex] = embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate(
() -> subValues,
embeddableMappingType.findContainingEntityMapping()
.getEntityPersister()
.getFactory()
);
final StructAttributeValues subValues = getAttributeValues(
embeddableMappingType,
null,
subJdbcValues,
options
);
attributeValues.setAttributeValue(
attributeIndex,
instantiate( embeddableMappingType, subValues, options.getSessionFactory() )
);
}
}
else {
assert attributeMapping.getJdbcTypeCount() == 1;
assert modelPart.getJdbcTypeCount() == 1;
jdbcValueCount = 1;
final JdbcMapping jdbcMapping = attributeMapping.getSingleJdbcMapping();
final JdbcMapping jdbcMapping = modelPart.getSingleJdbcMapping();
final Object jdbcValue = jdbcMapping.getJdbcJavaType().wrap(
rawJdbcValue,
options
);
attributeValues[attributeIndex] = jdbcMapping.convertToDomainValue( jdbcValue );
attributeValues.setAttributeValue( attributeIndex, jdbcMapping.convertToDomainValue( jdbcValue ) );
}
return jdbcValueCount;
}

View File

@ -22,11 +22,11 @@ import java.util.Objects;
import org.hibernate.Internal;
import org.hibernate.internal.util.CharSequenceHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.type.BasicPluralType;
@ -41,6 +41,9 @@ import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
import static org.hibernate.dialect.StructHelper.instantiate;
/**
* A Helper for serializing and deserializing JSON, based on an {@link org.hibernate.metamodel.mapping.EmbeddableMappingType}.
*/
@ -57,8 +60,7 @@ public class JsonHelper {
}
private static void toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options, JsonAppender appender) {
final Object[] values = embeddableMappingType.getValues( value );
toString( embeddableMappingType, options, appender, values, '{' );
toString( embeddableMappingType, options, appender, value, '{' );
appender.append( '}' );
}
@ -66,10 +68,12 @@ public class JsonHelper {
EmbeddableMappingType embeddableMappingType,
WrapperOptions options,
JsonAppender appender,
Object[] values,
Object domainValue,
char separator) {
final Object[] values = embeddableMappingType.getValues( domainValue );
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < values.length; i++ ) {
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i );
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i );
if ( attributeMapping instanceof SelectableMapping ) {
final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
appender.append( separator );
@ -90,7 +94,7 @@ public class JsonHelper {
mappingType,
options,
appender,
mappingType.getValues( values[i] ),
values[i],
separator
);
}
@ -295,20 +299,18 @@ public class JsonHelper {
return null;
}
final Object[] values = new Object[embeddableMappingType.getJdbcValueCount()];
final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
final Object[] values = new Object[jdbcValueCount + ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
final int end = fromString( embeddableMappingType, string, 0, string.length(), values, returnEmbeddable, options );
assert string.substring( end ).isBlank();
if ( returnEmbeddable ) {
final Object[] attributeValues = StructHelper.getAttributeValues(
final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
embeddableMappingType,
values,
options
);
//noinspection unchecked
return (X) embeddableMappingType.getRepresentationStrategy().getInstantiator().instantiate(
() -> attributeValues,
options.getSessionFactory()
);
return (X) instantiate( embeddableMappingType, attributeValues, options.getSessionFactory() );
}
//noinspection unchecked
return (X) values;
@ -440,17 +442,12 @@ public class JsonHelper {
i = fromString( subMappingType, string, i, end, subValues, returnEmbeddable, options ) - 1;
assert string.charAt( i ) == '}';
if ( returnEmbeddable ) {
final Object[] attributeValues = StructHelper.getAttributeValues(
final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
subMappingType,
subValues,
options
);
values[selectableIndex] = embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate(
() -> attributeValues,
options.getSessionFactory()
);
values[selectableIndex] = instantiate( embeddableMappingType, attributeValues, options.getSessionFactory() );
}
else {
values[selectableIndex] = subValues;
@ -1113,13 +1110,13 @@ public class JsonHelper {
options
);
if ( returnEmbeddable ) {
final StructAttributeValues subAttributeValues = StructHelper.getAttributeValues(
aggregateJdbcType.getEmbeddableMappingType(),
subValues,
options
);
final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
return embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate(
() -> subValues,
options.getSessionFactory()
);
return instantiate( embeddableMappingType, subAttributeValues, options.getSessionFactory() ) ;
}
return subValues;
}

View File

@ -0,0 +1,46 @@
/*
* 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.dialect;
import org.hibernate.metamodel.spi.ValueAccess;
/**
* @author Marco Belladelli
*/
public class StructAttributeValues implements ValueAccess {
private final Object[] attributeValues;
private final int size;
private Object discriminator;
public StructAttributeValues(int size, Object[] rawJdbcValues) {
this.size = size;
if ( rawJdbcValues == null || size != rawJdbcValues.length) {
attributeValues = new Object[size];
}
else {
attributeValues = rawJdbcValues;
}
}
@Override
public Object[] getValues() {
return attributeValues;
}
public void setAttributeValue(int index, Object value) {
if ( index == size ) {
discriminator = value;
}
else {
attributeValues[index] = value;
}
}
public Object getDiscriminator() {
return discriminator;
}
}

View File

@ -13,10 +13,13 @@ import java.sql.NClob;
import java.sql.SQLException;
import org.hibernate.Internal;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
@ -27,67 +30,61 @@ import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
*/
@Internal
public class StructHelper {
public static Object[] getAttributeValues(
public static StructAttributeValues getAttributeValues(
EmbeddableMappingType embeddableMappingType,
Object[] rawJdbcValues,
WrapperOptions options) throws SQLException {
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
final Object[] attributeValues;
if ( numberOfAttributeMappings != rawJdbcValues.length ) {
attributeValues = new Object[numberOfAttributeMappings];
}
else {
attributeValues = rawJdbcValues;
}
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final StructAttributeValues attributeValues = new StructAttributeValues( numberOfAttributeMappings, rawJdbcValues );
int jdbcIndex = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i );
jdbcIndex += injectAttributeValue( attributeMapping, attributeValues, i, rawJdbcValues, jdbcIndex, options );
for ( int i = 0; i < size; i++ ) {
final ValuedModelPart valuedModelPart = getEmbeddedPart(
embeddableMappingType,
numberOfAttributeMappings,
i
);
jdbcIndex += injectAttributeValue( valuedModelPart, attributeValues, i, rawJdbcValues, jdbcIndex, options );
}
return attributeValues;
}
private static int injectAttributeValue(
AttributeMapping attributeMapping,
Object[] attributeValues,
ValuedModelPart modelPart,
StructAttributeValues attributeValues,
int attributeIndex,
Object[] rawJdbcValues,
int jdbcIndex,
WrapperOptions options) throws SQLException {
final MappingType mappedType = attributeMapping.getMappedType();
final MappingType mappedType = modelPart.getMappedType();
final int jdbcValueCount;
final Object rawJdbcValue = rawJdbcValues[jdbcIndex];
if ( mappedType instanceof EmbeddableMappingType ) {
final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType;
if ( embeddableMappingType.getAggregateMapping() != null ) {
jdbcValueCount = 1;
attributeValues[attributeIndex] = rawJdbcValue;
attributeValues.setAttributeValue( attributeIndex, rawJdbcValue );
}
else {
jdbcValueCount = embeddableMappingType.getJdbcValueCount();
final Object[] subJdbcValues = new Object[jdbcValueCount];
System.arraycopy( rawJdbcValues, jdbcIndex, subJdbcValues, 0, subJdbcValues.length );
final Object[] subValues = getAttributeValues( embeddableMappingType, subJdbcValues, options );
attributeValues[attributeIndex] = embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate(
() -> subValues,
embeddableMappingType.findContainingEntityMapping()
.getEntityPersister()
.getFactory()
);
final StructAttributeValues subValues = getAttributeValues( embeddableMappingType, subJdbcValues, options );
attributeValues.setAttributeValue(
attributeIndex,
instantiate( embeddableMappingType, subValues, options.getSessionFactory() )
);
}
}
else {
assert attributeMapping.getJdbcTypeCount() == 1;
assert modelPart.getJdbcTypeCount() == 1;
jdbcValueCount = 1;
final JdbcMapping jdbcMapping = attributeMapping.getSingleJdbcMapping();
final JdbcMapping jdbcMapping = modelPart.getSingleJdbcMapping();
final Object jdbcValue = jdbcMapping.getJdbcJavaType().wrap(
rawJdbcValue,
options
);
attributeValues[attributeIndex] = jdbcMapping.convertToDomainValue( jdbcValue );
attributeValues.setAttributeValue( attributeIndex, jdbcMapping.convertToDomainValue( jdbcValue ) );
}
return jdbcValueCount;
}
@ -95,18 +92,20 @@ public class StructHelper {
public static Object[] getJdbcValues(
EmbeddableMappingType embeddableMappingType,
int[] orderMapping,
Object[] attributeValues,
Object domainValue,
WrapperOptions options) throws SQLException {
final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
final int valueCount = jdbcValueCount + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final Object[] values = embeddableMappingType.getValues( domainValue );
final Object[] jdbcValues;
if ( jdbcValueCount != attributeValues.length || orderMapping != null ) {
jdbcValues = new Object[jdbcValueCount];
if ( valueCount != values.length || orderMapping != null ) {
jdbcValues = new Object[valueCount];
}
else {
jdbcValues = attributeValues;
jdbcValues = values;
}
int jdbcIndex = 0;
for ( int i = 0; i < attributeValues.length; i++ ) {
for ( int i = 0; i < values.length; i++ ) {
final int attributeIndex;
if ( orderMapping == null ) {
attributeIndex = i;
@ -115,20 +114,45 @@ public class StructHelper {
attributeIndex = orderMapping[i];
}
jdbcIndex += injectJdbcValue(
embeddableMappingType.getAttributeMapping( attributeIndex ),
attributeValues,
getEmbeddedPart( embeddableMappingType, jdbcValueCount, attributeIndex ),
values,
attributeIndex,
jdbcValues,
jdbcIndex,
options
);
}
assert jdbcIndex == jdbcValueCount;
assert jdbcIndex == valueCount;
return jdbcValues;
}
public static Object instantiate(
EmbeddableMappingType embeddableMappingType,
StructAttributeValues attributeValues,
SessionFactoryImplementor sessionFactory) {
final EmbeddableRepresentationStrategy representationStrategy = embeddableMappingType.getRepresentationStrategy();
final EmbeddableInstantiator instantiator;
if ( !embeddableMappingType.isPolymorphic() ) {
instantiator = representationStrategy.getInstantiator();
}
else {
// the discriminator here is the composite class name because it gets converted to the domain type when extracted
instantiator = representationStrategy.getInstantiatorForClass( (String) attributeValues.getDiscriminator() );
}
return instantiator.instantiate( attributeValues, sessionFactory );
}
public static ValuedModelPart getEmbeddedPart(
EmbeddableMappingType embeddableMappingType,
int numberOfAttributes,
int position) {
return position == numberOfAttributes ?
embeddableMappingType.getDiscriminatorMapping() :
embeddableMappingType.getAttributeMapping( position );
}
private static int injectJdbcValue(
AttributeMapping attributeMapping,
ValuedModelPart attributeMapping,
Object[] attributeValues,
int attributeIndex,
Object[] jdbcValues,
@ -149,13 +173,14 @@ public class StructHelper {
);
}
else {
jdbcValueCount = embeddableMappingType.getJdbcValueCount();
jdbcValueCount = embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
final int numberOfValues = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final Object[] subValues = embeddableMappingType.getValues( attributeValues[attributeIndex] );
int offset = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
for ( int i = 0; i < numberOfValues; i++ ) {
offset += injectJdbcValue(
embeddableMappingType.getAttributeMapping( i ),
getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, i ),
subValues,
i,
jdbcValues,
@ -226,10 +251,10 @@ public class StructHelper {
int[] inverseMapping,
Object[] sourceJdbcValues,
Object[] targetJdbcValues) {
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
int targetJdbcOffset = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i );
for ( int i = 0; i < numberOfAttributes + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); i++ ) {
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i );
final MappingType mappedType = attributeMapping.getMappedType();
final int jdbcValueCount = getJdbcValueCount( mappedType );

View File

@ -11,14 +11,12 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Struct;
import java.util.ArrayList;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType;
@ -29,12 +27,14 @@ import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
import static org.hibernate.dialect.StructHelper.instantiate;
/**
* @author Christian Beikov
*/
@ -132,7 +132,7 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
final Object[] jdbcValues = StructHelper.getJdbcValues(
embeddableMappingType,
orderMapping,
embeddableMappingType.getValues( domainValue ),
domainValue,
options
);
return options.getSession()
@ -206,36 +206,33 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
return (X) values;
}
assert embeddableMappingType != null && embeddableMappingType.getJavaType() == getJavaType();
final Object[] attributeValues = getAttributeValues(
final StructAttributeValues attributeValues = getAttributeValues(
embeddableMappingType,
orderMapping,
values,
options
);
//noinspection unchecked
return (X) embeddableMappingType.getRepresentationStrategy().getInstantiator().instantiate(
() -> attributeValues,
options.getSessionFactory()
);
return (X) instantiate( embeddableMappingType, attributeValues, options.getSessionFactory() );
}
};
}
private Object[] getAttributeValues(
private StructAttributeValues getAttributeValues(
EmbeddableMappingType embeddableMappingType,
int[] orderMapping,
Object[] rawJdbcValues,
WrapperOptions options) throws SQLException {
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
final Object[] attributeValues;
if ( numberOfAttributeMappings != rawJdbcValues.length || orderMapping != null ) {
attributeValues = new Object[numberOfAttributeMappings];
}
else {
attributeValues = rawJdbcValues;
}
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final StructAttributeValues attributeValues = new StructAttributeValues(
numberOfAttributeMappings,
orderMapping != null ?
null :
rawJdbcValues
);
int jdbcIndex = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
for ( int i = 0; i < size; i++ ) {
final int attributeIndex;
if ( orderMapping == null ) {
attributeIndex = i;
@ -243,9 +240,9 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
else {
attributeIndex = orderMapping[i];
}
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( attributeIndex );
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, attributeIndex );
jdbcIndex += injectAttributeValue(
attributeMapping,
modelPart,
attributeValues,
attributeIndex,
rawJdbcValues,
@ -257,13 +254,13 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
}
private int injectAttributeValue(
AttributeMapping attributeMapping,
Object[] attributeValues,
ValuedModelPart modelPart,
StructAttributeValues attributeValues,
int attributeIndex,
Object[] rawJdbcValues,
int jdbcIndex,
WrapperOptions options) throws SQLException {
final MappingType mappedType = attributeMapping.getMappedType();
final MappingType mappedType = modelPart.getMappedType();
final int jdbcValueCount;
final Object rawJdbcValue = rawJdbcValues[jdbcIndex];
if ( mappedType instanceof EmbeddableMappingType ) {
@ -271,13 +268,13 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
if ( embeddableMappingType.getAggregateMapping() != null ) {
jdbcValueCount = 1;
if ( rawJdbcValue == null ) {
attributeValues[attributeIndex] = null;
attributeValues.setAttributeValue( attributeIndex, null );
}
else {
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) embeddableMappingType.getAggregateMapping()
.getJdbcMapping()
.getJdbcType();
final Object[] subValues;
final StructAttributeValues subValues;
if ( aggregateJdbcType instanceof StructJdbcType ) {
subValues = getAttributeValues(
embeddableMappingType,
@ -287,37 +284,33 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
);
}
else {
subValues = aggregateJdbcType.extractJdbcValues( rawJdbcValue, options );
subValues = StructHelper.getAttributeValues(
embeddableMappingType,
aggregateJdbcType.extractJdbcValues( rawJdbcValue, options ),
options
);
}
attributeValues[attributeIndex] = embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate(
() -> subValues,
embeddableMappingType.findContainingEntityMapping()
.getEntityPersister()
.getFactory()
);
attributeValues.setAttributeValue(
attributeIndex,
instantiate( embeddableMappingType, subValues, options.getSessionFactory() )
);
}
}
else {
jdbcValueCount = embeddableMappingType.getJdbcValueCount();
final Object[] jdbcValues = new Object[jdbcValueCount];
System.arraycopy( rawJdbcValues, jdbcIndex, jdbcValues, 0, jdbcValues.length );
final Object[] subValues = getAttributeValues( embeddableMappingType, null, jdbcValues, options );
attributeValues[attributeIndex] = embeddableMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate(
() -> subValues,
embeddableMappingType.findContainingEntityMapping()
.getEntityPersister()
.getFactory()
);
final StructAttributeValues subValues = getAttributeValues( embeddableMappingType, null, jdbcValues, options );
attributeValues.setAttributeValue(
attributeIndex,
instantiate( embeddableMappingType, subValues, options.getSessionFactory() )
);
}
}
else {
assert attributeMapping.getJdbcTypeCount() == 1;
assert modelPart.getJdbcTypeCount() == 1;
jdbcValueCount = 1;
final JdbcMapping jdbcMapping = attributeMapping.getSingleJdbcMapping();
final JdbcMapping jdbcMapping = modelPart.getSingleJdbcMapping();
final Object jdbcValue;
if ( rawJdbcValue == null ) {
jdbcValue = null;
@ -358,10 +351,8 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
newArray = new Object[array.length];
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType;
final EmbeddableMappingType subEmbeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
final EmbeddableInstantiator instantiator = subEmbeddableMappingType.getRepresentationStrategy()
.getInstantiator();
for ( int j = 0; j < array.length; j++ ) {
final Object[] subValues = StructHelper.getAttributeValues(
final StructAttributeValues subValues = StructHelper.getAttributeValues(
subEmbeddableMappingType,
aggregateJdbcType.extractJdbcValues(
array[j],
@ -369,10 +360,7 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
),
options
);
newArray[j] = instantiator.instantiate(
() -> subValues,
options.getSessionFactory()
);
newArray[j] = instantiate( subEmbeddableMappingType, subValues, options.getSessionFactory() );
}
jdbcValue = jdbcMapping.getJdbcJavaType().wrap( newArray, options );
break;
@ -386,7 +374,7 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
break;
}
}
attributeValues[attributeIndex] = jdbcMapping.convertToDomainValue( jdbcValue );
attributeValues.setAttributeValue( attributeIndex, jdbcMapping.convertToDomainValue( jdbcValue ) );
}
return jdbcValueCount;
}
@ -406,13 +394,13 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
targetJdbcValues = jdbcValues.clone();
}
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
final AttributeMapping attributeMapping;
for ( int i = 0; i < numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); i++ ) {
final ValuedModelPart attributeMapping;
if ( orderMapping == null ) {
attributeMapping = embeddableMappingType.getAttributeMapping( i );
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, i );
}
else {
attributeMapping = embeddableMappingType.getAttributeMapping( orderMapping[i] );
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, orderMapping[i] );
}
final MappingType mappedType = attributeMapping.getMappedType();
@ -472,10 +460,8 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
newArray = new Object[array.length];
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType;
final EmbeddableMappingType subEmbeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
final EmbeddableInstantiator instantiator = subEmbeddableMappingType.getRepresentationStrategy()
.getInstantiator();
for ( int j = 0; j < array.length; j++ ) {
final Object[] subValues = StructHelper.getAttributeValues(
final StructAttributeValues subValues = StructHelper.getAttributeValues(
subEmbeddableMappingType,
aggregateJdbcType.extractJdbcValues(
array[j],
@ -483,10 +469,7 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
),
options
);
newArray[j] = instantiator.instantiate(
() -> subValues,
options.getSessionFactory()
);
newArray[j] = instantiate( subEmbeddableMappingType, subValues, options.getSessionFactory() );
}
targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( newArray, options );
break;

View File

@ -18,10 +18,10 @@ import java.util.List;
import org.hibernate.Internal;
import org.hibernate.internal.util.CharSequenceHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.type.SqlTypes;
@ -34,6 +34,9 @@ import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
import static org.hibernate.dialect.StructHelper.instantiate;
/**
* A Helper for serializing and deserializing XML, based on an {@link EmbeddableMappingType}.
*/
@ -234,18 +237,15 @@ public class XmlHelper {
array = values.toArray();
}
else {
array = new Object[embeddableMappingType.getJdbcValueCount()];
array = new Object[embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
end = fromString( embeddableMappingType, string, returnEmbeddable, options, array, START_TAG.length() );
}
assert end + END_TAG.length() == string.length();
if ( returnEmbeddable ) {
final Object[] attributeValues = StructHelper.getAttributeValues( embeddableMappingType, array, options );
final StructAttributeValues attributeValues = StructHelper.getAttributeValues( embeddableMappingType, array, options );
//noinspection unchecked
return (X) embeddableMappingType.getRepresentationStrategy().getInstantiator().instantiate(
() -> attributeValues,
options.getSessionFactory()
);
return (X) instantiate( embeddableMappingType, attributeValues, options.getSessionFactory() );
}
//noinspection unchecked
return (X) array;
@ -416,15 +416,12 @@ public class XmlHelper {
);
}
if ( returnEmbeddable ) {
final Object[] attributeValues = StructHelper.getAttributeValues(
final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
subMappingType,
subValues,
options
);
final Object subValue = subMappingType.getRepresentationStrategy()
.getInstantiator()
.instantiate( () -> attributeValues, options.getSessionFactory() );
values[selectableIndex] = subValue;
values[selectableIndex] = instantiate( subMappingType, attributeValues, options.getSessionFactory() );
}
else {
values[selectableIndex] = subValues;
@ -491,11 +488,12 @@ public class XmlHelper {
WrapperOptions options,
XMLAppender sb) {
final Object[] array = embeddableMappingType.getValues( value );
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < array.length; i++ ) {
if ( array[i] == null ) {
continue;
}
final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i );
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i );
if ( attributeMapping instanceof SelectableMapping ) {
final SelectableMapping selectable = (SelectableMapping) attributeMapping;
final String tagName = selectable.getSelectableName();

View File

@ -11,6 +11,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import jakarta.persistence.CacheRetrieveMode;
import jakarta.persistence.CacheStoreMode;
@ -163,12 +165,12 @@ public class LoaderSqlAstCreationState
}
@Override
public ImmutableFetchList visitNestedFetches(FetchParent fetchParent) {
public <R> R withNestedFetchParent(FetchParent fetchParent, Function<FetchParent, R> action) {
final FetchParent nestingFetchParent = processingState.getNestingFetchParent();
processingState.setNestingFetchParent( fetchParent );
final ImmutableFetchList fetches = fetchProcessor.visitFetches( fetchParent, this );
final R result = action.apply( fetchParent );
processingState.setNestingFetchParent( nestingFetchParent );
return fetches;
return result;
}
@Override

View File

@ -8,6 +8,7 @@ package org.hibernate.mapping;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@ -20,6 +21,7 @@ import java.util.Set;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.Remove;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.ExportableProducer;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
@ -73,8 +75,12 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
private boolean isKey;
private Boolean isGeneric;
private String roleName;
private Value discriminator;
private Map<Object, String> discriminatorValues;
private Map<String, String> subclassToSuperclass;
private final ArrayList<Property> properties = new ArrayList<>();
private Map<Property, String> propertyDeclaringClasses;
private int[] originalPropertyOrder = ArrayHelper.EMPTY_INT_ARRAY;
private Map<String,MetaAttribute> metaAttributes;
@ -89,6 +95,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
private AggregateColumn parentAggregateColumn;
private String structName;
private String[] structColumnNames;
private transient Class<?> componentClass;
// lazily computed based on 'properties' field: invalidate by setting to null when properties are modified
private transient List<Selectable> cachedSelectables;
// lazily computed based on 'properties' field: invalidate by setting to null when properties are modified
@ -123,7 +130,9 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
super( original );
this.properties.addAll( original.properties );
this.originalPropertyOrder = original.originalPropertyOrder == null ? null : original.originalPropertyOrder.clone();
this.propertyDeclaringClasses = original.propertyDeclaringClasses;
this.componentClassName = original.componentClassName;
this.componentClass = original.componentClass;
this.embedded = original.embedded;
this.parentProperty = original.parentProperty;
this.owner = original.owner;
@ -132,6 +141,9 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.metaAttributes = original.metaAttributes == null ? null : new HashMap<>( original.metaAttributes );
this.isKey = original.isKey;
this.roleName = original.roleName;
this.discriminator = original.discriminator;
this.discriminatorValues = original.discriminatorValues;
this.subclassToSuperclass = original.subclassToSuperclass;
this.customInstantiator = original.customInstantiator;
this.type = original.type;
}
@ -154,11 +166,28 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
return properties;
}
public void addProperty(Property p) {
public void addProperty(Property p, XClass declaringClass) {
properties.add( p );
if ( isPolymorphic() && declaringClass != null ) {
if ( propertyDeclaringClasses == null ) {
propertyDeclaringClasses = new HashMap<>();
}
propertyDeclaringClasses.put( p, declaringClass.getName() );
}
propertiesListModified();
}
public void addProperty(Property p) {
addProperty( p, null );
}
public String getPropertyDeclaringClass(Property p) {
if ( propertyDeclaringClasses != null ) {
return propertyDeclaringClasses.get( p );
}
return null;
}
private void propertiesListModified() {
this.cachedSelectables = null;
this.cachedColumns = null;
@ -172,9 +201,13 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
@Override
public List<Selectable> getSelectables() {
if ( cachedSelectables == null ) {
cachedSelectables = properties.stream()
final List<Selectable> selectables = properties.stream()
.flatMap( p -> p.getSelectables().stream() )
.collect( toList() );
if ( discriminator != null ) {
selectables.addAll( discriminator.getSelectables() );
}
cachedSelectables = selectables;
}
return cachedSelectables;
}
@ -185,9 +218,13 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
return cachedColumns;
}
else {
this.cachedColumns = properties.stream()
final List<Column> columns = properties.stream()
.flatMap( p -> p.getValue().getColumns().stream() )
.collect( toList() );
if ( discriminator != null ) {
columns.addAll( discriminator.getColumns() );
}
this.cachedColumns = Collections.unmodifiableList( columns );
return cachedColumns;
}
}
@ -233,6 +270,9 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
aggregatedColumns.addAll( value.getColumns() );
}
}
if ( component.isPolymorphic() ) {
aggregatedColumns.addAll( component.getDiscriminator().getColumns() );
}
}
private void notifyPropertiesAboutAggregateColumn(AggregateColumn aggregateColumn, Component component) {
@ -254,6 +294,9 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
}
}
}
if ( component.isPolymorphic() ) {
( (BasicValue) component.getDiscriminator() ).setAggregateColumn( aggregateColumn );
}
}
public AggregateColumn getParentAggregateColumn() {
@ -275,7 +318,27 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
@Override
public void checkColumnDuplication(Set<String> distinctColumns, String owner) {
if ( aggregateColumn == null ) {
checkPropertyColumnDuplication( distinctColumns, getProperties(), owner );
if ( isPolymorphic() ) {
// We can allow different subtypes reusing the same columns
// since only one subtype can exist at one time
final Map<String, Set<String>> distinctColumnsByClass = new HashMap<>();
for ( Property prop : properties ) {
if ( prop.isUpdateable() || prop.isInsertable() ) {
final String declaringClass = propertyDeclaringClasses.get( prop );
final Set<String> set = distinctColumnsByClass.computeIfAbsent(
declaringClass,
k -> new HashSet<>( distinctColumns )
);
prop.getValue().checkColumnDuplication( set, owner );
}
}
for ( Set<String> columns : distinctColumnsByClass.values() ) {
distinctColumns.addAll( columns );
}
}
else {
checkPropertyColumnDuplication( distinctColumns, getProperties(), owner );
}
}
else {
checkPropertyColumnDuplication( new HashSet<>(), getProperties(), "component '" + getRoleName() + "'" );
@ -288,21 +351,24 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
}
public Class<?> getComponentClass() throws MappingException {
if ( componentClassName == null ) {
return null;
}
else {
final ClassLoaderService classLoaderService = getMetadata()
.getMetadataBuildingOptions()
.getServiceRegistry()
.requireService( ClassLoaderService.class );
try {
return classLoaderService.classForName( componentClassName );
Class<?> result = componentClass;
if ( result == null ) {
if ( componentClassName == null ) {
return null;
}
catch (ClassLoadingException e) {
throw new MappingException("component class not found: " + componentClassName, e);
else {
try {
result = componentClass = getMetadata()
.getMetadataBuildingOptions()
.getServiceRegistry()
.requireService( ClassLoaderService.class ).classForName( componentClassName );
}
catch (ClassLoadingException e) {
throw new MappingException( "component class not found: " + componentClassName, e );
}
}
}
return result;
}
public PersistentClass getOwner() {
@ -315,6 +381,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
public void setComponentClassName(String componentClass) {
this.componentClassName = componentClass;
this.componentClass = null;
}
public void setEmbedded(boolean embedded) {
@ -426,18 +493,25 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
@Override
public boolean[] getColumnInsertability() {
final boolean[] result = new boolean[ getColumnSpan() ];
final boolean[] result = new boolean[getColumnSpan()];
int i = 0;
for ( Property prop : getProperties() ) {
final boolean[] chunk = prop.getValue().getColumnInsertability();
if ( prop.isInsertable() ) {
System.arraycopy( chunk, 0, result, i, chunk.length );
}
i += chunk.length;
i += copyFlags( prop.getValue().getColumnInsertability(), result, i, prop.isInsertable() );
}
if ( isPolymorphic() ) {
i += copyFlags( getDiscriminator().getColumnInsertability(), result, i, true );
}
assert i == getColumnSpan();
return result;
}
private static int copyFlags(boolean[] chunk, boolean[] result, int i, boolean doCopy) {
if ( doCopy ) {
System.arraycopy( chunk, 0, result, i, chunk.length );
}
return chunk.length;
}
@Override
public boolean hasAnyInsertableColumns() {
for ( Property property : properties ) {
@ -451,15 +525,15 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
@Override
public boolean[] getColumnUpdateability() {
boolean[] result = new boolean[ getColumnSpan() ];
int i=0;
boolean[] result = new boolean[getColumnSpan()];
int i = 0;
for ( Property prop : getProperties() ) {
boolean[] chunk = prop.getValue().getColumnUpdateability();
if ( prop.isUpdateable() ) {
System.arraycopy(chunk, 0, result, i, chunk.length);
}
i+=chunk.length;
i += copyFlags( prop.getValue().getColumnUpdateability(), result, i, prop.isUpdateable() );
}
if ( isPolymorphic() ) {
i += copyFlags( getDiscriminator().getColumnUpdateability(), result, i, true );
}
assert i == getColumnSpan();
return result;
}
@ -530,6 +604,34 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.roleName = roleName;
}
public Value getDiscriminator() {
return discriminator;
}
public void setDiscriminator(Value discriminator) {
this.discriminator = discriminator;
}
public boolean isPolymorphic() {
return discriminator != null;
}
public Map<Object, String> getDiscriminatorValues() {
return discriminatorValues;
}
public void setDiscriminatorValues(Map<Object, String> discriminatorValues) {
this.discriminatorValues = discriminatorValues;
}
public String getSuperclass(String subclass) {
return subclassToSuperclass.get( subclass );
}
public void setSubclassToSuperclass(Map<String, String> subclassToSuperclass) {
this.subclassToSuperclass = subclassToSuperclass;
}
@Override
public String toString() {
return getClass().getSimpleName() + '(' + componentClassName + ')';

View File

@ -1,84 +0,0 @@
/*
* 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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.type.descriptor.java.JavaType;
/**
* @author Steve Ebersole
*/
public abstract class AbstractEmbeddableRepresentationStrategy implements EmbeddableRepresentationStrategy {
private final JavaType<?> embeddableJavaType;
private final int propertySpan;
private final PropertyAccess[] propertyAccesses;
private final boolean hasCustomAccessors;
private final Map<String,Integer> attributeNameToPositionMap;
public AbstractEmbeddableRepresentationStrategy(
Component bootDescriptor,
JavaType<?> embeddableJavaType,
RuntimeModelCreationContext creationContext) {
this.propertySpan = bootDescriptor.getPropertySpan();
this.embeddableJavaType = embeddableJavaType;
this.propertyAccesses = new PropertyAccess[ propertySpan ];
this.attributeNameToPositionMap = new ConcurrentHashMap<>( propertySpan );
boolean foundCustomAccessor = false;
int i = 0;
for ( Property property : bootDescriptor.getProperties() ) {
propertyAccesses[i] = buildPropertyAccess( property );
attributeNameToPositionMap.put( property.getName(), i );
if ( !property.isBasicPropertyAccessor() ) {
foundCustomAccessor = true;
}
i++;
}
hasCustomAccessors = foundCustomAccessor;
}
protected abstract PropertyAccess buildPropertyAccess(Property bootAttributeDescriptor);
public JavaType<?> getEmbeddableJavaType() {
return embeddableJavaType;
}
@Override
public JavaType<?> getMappedJavaType() {
return getEmbeddableJavaType();
}
public int getPropertySpan() {
return propertySpan;
}
public PropertyAccess[] getPropertyAccesses() {
return propertyAccesses;
}
public boolean hasCustomAccessors() {
return hasCustomAccessors;
}
@Override
public PropertyAccess resolvePropertyAccess(Property bootAttributeDescriptor) {
return propertyAccesses[ attributeNameToPositionMap.get( bootAttributeDescriptor.getName() ) ];
}
}

View File

@ -10,10 +10,12 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import org.hibernate.AssertionFailure;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.internal.EntityManagerMessageLogger;
import org.hibernate.internal.HEMLogging;
import org.hibernate.mapping.AggregateColumn;
@ -255,7 +257,7 @@ public class AttributeFactory {
final Class<Y> embeddableClass = (Class<Y>) component.getComponentClass();
if ( !component.isGeneric() ) {
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component);
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component );
if ( cached != null ) {
return cached;
}
@ -266,9 +268,30 @@ public class AttributeFactory {
false,
context.getJpaMetamodel()
);
context.registerEmbeddableType( embeddableType, component);
if ( component.isPolymorphic() ) {
final java.util.Collection<String> embeddableSubclasses = component.getDiscriminatorValues().values();
final java.util.Map<String, EmbeddableTypeImpl<?>> domainTypes = new HashMap<>();
domainTypes.put( embeddableType.getTypeName(), embeddableType );
final ClassLoaderService cls = context.getJpaMetamodel().getServiceRegistry().requireService(
ClassLoaderService.class
);
for ( final String subclassName : embeddableSubclasses ) {
if ( domainTypes.containsKey( subclassName ) ) {
assert subclassName.equals( embeddableType.getTypeName() );
continue;
}
final Class<?> subclass = cls.classForName( subclassName );
context.registerEmbeddableType( new EmbeddableTypeImpl<>(
context.getJavaTypeRegistry().resolveManagedTypeDescriptor( subclass ),
domainTypes.get( component.getSuperclass( subclassName ) ),
false,
context.getJpaMetamodel()
), component );
}
}
return embeddableType;
}

View File

@ -11,7 +11,6 @@ import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.type.descriptor.java.JavaType;
import static org.hibernate.bytecode.spi.ReflectionOptimizer.InstantiationOptimizer;
@ -24,10 +23,10 @@ public class EmbeddableInstantiatorPojoOptimized extends AbstractPojoInstantiato
private final InstantiationOptimizer instantiationOptimizer;
public EmbeddableInstantiatorPojoOptimized(
JavaType<?> javaType,
Class<?> embeddableClass,
Supplier<EmbeddableMappingType> embeddableMappingAccess,
InstantiationOptimizer instantiationOptimizer) {
super( javaType.getJavaTypeClass() );
super( embeddableClass );
this.embeddableMappingAccess = embeddableMappingAccess;
this.instantiationOptimizer = instantiationOptimizer;
}

View File

@ -17,7 +17,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.type.descriptor.java.JavaType;
/**
* Support for instantiating embeddables as POJO representation
@ -28,11 +27,11 @@ public class EmbeddableInstantiatorPojoStandard extends AbstractPojoInstantiator
private final Supplier<EmbeddableMappingType> embeddableMappingAccess;
private final Constructor<?> constructor;
public EmbeddableInstantiatorPojoStandard(JavaType<?> javaType, Supplier<EmbeddableMappingType> embeddableMappingAccess) {
super( javaType.getJavaTypeClass() );
public EmbeddableInstantiatorPojoStandard(Class<?> embeddableClass, Supplier<EmbeddableMappingType> embeddableMappingAccess) {
super( embeddableClass );
this.embeddableMappingAccess = embeddableMappingAccess;
this.constructor = resolveConstructor( javaType.getJavaTypeClass() );
this.constructor = resolveConstructor( embeddableClass );
}
protected static Constructor<?> resolveConstructor(Class<?> mappedPojoClass) {

View File

@ -6,12 +6,16 @@
*/
package org.hibernate.metamodel.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
@ -25,6 +29,7 @@ import org.hibernate.mapping.Property;
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.RuntimeModelCreationContext;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyIndexBackRefImpl;
@ -36,14 +41,21 @@ import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.internal.CompositeUserTypeJavaTypeWrapper;
import org.hibernate.usertype.CompositeUserType;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
/**
* @author Steve Ebersole
*/
public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepresentationStrategy {
private final StrategySelector strategySelector;
public class EmbeddableRepresentationStrategyPojo implements EmbeddableRepresentationStrategy {
private final JavaType<?> embeddableJavaType;
private final PropertyAccess[] propertyAccesses;
private final Map<String, Integer> attributeNameToPositionMap;
private final StrategySelector strategySelector;
private final ReflectionOptimizer reflectionOptimizer;
private final EmbeddableInstantiator instantiator;
private final Map<Object, EmbeddableInstantiator> instantiatorsByDiscriminator;
private final Map<String, EmbeddableInstantiator> instantiatorsByClass;
public EmbeddableRepresentationStrategyPojo(
Component bootDescriptor,
@ -51,22 +63,68 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
EmbeddableInstantiator customInstantiator,
CompositeUserType<Object> compositeUserType,
RuntimeModelCreationContext creationContext) {
super(
this.embeddableJavaType = resolveEmbeddableJavaType( bootDescriptor, compositeUserType, creationContext );
final int propertySpan = bootDescriptor.getPropertySpan();
this.propertyAccesses = new PropertyAccess[propertySpan];
this.attributeNameToPositionMap = new HashMap<>( propertySpan );
// We need access to the Class objects, used only during initialization
final Map<String, Class<?>> subclassesByName = getSubclassesByName( bootDescriptor, creationContext );
boolean foundCustomAccessor = false;
for ( int i = 0; i < bootDescriptor.getProperties().size(); i++ ) {
final Property property = bootDescriptor.getProperty( i );
final Class<?> embeddableClass = bootDescriptor.isPolymorphic() ?
castNonNull( subclassesByName ).get( bootDescriptor.getPropertyDeclaringClass( property ) ) :
getEmbeddableJavaType().getJavaTypeClass();
propertyAccesses[i] = buildPropertyAccess( property, embeddableClass, customInstantiator == null );
attributeNameToPositionMap.put( property.getName(), i );
if ( !property.isBasicPropertyAccessor() ) {
foundCustomAccessor = true;
}
}
boolean hasCustomAccessors = foundCustomAccessor;
this.strategySelector = creationContext.getServiceRegistry().getService( StrategySelector.class );
this.reflectionOptimizer = buildReflectionOptimizer(
bootDescriptor,
resolveEmbeddableJavaType( bootDescriptor, compositeUserType, creationContext ),
hasCustomAccessors,
propertyAccesses,
creationContext
);
assert bootDescriptor.getComponentClass() != null;
this.strategySelector = creationContext.getServiceRegistry().getService( StrategySelector.class );
this.reflectionOptimizer = buildReflectionOptimizer( bootDescriptor, creationContext );
this.instantiator = customInstantiator != null
? customInstantiator
: determineInstantiator( bootDescriptor, runtimeDescriptorAccess, creationContext );
if ( bootDescriptor.isPolymorphic() ) {
final int size = bootDescriptor.getDiscriminatorValues().size();
this.instantiatorsByDiscriminator = new HashMap<>( size );
this.instantiatorsByClass = new HashMap<>( size );
for ( Map.Entry<Object, String> discriminator : bootDescriptor.getDiscriminatorValues().entrySet() ) {
final String className = discriminator.getValue();
final EmbeddableInstantiator instantiator = determineInstantiator(
bootDescriptor,
castNonNull( subclassesByName ).get( className ),
reflectionOptimizer,
runtimeDescriptorAccess,
creationContext
);
instantiatorsByDiscriminator.put( discriminator.getKey(), instantiator );
instantiatorsByClass.put( className, instantiator );
}
this.instantiator = null;
}
else {
this.instantiator = customInstantiator != null ?
customInstantiator :
determineInstantiator(
bootDescriptor,
bootDescriptor.getComponentClass(),
reflectionOptimizer,
runtimeDescriptorAccess,
creationContext
);
this.instantiatorsByDiscriminator = null;
this.instantiatorsByClass = null;
}
}
private static <T> JavaType<T> resolveEmbeddableJavaType(
@ -83,40 +141,39 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
);
}
private EmbeddableInstantiator determineInstantiator(
private static EmbeddableInstantiator determineInstantiator(
Component bootDescriptor,
Class<?> embeddableClass,
ReflectionOptimizer reflectionOptimizer,
Supplier<EmbeddableMappingType> runtimeDescriptorAccess,
RuntimeModelCreationContext creationContext) {
if ( reflectionOptimizer != null && reflectionOptimizer.getInstantiationOptimizer() != null ) {
final ReflectionOptimizer.InstantiationOptimizer instantiationOptimizer = reflectionOptimizer.getInstantiationOptimizer();
return new EmbeddableInstantiatorPojoOptimized(
getEmbeddableJavaType(),
embeddableClass,
runtimeDescriptorAccess,
instantiationOptimizer
);
}
if ( bootDescriptor.isEmbedded() && ReflectHelper.isAbstractClass( bootDescriptor.getComponentClass() ) ) {
if ( bootDescriptor.isEmbedded() && ReflectHelper.isAbstractClass( embeddableClass ) ) {
return new EmbeddableInstantiatorProxied(
bootDescriptor.getComponentClass(),
embeddableClass,
runtimeDescriptorAccess,
creationContext.getServiceRegistry()
.requireService( ProxyFactoryFactory.class )
.buildBasicProxyFactory( bootDescriptor.getComponentClass() )
.buildBasicProxyFactory( embeddableClass )
);
}
return new EmbeddableInstantiatorPojoStandard( getEmbeddableJavaType(), runtimeDescriptorAccess );
return new EmbeddableInstantiatorPojoStandard( embeddableClass, runtimeDescriptorAccess );
}
@Override
public ReflectionOptimizer getReflectionOptimizer() {
return reflectionOptimizer;
}
@Override
protected PropertyAccess buildPropertyAccess(Property bootAttributeDescriptor) {
PropertyAccessStrategy strategy = bootAttributeDescriptor.getPropertyAccessStrategy( getEmbeddableJavaType().getJavaTypeClass() );
private PropertyAccess buildPropertyAccess(
Property bootAttributeDescriptor,
Class<?> embeddableClass,
boolean requireSetters) {
PropertyAccessStrategy strategy = bootAttributeDescriptor.getPropertyAccessStrategy( embeddableClass );
if ( strategy == null ) {
final String propertyAccessorName = bootAttributeDescriptor.getPropertyAccessorName();
@ -160,17 +217,18 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
}
return strategy.buildPropertyAccess(
getEmbeddableJavaType().getJavaTypeClass(),
embeddableClass,
bootAttributeDescriptor.getName(),
instantiator instanceof StandardEmbeddableInstantiator
requireSetters
);
}
private ReflectionOptimizer buildReflectionOptimizer(
private static ReflectionOptimizer buildReflectionOptimizer(
Component bootDescriptor,
boolean hasCustomAccessors,
PropertyAccess[] propertyAccesses,
RuntimeModelCreationContext creationContext) {
if ( hasCustomAccessors() || bootDescriptor.getCustomInstantiator() != null || bootDescriptor.getInstantiator() != null ) {
if ( hasCustomAccessors || bootDescriptor.getCustomInstantiator() != null || bootDescriptor.getInstantiator() != null || bootDescriptor.isPolymorphic() ) {
return null;
}
@ -178,7 +236,7 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
int i = 0;
for ( Property property : bootDescriptor.getProperties() ) {
propertyAccessMap.put( property.getName(), getPropertyAccesses()[i] );
propertyAccessMap.put( property.getName(), propertyAccesses[i] );
i++;
}
@ -187,6 +245,52 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
.getReflectionOptimizer( bootDescriptor.getComponentClass(), propertyAccessMap );
}
private static Map<String, Class<?>> getSubclassesByName(
Component bootDescriptor,
RuntimeModelCreationContext creationContext) {
if ( bootDescriptor.isPolymorphic() ) {
final Collection<String> subclassNames = bootDescriptor.getDiscriminatorValues().values();
final Map<String, Class<?>> result = new HashMap<>( subclassNames.size() );
final ClassLoaderService classLoaderService = creationContext.getMetadata()
.getMetadataBuildingOptions()
.getServiceRegistry()
.requireService( ClassLoaderService.class );
for ( final String subclassName : subclassNames ) {
final Class<?> embeddableClass;
if ( subclassName.equals( bootDescriptor.getComponentClassName() ) ) {
embeddableClass = bootDescriptor.getComponentClass();
}
else {
embeddableClass = classLoaderService.classForName( subclassName );
}
result.put( subclassName, embeddableClass );
}
return result;
}
else {
return null;
}
}
public JavaType<?> getEmbeddableJavaType() {
return embeddableJavaType;
}
@Override
public JavaType<?> getMappedJavaType() {
return getEmbeddableJavaType();
}
@Override
public ReflectionOptimizer getReflectionOptimizer() {
return reflectionOptimizer;
}
@Override
public PropertyAccess resolvePropertyAccess(Property bootAttributeDescriptor) {
return propertyAccesses[ attributeNameToPositionMap.get( bootAttributeDescriptor.getName() ) ];
}
@Override
public RepresentationMode getMode() {
return RepresentationMode.POJO;
@ -194,6 +298,27 @@ public class EmbeddableRepresentationStrategyPojo extends AbstractEmbeddableRepr
@Override
public EmbeddableInstantiator getInstantiator() {
assert instantiator != null && instantiatorsByDiscriminator == null && instantiatorsByClass == null;
return instantiator;
}
@Override
public EmbeddableInstantiator getInstantiatorForDiscriminator(Object discriminatorValue) {
if ( instantiator != null ) {
assert instantiatorsByDiscriminator == null;
return instantiator;
}
assert instantiatorsByDiscriminator != null;
return instantiatorsByDiscriminator.get( discriminatorValue );
}
@Override
public EmbeddableInstantiator getInstantiatorForClass(String className) {
if ( instantiator != null ) {
assert instantiatorsByClass == null;
return instantiator;
}
assert instantiatorsByClass != null;
return instantiatorsByClass.get( className );
}
}

View File

@ -407,6 +407,9 @@ public class MetadataContext {
for ( EmbeddableDomainType<?> embeddable : processingEmbeddables ) {
final Component component = componentByEmbeddable.get( embeddable );
for ( Property property : component.getProperties() ) {
if ( component.isPolymorphic() && !component.getPropertyDeclaringClass( property ).equals( embeddable.getTypeName() ) ) {
continue;
}
final PersistentAttribute<Object, ?> attribute =
attributeFactory.buildAttribute( (ManagedDomainType<Object>) embeddable, property );
if ( attribute != null ) {

View File

@ -6,11 +6,16 @@
*/
package org.hibernate.metamodel.mapping;
import org.hibernate.HibernateException;
import org.hibernate.engine.FetchTiming;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.type.descriptor.java.JavaType;
/**
@ -30,6 +35,15 @@ public interface DiscriminatorMapping extends VirtualModelPart, BasicValuedModel
*/
DiscriminatorConverter<?,?> getValueConverter();
/**
* Retrieve the {@linkplain DiscriminatorValueDetails details} for a particular discriminator value.
*
* @throws HibernateException if there is value matching the provided one
*/
default DiscriminatorValueDetails resolveDiscriminatorValue(Object discriminatorValue) {
return getValueConverter().getDetailsForDiscriminatorValue( discriminatorValue );
}
JdbcMapping getUnderlyingJdbcMapping();
/**
@ -59,4 +73,13 @@ public interface DiscriminatorMapping extends VirtualModelPart, BasicValuedModel
JdbcMapping jdbcMappingToUse,
TableGroup tableGroup,
SqlAstCreationState creationState);
@Override
BasicFetch<?> generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
String resultVariable,
DomainResultCreationState creationState);
}

View File

@ -0,0 +1,129 @@
/*
* 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.mapping;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.metamodel.mapping.internal.EmbeddableDiscriminatorValueDetailsImpl;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
/**
* Handles conversion of discriminator values for embeddable subtype classes
* to their domain typed form.
*
* @author Marco Belladelli
* @see EmbeddableDiscriminatorMapping
*/
public class EmbeddableDiscriminatorConverter<O, R> extends DiscriminatorConverter<O, R> {
public static <O, R> EmbeddableDiscriminatorConverter<O, R> fromValueMappings(
NavigableRole role,
JavaType<O> domainJavaType,
BasicType<R> underlyingJdbcMapping,
Map<Object, String> valueMappings) {
final List<DiscriminatorValueDetails> valueDetailsList = new ArrayList<>( valueMappings.size() );
valueMappings.forEach( (value, embeddableClassName) -> valueDetailsList.add( new EmbeddableDiscriminatorValueDetailsImpl(
value,
embeddableClassName
) ) );
return new EmbeddableDiscriminatorConverter<>(
role,
domainJavaType,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueDetailsList
);
}
private final Map<Object, DiscriminatorValueDetails> discriminatorValueToDetailsMap;
private final Map<String, DiscriminatorValueDetails> embeddableClassNameToDetailsMap;
public EmbeddableDiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
List<DiscriminatorValueDetails> valueMappings) {
super( discriminatorRole, domainJavaType, relationalJavaType );
this.discriminatorValueToDetailsMap = new HashMap<>( valueMappings.size() );
this.embeddableClassNameToDetailsMap = new HashMap<>( valueMappings.size() );
valueMappings.forEach( valueDetails -> {
discriminatorValueToDetailsMap.put( valueDetails.getValue(), valueDetails );
embeddableClassNameToDetailsMap.put( valueDetails.getIndicatedEntityName(), valueDetails );
} );
}
@Override
public O toDomainValue(R relationalForm) {
assert relationalForm == null || getRelationalJavaType().isInstance( relationalForm );
final DiscriminatorValueDetails matchingValueDetails = getDetailsForDiscriminatorValue( relationalForm );
if ( matchingValueDetails == null ) {
throw new IllegalStateException( "Could not resolve discriminator value" );
}
//noinspection unchecked
return (O) matchingValueDetails.getIndicatedEntityName();
}
@Override
public R toRelationalValue(O domainForm) {
assert domainForm == null || domainForm instanceof String;
if ( domainForm == null ) {
return null;
}
final String embeddableClassName = (String) domainForm;
//noinspection unchecked
return (R) getDetailsForEntityName( embeddableClassName ).getValue();
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
final DiscriminatorValueDetails valueMatch = discriminatorValueToDetailsMap.get( value );
if ( valueMatch != null ) {
return valueMatch;
}
throw new HibernateException( "Unrecognized discriminator value: " + value );
}
@Override
public DiscriminatorValueDetails getDetailsForEntityName(String embeddableClassName) {
final DiscriminatorValueDetails valueDetails = embeddableClassNameToDetailsMap.get( embeddableClassName );
if ( valueDetails != null ) {
return valueDetails;
}
throw new AssertionFailure( "Unrecognized embeddable class: " + embeddableClassName );
}
@Override
public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
discriminatorValueToDetailsMap.forEach( (value, detail) -> consumer.accept( detail ) );
}
@Override
public <X> X fromValueDetails(Function<DiscriminatorValueDetails, X> handler) {
for ( DiscriminatorValueDetails detail : discriminatorValueToDetailsMap.values() ) {
final X result = handler.apply( detail );
if ( result != null ) {
return result;
}
}
return null;
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.mapping;
import org.hibernate.HibernateException;
import org.hibernate.sql.results.graph.FetchOptions;
/**
* Details about the discriminator for an embeddable hierarchy.
*
* @author Marco Belladelli
* @see EmbeddableMappingType#getDiscriminatorMapping()
*/
public interface EmbeddableDiscriminatorMapping extends DiscriminatorMapping, FetchOptions {
/**
* Retrieve the relational discriminator value corresponding to the provided embeddable class name.
*
* @throws HibernateException if the embeddable class name is not handled by this discriminator
*/
default Object getDiscriminatorValue(String embeddableClassName) {
return getValueConverter().getDetailsForEntityName( embeddableClassName ).getValue();
}
}

View File

@ -36,6 +36,34 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
boolean isCreateEmptyCompositesEnabled();
/**
* Returns the {@linkplain EmbeddableDiscriminatorMapping discriminator mapping}
* if this discriminator type is polymorphic, {@code null} otherwise.
*/
default EmbeddableDiscriminatorMapping getDiscriminatorMapping() {
return null;
}
/**
* Returns {@code true} if this embeddable mapping type defines a
* discriminator-based inheritance hierarchy, {@code false} otherwise.
*/
default boolean isPolymorphic() {
return getDiscriminatorMapping() != null;
}
/**
* Returns {@code true} if the provided embeddable class contains the
* specified attribute mapping, {@code false} otherwise.
* @implNote This method always returns {@code true} for non-polymorphic embeddable types
*
* @param embeddableClassName the embeddable subclass in which the attribute must be declared
* @param attributeMapping the attribute to check
*/
default boolean declaresAttribute(String embeddableClassName, AttributeMapping attributeMapping) {
return true;
}
default SelectableMapping getAggregateMapping() {
return null;
}
@ -127,6 +155,9 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
count += attributeMapping.getJdbcTypeCount();
}
}
if ( isPolymorphic() && columnIndex == count ) {
return getDiscriminatorMapping();
}
return null;
}
@ -175,6 +206,9 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
offset += jdbcTypeCount;
}
}
if ( isPolymorphic() && getDiscriminatorMapping().getSelectableName().equals( selectableName ) ) {
return offset;
}
return -1;
}

View File

@ -107,7 +107,7 @@ public interface EmbeddableValuedModelPart extends ValuedModelPart, Fetchable, F
@Override
default int getNumberOfFetchables() {
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
return getEmbeddableTypeDescriptor().getNumberOfFetchables();
}
@Override

View File

@ -58,34 +58,6 @@ public interface EntityDiscriminatorMapping extends DiscriminatorMapping, FetchO
return -2;
}
/**
* Retrieve the details for a particular discriminator value.
*
* Returns {@code null} if there is no match.
*/
DiscriminatorValueDetails resolveDiscriminatorValue(Object value);
/**
* Create the appropriate SQL expression for this discriminator
*
* @param jdbcMappingToUse The JDBC mapping to use. This allows opting between
* the "domain result type" (aka Class) and the "underlying type" (Integer, String, etc)
*/
Expression resolveSqlExpression(
NavigablePath navigablePath,
JdbcMapping jdbcMappingToUse,
TableGroup tableGroup,
SqlAstCreationState creationState);
@Override
BasicFetch<?> generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
String resultVariable,
DomainResultCreationState creationState);
@Override
default FetchOptions getMappedFetchOptions() {
return this;

View File

@ -17,6 +17,7 @@ import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole;
@ -44,22 +45,22 @@ public abstract class AbstractDiscriminatorMapping implements EntityDiscriminato
private final BasicType<Object> underlyingJdbcMapping;
private final DiscriminatorType<Object> discriminatorType;
private final EntityMappingType entityDescriptor;
private final ManagedMappingType mappingType;
public AbstractDiscriminatorMapping(
EntityMappingType entityDescriptor,
ManagedMappingType mappingType,
DiscriminatorType<Object> discriminatorType,
BasicType<Object> underlyingJdbcMapping) {
this.underlyingJdbcMapping = underlyingJdbcMapping;
this.entityDescriptor = entityDescriptor;
this.mappingType = mappingType;
this.role = entityDescriptor.getNavigableRole().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME );
this.role = mappingType.getNavigableRole().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME );
this.discriminatorType = discriminatorType;
}
public EntityMappingType getEntityDescriptor() {
return entityDescriptor;
return mappingType.asEntityMappingType();
}
@Override
@ -86,14 +87,9 @@ public abstract class AbstractDiscriminatorMapping implements EntityDiscriminato
return discriminatorType;
}
@Override
public DiscriminatorValueDetails resolveDiscriminatorValue(Object value) {
return discriminatorType.getValueConverter().getDetailsForDiscriminatorValue( value );
}
@Override
public EntityMappingType findContainingEntityMapping() {
return entityDescriptor;
return mappingType.findContainingEntityMapping();
}
@Override

View File

@ -33,6 +33,7 @@ import org.hibernate.mapping.Value;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -91,6 +92,10 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
return optimizer.getAccessOptimizer().getPropertyValues( compositeInstance );
}
return getAttributeValues( compositeInstance );
}
protected Object[] getAttributeValues(Object compositeInstance) {
final Object[] results = new Object[getNumberOfAttributeMappings()];
for ( int i = 0; i < results.length; i++ ) {
final Getter getter = getAttributeMapping( i ).getAttributeMetadata()
@ -108,9 +113,13 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
optimizer.getAccessOptimizer().setPropertyValues( component, values );
}
else {
for ( int i = 0; i < values.length; i++ ) {
getAttributeMapping( i ).getPropertyAccess().getSetter().set( component, values[i] );
}
setAttributeValues( component, values );
}
}
protected void setAttributeValues(Object component, Object[] values) {
for ( int i = 0; i < values.length; i++ ) {
getAttributeMapping( i ).getPropertyAccess().getSetter().set( component, values[i] );
}
}
@ -641,6 +650,13 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
attributeMapping.addToCacheKey( cacheKey, attributeMapping.getValue( value ), session );
}
}
if ( isPolymorphic() ) {
final EmbeddableDiscriminatorMapping discriminatorMapping = getDiscriminatorMapping();
final Object discriminatorValue = value != null ?
discriminatorMapping.getDiscriminatorValue( value.getClass().getName() )
: null;
discriminatorMapping.addToCacheKey( cacheKey, discriminatorValue, session );
}
}
@Override
@ -727,6 +743,10 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
)
);
if ( getDiscriminatorMapping() != null ) {
getDiscriminatorMapping().forEachSelectable( (index, selection) -> selectableMappings.add( selection ) );
}
this.selectableMappings = new SelectableMappingsImpl( selectableMappings.toArray( new SelectableMapping[0] ) );
return true;

View File

@ -295,7 +295,7 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
}
@Override
public Fetch generateFetch(
public BasicFetch<?> generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,

View File

@ -51,8 +51,7 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
String[] notNullColumnNames,
String[] discriminatorValues,
boolean[] discriminatorAbstract,
DiscriminatorType<?> incomingDiscriminatorType,
MappingModelCreationProcess creationProcess) {
DiscriminatorType<?> incomingDiscriminatorType) {
//noinspection unchecked
super( entityDescriptor, (DiscriminatorType<Object>) incomingDiscriminatorType, (BasicType<Object>) incomingDiscriminatorType.getUnderlyingJdbcMapping() );

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorConverter;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
/**
* Implementation of {@link DiscriminatorValueDetails} used for embeddable inheritance.
*
* @author Marco Belladelli
* @see EmbeddableDiscriminatorConverter
* @see EmbeddableDiscriminatorMapping
*/
public class EmbeddableDiscriminatorValueDetailsImpl implements DiscriminatorValueDetails {
final Object value;
final String embeddableClassName;
public EmbeddableDiscriminatorValueDetailsImpl(Object value, String embeddableClassName) {
this.value = value;
this.embeddableClassName = embeddableClassName;
}
@Override
public Object getValue() {
return value;
}
@Override
public String getIndicatedEntityName() {
return embeddableClassName;
}
@Override
public EntityMappingType getIndicatedEntity() {
throw new UnsupportedOperationException();
}
}

View File

@ -7,11 +7,16 @@
package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.hibernate.MappingException;
import org.hibernate.SharedSessionContract;
import org.hibernate.WrongClassException;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.aggregate.AggregateSupport;
@ -28,15 +33,20 @@ import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorConverter;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
@ -44,7 +54,9 @@ import org.hibernate.metamodel.mapping.SelectablePath;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.DiscriminatorHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup;
@ -64,12 +76,15 @@ import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.persister.entity.DiscriminatorHelper.getDiscriminatorType;
import static org.hibernate.type.SqlTypes.JSON;
import static org.hibernate.type.SqlTypes.JSON_ARRAY;
import static org.hibernate.type.SqlTypes.SQLXML;
@ -155,6 +170,8 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
private final EmbeddableRepresentationStrategy representationStrategy;
private final EmbeddableValuedModelPart valueMapping;
private final EmbeddableDiscriminatorMapping discriminatorMapping;
private final Map<String, Set<AttributeMapping>> declaredAttributesBySubclass;
private final boolean createEmptyCompositesEnabled;
private final SelectableMapping aggregateMapping;
@ -175,6 +192,8 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
this.embeddableJtd = representationStrategy.getMappedJavaType();
this.valueMapping = embeddedPartBuilder.apply( this );
this.discriminatorMapping = generateDiscriminatorMapping( bootDescriptor, creationContext );
this.declaredAttributesBySubclass = bootDescriptor.isPolymorphic() ? new HashMap<>() : null;
this.createEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
@ -317,6 +336,8 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
this.embeddableJtd = inverseMappingType.getJavaType();
this.representationStrategy = inverseMappingType.getRepresentationStrategy();
this.valueMapping = valueMapping;
this.discriminatorMapping = null;
this.declaredAttributesBySubclass = null;
this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled();
this.aggregateMapping = null;
this.aggregateMappingRequiresColumnWriter = false;
@ -400,7 +421,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
// Reset the attribute mappings that were added in previous attempts
attributeMappings.clear();
for ( Property bootPropertyDescriptor : bootDescriptor.getProperties() ) {
for ( final Property bootPropertyDescriptor : bootDescriptor.getProperties() ) {
final AttributeMapping attributeMapping;
final Type subtype = subtypes[attributeIndex];
@ -594,7 +615,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
this,
entityPersister,
(EntityType) subtype,
getRepresentationStrategy().resolvePropertyAccess( bootPropertyDescriptor ),
representationStrategy.resolvePropertyAccess( bootPropertyDescriptor ),
compositeType.getCascadeStyle( attributeIndex ),
creationProcess
);
@ -611,6 +632,18 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
);
}
if ( isPolymorphic() ) {
final String declaringClass = bootDescriptor.getPropertyDeclaringClass( bootPropertyDescriptor );
for ( final String subclass : bootDescriptor.getDiscriminatorValues().values() ) {
if ( isDefinedInClassOrSuperclass( bootDescriptor, declaringClass, subclass ) ) {
declaredAttributesBySubclass.computeIfAbsent(
subclass,
k -> new HashSet<>()
).add( attributeMapping );
}
}
}
addAttribute( attributeMapping );
attributeIndex++;
@ -618,13 +651,23 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
// We need the attribute mapping types to finish initialization first before we can build the column mappings
creationProcess.registerInitializationCallback(
"EmbeddableMappingType(" + getEmbeddedValueMapping().getNavigableRole().getFullPath() + ")#initColumnMappings",
"EmbeddableMappingType(" + valueMapping.getNavigableRole().getFullPath() + ")#initColumnMappings",
this::initColumnMappings
);
return true;
}
private boolean isDefinedInClassOrSuperclass(Component bootDescriptor, String declaringClass, String subclass) {
while ( subclass != null ) {
if ( declaringClass.equals( subclass ) ) {
return true;
}
subclass = bootDescriptor.getSuperclass( subclass );
}
return false;
}
private static MutabilityPlan<?> getMutabilityPlan(boolean updateable) {
if ( updateable ) {
return new MutabilityPlan<>() {
@ -654,12 +697,91 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
}
}
private EmbeddableDiscriminatorMapping generateDiscriminatorMapping(
Component bootDescriptor,
RuntimeModelCreationContext creationContext) {
final Value discriminator = bootDescriptor.getDiscriminator();
if ( discriminator == null ) {
return null;
}
final Selectable selectable = discriminator.getSelectables().get( 0 );
final String discriminatorColumnExpression;
final String columnDefinition;
final String name;
final Long length;
final Integer precision;
final Integer scale;
final boolean isFormula = discriminator.hasFormula();
if ( isFormula ) {
final Formula formula = (Formula) selectable;
discriminatorColumnExpression = name = formula.getTemplate(
creationContext.getDialect(),
creationContext.getTypeConfiguration(),
creationContext.getFunctionRegistry()
);
columnDefinition = null;
length = null;
precision = null;
scale = null;
}
else {
final Column column = discriminator.getColumns().get( 0 );
assert column != null : "Embeddable discriminators require a column";
discriminatorColumnExpression = column.getReadExpr( creationContext.getDialect() );
columnDefinition = column.getSqlType();
name = column.getName();
length = column.getLength();
precision = column.getPrecision();
scale = column.getScale();
}
final DiscriminatorType<?> discriminatorType = buildDiscriminatorType(
bootDescriptor,
creationContext
);
return new ExplicitColumnDiscriminatorMappingImpl(
this,
name,
bootDescriptor.getTable().getName(),
discriminatorColumnExpression,
isFormula,
true,
true,
columnDefinition,
selectable.getCustomReadExpression(),
length,
precision,
scale,
discriminatorType
);
}
private DiscriminatorType<?> buildDiscriminatorType(
Component bootDescriptor,
RuntimeModelCreationContext creationContext) {
final JavaTypeRegistry javaTypeRegistry = creationContext.getSessionFactory().getTypeConfiguration().getJavaTypeRegistry();
final JavaType<String> domainJavaType = javaTypeRegistry.resolveDescriptor( String.class );
final BasicType<?> discriminatorType = getDiscriminatorType( bootDescriptor );
final DiscriminatorConverter<String, ?> converter = EmbeddableDiscriminatorConverter.fromValueMappings(
getNavigableRole().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
domainJavaType,
discriminatorType,
bootDescriptor.getDiscriminatorValues()
);
return new DiscriminatorTypeImpl<>( discriminatorType, converter );
}
public EmbeddableValuedModelPart getEmbeddedValueMapping() {
return valueMapping;
}
@Override
public EmbeddableDiscriminatorMapping getDiscriminatorMapping() {
return discriminatorMapping;
}
@Override
public JavaType<?> getMappedJavaType() {
return embeddableJtd;
@ -693,6 +815,73 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
);
}
@Override
public boolean declaresAttribute(String embeddableClassName, AttributeMapping attributeMapping) {
if ( declaredAttributesBySubclass == null ) {
return true;
}
final Set<AttributeMapping> declaredAttributes = declaredAttributesBySubclass.get( embeddableClassName );
return declaredAttributes != null && declaredAttributes.contains( attributeMapping );
}
@Override
public Object getValue(Object instance, int position) {
final AttributeMapping attributeMapping = getAttributeMapping( position );
if ( declaresAttribute( instance.getClass().getName(), attributeMapping ) ) {
return attributeMapping.getValue( instance );
}
return null;
}
@Override
protected Object[] getAttributeValues(Object compositeInstance) {
if ( !isPolymorphic() ) {
return super.getAttributeValues( compositeInstance );
}
else {
final int numberOfAttributes = getNumberOfAttributeMappings();
final Object[] results = new Object[numberOfAttributes + 1];
final String compositeClassName = compositeInstance.getClass().getName();
int i = 0;
for ( ; i < numberOfAttributes; i++ ) {
final AttributeMapping attributeMapping = getAttributeMapping( i );
if ( declaresAttribute( compositeClassName, attributeMapping ) ) {
final Getter getter = attributeMapping.getAttributeMetadata()
.getPropertyAccess()
.getGetter();
results[i] = getter.get( compositeInstance );
}
else {
results[i] = null;
}
}
results[i] = compositeInstance.getClass().getName();
return results;
}
}
@Override
protected void setAttributeValues(Object component, Object[] values) {
if ( !isPolymorphic() ) {
super.setAttributeValues( component, values );
}
else {
final String compositeClassName = component.getClass().getName();
for ( int i = 0; i < getNumberOfAttributeMappings(); i++ ) {
final AttributeMapping attributeMapping = getAttributeMapping( i );
if ( declaresAttribute( compositeClassName, attributeMapping ) ) {
attributeMapping.getPropertyAccess().getSetter().set( component, values[i] );
}
else if ( values[i] != null ) {
throw new IllegalArgumentException( String.format(
"Unexpected non-null value for embeddable subtype '%s'",
compositeClassName
) );
}
}
}
}
@Override
public <X, Y> int breakDownJdbcValues(
Object domainValue,
@ -705,9 +894,9 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
int span = 0;
if ( domainValue instanceof Object[] ) {
final Object[] values = (Object[]) domainValue;
assert values.length == size;
for ( int i = 0; i < size; i++ ) {
assert values.length == size + ( isPolymorphic() ? 1 : 0 );
int i = 0;
for ( ; i < size; i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
if ( !attributeMapping.isPluralAttributeMapping() ) {
final Object attributeValue = values[i];
@ -721,12 +910,16 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
);
}
}
if ( isPolymorphic() ) {
span += discriminatorMapping.breakDownJdbcValues( values[i], offset + span, x, y, valueConsumer, session );
}
}
else {
final String compositeClassName = domainValue == null ? null : domainValue.getClass().getName();
for ( int i = 0; i < size; i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
if ( !attributeMapping.isPluralAttributeMapping() ) {
final Object attributeValue = domainValue == null
final Object attributeValue = domainValue == null || !declaresAttribute( compositeClassName, attributeMapping )
? null
: attributeMapping.getPropertyAccess().getGetter().get( domainValue );
span += attributeMapping.breakDownJdbcValues(
@ -739,6 +932,10 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
);
}
}
if ( isPolymorphic() ) {
final Object d = domainValue == null ? null : discriminatorMapping.getDiscriminatorValue( compositeClassName );
span += discriminatorMapping.breakDownJdbcValues( d, offset + span, x, y, valueConsumer, session );
}
}
return span;
}
@ -755,27 +952,36 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
valueConsumer.consume( offset, x, y, domainValue, aggregateMapping );
return 1;
}
final int size = attributeMappings.size();
int span = 0;
if ( domainValue instanceof Object[] ) {
final Object[] values = (Object[]) domainValue;
assert values.length == attributeMappings.size();
for ( int i = 0; i < attributeMappings.size(); i++ ) {
assert values.length == size + ( isPolymorphic() ? 1 : 0 );
int i = 0;
for ( ; i < size; i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
final Object attributeValue = values[ i ];
span += attributeMapping.decompose( attributeValue, offset + span, x, y, valueConsumer, session );
}
if ( isPolymorphic() ) {
span += discriminatorMapping.decompose( values[i], offset + span, x, y, valueConsumer, session );
}
}
else {
for ( int i = 0; i < attributeMappings.size(); i++ ) {
final String compositeClassName = domainValue == null ? null : domainValue.getClass().getName();
for ( int i = 0; i < size; i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
if ( !(attributeMapping instanceof PluralAttributeMapping )) {
final Object attributeValue = domainValue == null
if ( !attributeMapping.isPluralAttributeMapping() ) {
final Object attributeValue = domainValue == null || !declaresAttribute( compositeClassName, attributeMapping )
? null
: attributeMapping.getPropertyAccess().getGetter().get( domainValue );
span += attributeMapping.decompose( attributeValue, offset + span, x, y, valueConsumer, session );
}
}
if ( isPolymorphic() ) {
final Object d = domainValue == null ? null : discriminatorMapping.getDiscriminatorValue( compositeClassName );
span += discriminatorMapping.decompose( d, offset + span, x, y, valueConsumer, session );
}
}
return span;
}

View File

@ -7,10 +7,10 @@
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@ -25,43 +25,53 @@ import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnRefere
/**
* @author Steve Ebersole
*/
public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminatorMapping {
public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminatorMapping
implements EmbeddableDiscriminatorMapping {
private final String name;
private final String tableExpression;
private final String columnName;
private final String columnFormula;
private final boolean isPhysical;
private final boolean isUpdateable;
private final String columnDefinition;
private final String customReadExpression;
private final Long length;
private final Integer precision;
private final Integer scale;
public ExplicitColumnDiscriminatorMappingImpl(
EntityMappingType entityDescriptor,
ManagedMappingType mappingType,
String name,
String tableExpression,
String columnExpression,
boolean isFormula,
boolean isPhysical,
boolean isUpdateable,
String columnDefinition,
String customReadExpression,
Long length,
Integer precision,
Integer scale,
DiscriminatorType<?> discriminatorType,
MappingModelCreationProcess creationProcess) {
DiscriminatorType<?> discriminatorType) {
//noinspection unchecked
super( entityDescriptor, (DiscriminatorType<Object>) discriminatorType, (BasicType<Object>) discriminatorType.getUnderlyingJdbcMapping() );
super( mappingType, (DiscriminatorType<Object>) discriminatorType, (BasicType<Object>) discriminatorType.getUnderlyingJdbcMapping() );
this.name = name;
this.tableExpression = tableExpression;
this.isPhysical = isPhysical;
this.columnDefinition = columnDefinition;
this.customReadExpression = customReadExpression;
this.length = length;
this.precision = precision;
this.scale = scale;
if ( isFormula ) {
columnName = null;
columnFormula = columnExpression;
this.isUpdateable = false;
}
else {
columnName = columnExpression;
columnFormula = null;
this.isUpdateable = isUpdateable;
}
}
@ -99,6 +109,11 @@ public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminato
return tableExpression;
}
@Override
public String getSelectableName() {
return name;
}
@Override
public String getSelectionExpression() {
return columnName == null ? columnFormula : columnName;
@ -106,7 +121,7 @@ public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminato
@Override
public String getCustomReadExpression() {
return null;
return customReadExpression;
}
@Override
@ -156,7 +171,7 @@ public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminato
@Override
public boolean isUpdateable() {
return false;
return isUpdateable;
}
@Override

View File

@ -33,7 +33,7 @@ public class IdClassRepresentationStrategy implements EmbeddableRepresentationSt
this.idClassType = idClassEmbeddable.getMappedJavaType();
this.instantiator = isRecord( idClassType.getJavaTypeClass() ) ?
new EmbeddableInstantiatorRecordStandard( idClassType.getJavaTypeClass() ) :
new EmbeddableInstantiatorPojoStandard( idClassType, () -> idClassEmbeddable );
new EmbeddableInstantiatorPojoStandard( idClassType.getJavaTypeClass(), () -> idClassEmbeddable );
}
@Override

View File

@ -11,6 +11,7 @@ import java.io.Serializable;
import org.hibernate.metamodel.model.domain.AbstractManagedType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.type.descriptor.java.JavaType;
@ -25,14 +26,21 @@ import jakarta.persistence.metamodel.SingularAttribute;
public class EmbeddableTypeImpl<J>
extends AbstractManagedType<J>
implements EmbeddableDomainType<J>, Serializable {
private final boolean isDynamic;
public EmbeddableTypeImpl(
JavaType<J> javaType,
boolean isDynamic,
JpaMetamodelImplementor domainMetamodel) {
super( javaType.getTypeName(), javaType, null, domainMetamodel );
this( javaType, null, isDynamic, domainMetamodel );
}
public EmbeddableTypeImpl(
JavaType<J> javaType,
ManagedDomainType<? super J> superType,
boolean isDynamic,
JpaMetamodelImplementor domainMetamodel) {
super( javaType.getTypeName(), javaType, superType, domainMetamodel );
this.isDynamic = isDynamic;
}

View File

@ -7,6 +7,8 @@
package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.EmbeddableDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
@ -35,6 +37,16 @@ public class EmbeddedSqmPathSource<J>
return (EmbeddableDomainType<J>) super.getSqmPathType();
}
@Override
public SqmPathSource<?> findSubPathSource(String name, JpaMetamodelImplementor metamodel) {
final PersistentAttribute<? super J, ?> attribute = getSqmPathType().findAttribute( name );
if ( attribute != null ) {
return (SqmPathSource<?>) attribute;
}
return (SqmPathSource<?>) getSqmPathType().findSubTypesAttribute( name );
}
@Override
public SqmPathSource<?> findSubPathSource(String name) {
return (SqmPathSource<?>) getSqmPathType().findAttribute( name );

View File

@ -21,6 +21,14 @@ public interface EmbeddableRepresentationStrategy extends ManagedTypeRepresentat
*/
EmbeddableInstantiator getInstantiator();
default EmbeddableInstantiator getInstantiatorForDiscriminator(Object discriminatorValue) {
return getInstantiator();
}
default EmbeddableInstantiator getInstantiatorForClass(String className) {
return getInstantiator();
}
/**
* The reflection optimizer to use for this embeddable.
*

View File

@ -5202,7 +5202,7 @@ public abstract class AbstractEntityPersister
);
}
discriminatorMapping = generateDiscriminatorMapping( bootEntityDescriptor, creationProcess );
discriminatorMapping = generateDiscriminatorMapping( bootEntityDescriptor );
softDeleteMapping = resolveSoftDeleteMapping( this, bootEntityDescriptor, getIdentifierTableName(), creationProcess );
if ( softDeleteMapping != null ) {
@ -5329,9 +5329,7 @@ public abstract class AbstractEntityPersister
return getDiscriminatorFormulaTemplate() == null;
}
protected EntityDiscriminatorMapping generateDiscriminatorMapping(
PersistentClass bootEntityDescriptor,
MappingModelCreationProcess modelCreationProcess) {
protected EntityDiscriminatorMapping generateDiscriminatorMapping(PersistentClass bootEntityDescriptor) {
if ( getDiscriminatorType() == null ) {
return null;
}
@ -5368,13 +5366,18 @@ public abstract class AbstractEntityPersister
}
return new ExplicitColumnDiscriminatorMappingImpl(
this,
discriminatorColumnExpression,
getTableName(),
discriminatorColumnExpression,
getDiscriminatorFormulaTemplate() != null,
isPhysicalDiscriminator(),
columnDefinition, length, precision, scale,
(DiscriminatorType<?>) getTypeDiscriminatorMetadata().getResolutionType(),
modelCreationProcess
false,
columnDefinition,
null,
length,
precision,
scale,
(DiscriminatorType<?>) getTypeDiscriminatorMetadata().getResolutionType()
);
}
}

View File

@ -10,6 +10,7 @@ import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.sql.InFragment;
import org.hibernate.type.BasicType;
@ -41,6 +42,16 @@ public class DiscriminatorHelper {
}
}
public static BasicType<?> getDiscriminatorType(Component component) {
Type discriminatorType = component.getDiscriminator().getType();
if ( discriminatorType instanceof BasicType ) {
return (BasicType<?>) discriminatorType;
}
else {
throw new MappingException( "Illegal discriminator type: " + discriminatorType.getName() );
}
}
static String getDiscriminatorSQLValue(PersistentClass persistentClass, Dialect dialect) {
if ( persistentClass.isDiscriminatorValueNull() ) {
return InFragment.NULL;

View File

@ -1205,9 +1205,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
}
@Override
protected EntityDiscriminatorMapping generateDiscriminatorMapping(
PersistentClass bootEntityDescriptor,
MappingModelCreationProcess modelCreationProcess) {
protected EntityDiscriminatorMapping generateDiscriminatorMapping(PersistentClass bootEntityDescriptor) {
final EntityMappingType superMappingType = getSuperMappingType();
if ( superMappingType != null ) {
return superMappingType.getDiscriminatorMapping();
@ -1218,7 +1216,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
// even though this is a JOINED hierarchy the user has defined an
// explicit discriminator column - so we can use the normal
// discriminator mapping
return super.generateDiscriminatorMapping( bootEntityDescriptor, modelCreationProcess );
return super.generateDiscriminatorMapping( bootEntityDescriptor );
}
else {
// otherwise, we need to use the case approach
@ -1229,8 +1227,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
notNullColumnNames,
discriminatorValues,
discriminatorAbstract,
resolveDiscriminatorType(),
modelCreationProcess
resolveDiscriminatorType()
);
}
}

View File

@ -438,10 +438,8 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
}
@Override
protected EntityDiscriminatorMapping generateDiscriminatorMapping(
PersistentClass bootEntityDescriptor,
MappingModelCreationProcess modelCreationProcess) {
return hasSubclasses() ? super.generateDiscriminatorMapping( bootEntityDescriptor, modelCreationProcess ) : null;
protected EntityDiscriminatorMapping generateDiscriminatorMapping(PersistentClass bootEntityDescriptor) {
return hasSubclasses() ? super.generateDiscriminatorMapping( bootEntityDescriptor ) : null;
}
@Override

View File

@ -362,12 +362,12 @@ public class DomainResultCreationStateImpl
}
@Override
public ImmutableFetchList visitNestedFetches(FetchParent fetchParent) {
public <R> R withNestedFetchParent(FetchParent fetchParent, Function<FetchParent, R> action) {
final FetchParent oldNestingFetchParent = this.nestingFetchParent;
this.nestingFetchParent = fetchParent;
final ImmutableFetchList fetches = visitFetches( fetchParent );
final R result = action.apply( fetchParent );
this.nestingFetchParent = oldNestingFetchParent;
return fetches;
return result;
}
@Override

View File

@ -28,7 +28,6 @@ import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.loader.MultipleBagFetchException;
@ -60,7 +59,6 @@ import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.Restrictable;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.SoftDeleteMapping;
@ -356,7 +354,6 @@ import org.hibernate.sql.ast.tree.predicate.LikePredicate;
import org.hibernate.sql.ast.tree.predicate.NegatedPredicate;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.predicate.PredicateCollector;
import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate;
import org.hibernate.sql.ast.tree.predicate.ThruthnessPredicate;
import org.hibernate.sql.ast.tree.select.QueryGroup;
@ -8350,13 +8347,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
@Override
public ImmutableFetchList visitNestedFetches(FetchParent fetchParent) {
public <R> R withNestedFetchParent(FetchParent fetchParent, Function<FetchParent, R> action) {
final SqlAstQueryPartProcessingStateImpl processingState = (SqlAstQueryPartProcessingStateImpl) getCurrentProcessingState();
final FetchParent nestingFetchParent = processingState.getNestingFetchParent();
processingState.setNestingFetchParent( fetchParent );
final ImmutableFetchList fetches = visitFetches( fetchParent );
final R result = action.apply( fetchParent );
processingState.setNestingFetchParent( nestingFetchParent );
return fetches;
return result;
}
@Override

View File

@ -6,9 +6,14 @@
*/
package org.hibernate.sql.results.graph;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.Incubating;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -18,7 +23,9 @@ import org.hibernate.spi.EntityIdentifierNavigablePath;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlAstQueryPartProcessingState;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
@ -117,6 +124,30 @@ public interface DomainResultCreationState {
}
}
default BasicFetch<?> visitEmbeddableDiscriminatorFetch(EmbeddableResultGraphNode fetchParent, boolean nested) {
final EmbeddableMappingType embeddableType = fetchParent.getReferencedMappingType();
final EmbeddableDiscriminatorMapping discriminatorMapping = embeddableType.getDiscriminatorMapping();
if ( discriminatorMapping != null ) {
final Function<FetchParent, BasicFetch<?>> fetchSupplier = fp -> discriminatorMapping.generateFetch(
fp,
fp.getNavigablePath().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
FetchTiming.IMMEDIATE,
true,
null,
this
);
if ( nested ) {
return withNestedFetchParent( fetchParent, fetchSupplier );
}
else {
return fetchSupplier.apply( fetchParent );
}
}
else {
return null;
}
}
/**
* Visit fetches for the given parent.
*
@ -150,7 +181,11 @@ public interface DomainResultCreationState {
*/
ImmutableFetchList visitFetches(FetchParent fetchParent);
ImmutableFetchList visitNestedFetches(FetchParent fetchParent);
default ImmutableFetchList visitNestedFetches(FetchParent fetchParent) {
return withNestedFetchParent( fetchParent, this::visitFetches );
}
<R> R withNestedFetchParent(FetchParent fetchParent, Function<FetchParent, R> action);
boolean isResolvingCircularFetch();

View File

@ -8,11 +8,14 @@ package org.hibernate.sql.results.graph;
import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.results.graph.collection.internal.AbstractImmediateCollectionInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -60,7 +63,7 @@ public interface FetchParentAccess extends Initializer {
if ( parentAccess == null
|| parentAccess.isEntityInitializer()
|| parentAccess.isCollectionInitializer()
|| parentAccess.isEmbeddableInitializer() && parentAccess.isResultInitializer() ) {
|| parentAccess.isEmbeddableInitializer() ) {
return parentAccess;
}
return parentAccess.getOwningParent();
@ -72,8 +75,10 @@ public interface FetchParentAccess extends Initializer {
ModelPart modelPart,
@Nullable FetchParentAccess parentAccess,
@Nullable FetchParentAccess owningParent) {
final EntityInitializer entityInitializer;
if ( owningParent == null || ( entityInitializer = owningParent.asEntityInitializer() ) == null ) {
final EntityInitializer entityInitializer = owningParent != null ?
owningParent.findFirstEntityInitializer() :
null;
if ( entityInitializer == null ) {
return null;
}
@ -103,14 +108,34 @@ public interface FetchParentAccess extends Initializer {
// skipping only depends on whether the collection key is resolvable or not
return collectionInitializer.resolveCollectionKey( rowProcessingState ) == null;
}
final EntityInitializer entityInitializer = owningParent.asEntityInitializer();
EntityInitializer entityInitializer = owningParent.asEntityInitializer();
if ( entityInitializer == null ) {
// We can never skip an initializer if it is part of an embeddable domain result,
// because that embeddable always has to be materialized with its full state
assert owningParent.isEmbeddableInitializer() && owningParent.isResultInitializer();
return false;
final EmbeddableInitializer embeddableInitializer = owningParent.asEmbeddableInitializer();
assert embeddableInitializer != null;
final EmbeddableMappingType descriptor = embeddableInitializer.getInitializedPart()
.getEmbeddableTypeDescriptor();
if ( descriptor.isPolymorphic() ) {
// The embeddable is polymorphic, check if the current subtype defines the initialized attribute
final AttributeMapping attribute = getInitializedPart().asAttributeMapping();
if ( attribute != null ) {
embeddableInitializer.resolveKey( rowProcessingState );
final String embeddableClassName = descriptor.getDiscriminatorMapping()
.resolveDiscriminatorValue( embeddableInitializer.getDiscriminatorValue() )
.getIndicatedEntityName();
if ( !descriptor.declaresAttribute( embeddableClassName, attribute ) ) {
return true;
}
}
}
if ( embeddableInitializer.isResultInitializer() ) {
// We can never skip an initializer if it is part of an embeddable domain result,
// because that embeddable always has to be materialized with its full state
return false;
}
else if ( ( entityInitializer = embeddableInitializer.findFirstEntityInitializer() ) == null ) {
return false;
}
}
// We must resolve the key of the parent in order to determine the concrete descriptor
entityInitializer.resolveKey( rowProcessingState );
final EntityPersister concreteDescriptor = entityInitializer.getConcreteDescriptor();

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.results.graph.embeddable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -26,6 +27,8 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.graph.collection.CollectionInitializer;
import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.internal.NullValueAssembler;
@ -51,16 +54,19 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
private final SessionFactoryImplementor sessionFactory;
protected final DomainResultAssembler<?>[] assemblers;
private final BasicResultAssembler<?> discriminatorAssembler;
// per-row state
private final Object[] rowState;
private State state = State.INITIAL;
protected Object compositeInstance;
private Object discriminatorValue;
private RowProcessingState wrappedProcessingState;
public AbstractEmbeddableInitializer(
EmbeddableResultGraphNode resultDescriptor,
FetchParentAccess parentAccess,
BasicFetch<?> discriminatorFetch,
AssemblerCreationState creationState) {
this.navigablePath = resultDescriptor.getNavigablePath();
this.embedded = resultDescriptor.getReferencedMappingContainer();
@ -77,6 +83,9 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
this.createEmptyCompositesEnabled = !isPartOfKey && embeddableTypeDescriptor.isCreateEmptyCompositesEnabled();
this.sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory();
this.assemblers = createAssemblers( resultDescriptor, creationState, embeddableTypeDescriptor );
discriminatorAssembler = discriminatorFetch != null ?
(BasicResultAssembler<?>) discriminatorFetch.createAssembler( parentAccess, creationState ) :
null;
}
protected DomainResultAssembler<?>[] createAssemblers(
@ -151,7 +160,16 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
@Override
public void resolveKey(RowProcessingState processingState) {
// nothing to do
// We need to possibly wrap the processing state if the embeddable is within an aggregate
if ( wrappedProcessingState == null ) {
wrappedProcessingState = wrapProcessingState( processingState );
}
if ( discriminatorAssembler != null ) {
final EmbeddableDiscriminatorMapping discriminatorMapping = embedded.getEmbeddableTypeDescriptor()
.getDiscriminatorMapping();
assert discriminatorMapping != null;
discriminatorValue = discriminatorAssembler.extractRawValue( wrappedProcessingState );
}
}
@Override
@ -191,10 +209,6 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
return;
}
// We need to possibly wrap the processing state if the embeddable is within an aggregate
if ( wrappedProcessingState == null ) {
wrappedProcessingState = wrapProcessingState( processingState );
}
extractRowState( wrappedProcessingState );
prepareCompositeInstance( wrappedProcessingState );
if ( state == State.NULL ) {
@ -275,6 +289,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
if ( compositeInstance == null ) {
compositeInstance = createCompositeInstance(
discriminatorValue,
navigablePath,
sessionFactory
);
@ -334,7 +349,10 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
}
}
private Object createCompositeInstance(NavigablePath navigablePath, SessionFactoryImplementor sessionFactory) {
private Object createCompositeInstance(
Object discriminatorValue,
NavigablePath navigablePath,
SessionFactoryImplementor sessionFactory) {
if ( state == State.NULL ) {
// todo (6.0) : should we initialize the composite instance if it has a parent attribute?
// if ( !createEmptyCompositesEnabled && embedded.getParentInjectionAttributePropertyAccess() == null ) {
@ -345,7 +363,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
final Object instance = embedded.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
.getInstantiator()
.getInstantiatorForDiscriminator( discriminatorValue )
.instantiate( this, sessionFactory );
state = State.EXTRACTED;
@ -369,6 +387,11 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
return fetchParentAccess.getInitializedInstance();
}
@Override
public Object getDiscriminatorValue() {
return discriminatorValue;
}
private void handleParentInjection() {
final PropertyAccess parentInjectionAccess = embedded.getParentInjectionAttributePropertyAccess();
if ( parentInjectionAccess == null ) {
@ -467,6 +490,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
@Override
public void finishUpRow(RowProcessingState rowProcessingState) {
compositeInstance = null;
discriminatorValue = null;
state = State.INITIAL;
wrappedProcessingState = null;

View File

@ -50,4 +50,8 @@ public interface EmbeddableInitializer extends FetchParentAccess {
}
void resolveState(RowProcessingState rowProcessingState);
default Object getDiscriminatorValue() {
return null;
}
}

View File

@ -29,6 +29,7 @@ import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.InitializerProducer;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
@ -49,6 +50,7 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
private final boolean hasTableGroup;
private final SqlSelection aggregateSelection;
private final EmbeddableMappingType fetchContainer;
private final BasicFetch<?> discriminatorFetch;
public AggregateEmbeddableFetchImpl(
NavigablePath navigablePath,
@ -99,6 +101,7 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
fetchParent,
typeConfiguration
);
this.discriminatorFetch = creationState.visitEmbeddableDiscriminatorFetch( this, true );
resetFetches( creationState.visitNestedFetches( this ) );
}
@ -173,6 +176,7 @@ public class AggregateEmbeddableFetchImpl extends AbstractFetchParent
return new AggregateEmbeddableFetchInitializer(
parentAccess,
this,
discriminatorFetch,
creationState,
aggregateSelection
);

View File

@ -11,6 +11,7 @@ import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -28,8 +29,10 @@ public class AggregateEmbeddableFetchInitializer extends AbstractEmbeddableIniti
public AggregateEmbeddableFetchInitializer(
FetchParentAccess fetchParentAccess,
EmbeddableResultGraphNode resultDescriptor,
AssemblerCreationState creationState, SqlSelection structSelection) {
super( resultDescriptor, fetchParentAccess, creationState );
BasicFetch<?> discriminatorFetch,
AssemblerCreationState creationState,
SqlSelection structSelection) {
super( resultDescriptor, fetchParentAccess, discriminatorFetch, creationState );
this.aggregateValuesArrayPositions = AggregateEmbeddableInitializer.determineAggregateValuesArrayPositions(
fetchParentAccess,
structSelection

View File

@ -30,6 +30,7 @@ import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.InitializerProducer;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResult;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
@ -51,6 +52,7 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
private final boolean containsAnyNonScalars;
private final SqlSelection aggregateSelection;
private final EmbeddableMappingType fetchContainer;
private final BasicFetch<?> discriminatorFetch;
public AggregateEmbeddableResultImpl(
NavigablePath navigablePath,
@ -104,6 +106,7 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
null,
typeConfiguration
);
this.discriminatorFetch = creationState.visitEmbeddableDiscriminatorFetch( this, true );
resetFetches( creationState.visitNestedFetches( this ) );
this.containsAnyNonScalars = determineIfContainedAnyScalars( getFetches() );
}
@ -169,6 +172,7 @@ public class AggregateEmbeddableResultImpl<T> extends AbstractFetchParent implem
return new AggregateEmbeddableResultInitializer(
parentAccess,
this,
discriminatorFetch,
creationState,
aggregateSelection
);

View File

@ -11,6 +11,7 @@ import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -28,8 +29,10 @@ public class AggregateEmbeddableResultInitializer extends AbstractEmbeddableInit
public AggregateEmbeddableResultInitializer(
FetchParentAccess fetchParentAccess,
EmbeddableResultGraphNode resultDescriptor,
AssemblerCreationState creationState, SqlSelection structSelection) {
super( resultDescriptor, fetchParentAccess, creationState );
BasicFetch<?> discriminatorFetch,
AssemblerCreationState creationState,
SqlSelection structSelection) {
super( resultDescriptor, fetchParentAccess, discriminatorFetch, creationState );
this.aggregateValuesArrayPositions = AggregateEmbeddableInitializer.determineAggregateValuesArrayPositions(
fetchParentAccess,
structSelection

View File

@ -140,6 +140,6 @@ public class EmbeddableExpressionResultImpl<T> extends AbstractFetchParent imple
@Override
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
return new EmbeddableResultInitializer( this, parentAccess, creationState );
return new EmbeddableResultInitializer( this, parentAccess, null, creationState );
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.engine.FetchTiming;
import org.hibernate.graph.spi.GraphImplementor;
import org.hibernate.metamodel.mapping.DiscriminatorMapping;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
@ -26,6 +27,7 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.InitializerProducer;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable;
@ -41,6 +43,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent
private final TableGroup tableGroup;
private final boolean hasTableGroup;
private final EmbeddableMappingType fetchContainer;
private final BasicFetch<?> discriminatorFetch;
public EmbeddableFetchImpl(
NavigablePath navigablePath,
@ -77,6 +80,8 @@ public class EmbeddableFetchImpl extends AbstractFetchParent
}
);
this.discriminatorFetch = creationState.visitEmbeddableDiscriminatorFetch( this, false );
afterInitialize( this, creationState );
}
@ -90,6 +95,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent
fetchTiming = original.fetchTiming;
tableGroup = original.tableGroup;
hasTableGroup = original.hasTableGroup;
discriminatorFetch = original.discriminatorFetch;
}
@Override
@ -160,7 +166,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent
@Override
public EmbeddableInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
return new EmbeddableFetchInitializer( parentAccess, this, creationState );
return new EmbeddableFetchInitializer( parentAccess, this, discriminatorFetch, creationState );
}
@Override

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
@ -19,8 +20,9 @@ public class EmbeddableFetchInitializer
public EmbeddableFetchInitializer(
FetchParentAccess fetchParentAccess,
EmbeddableResultGraphNode resultDescriptor,
BasicFetch<?> discriminatorFetch,
AssemblerCreationState creationState) {
super( resultDescriptor, fetchParentAccess, creationState );
super( resultDescriptor, fetchParentAccess, discriminatorFetch, creationState );
}
@Override

View File

@ -116,7 +116,7 @@ public class EmbeddableForeignKeyResultImpl<T>
public EmbeddableInitializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
return getReferencedModePart() instanceof NonAggregatedIdentifierMapping
? new NonAggregatedIdentifierMappingResultInitializer( this, null, creationState )
: new EmbeddableResultInitializer( this, null, creationState );
: new EmbeddableResultInitializer( this, null, null, creationState );
}
@Override

View File

@ -23,6 +23,7 @@ import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Initializer;
import org.hibernate.sql.results.graph.InitializerProducer;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResult;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
@ -38,6 +39,7 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
private final String resultVariable;
private final boolean containsAnyNonScalars;
private final EmbeddableMappingType fetchContainer;
private final BasicFetch<?> discriminatorFetch;
public EmbeddableResultImpl(
NavigablePath navigablePath,
@ -75,6 +77,8 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
}
);
this.discriminatorFetch = creationState.visitEmbeddableDiscriminatorFetch( this, false );
afterInitialize( this, creationState );
// after-after-initialize :D
@ -139,6 +143,6 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
@Override
public Initializer createInitializer(FetchParentAccess parentAccess, AssemblerCreationState creationState) {
return new EmbeddableResultInitializer( this, parentAccess, creationState );
return new EmbeddableResultInitializer( this, parentAccess, discriminatorFetch, creationState );
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.embeddable.internal;
import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer;
import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode;
@ -18,8 +19,9 @@ public class EmbeddableResultInitializer extends AbstractEmbeddableInitializer {
public EmbeddableResultInitializer(
EmbeddableResultGraphNode resultDescriptor,
FetchParentAccess parentAccess,
BasicFetch<?> discriminatorFetch,
AssemblerCreationState creationState) {
super( resultDescriptor, parentAccess, creationState );
super( resultDescriptor, parentAccess, discriminatorFetch, creationState );
}
@Override

View File

@ -22,6 +22,7 @@ import org.hibernate.Remove;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadeStyles;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -30,11 +31,14 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.EmbeddableDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.type.descriptor.ValueExtractor;
@ -42,6 +46,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.CompositeTypeImplementor;
import static org.hibernate.internal.util.ReflectHelper.isRecord;
import static org.hibernate.metamodel.mapping.EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME;
/**
* Handles {@linkplain jakarta.persistence.Embedded embedded} mappings.
@ -59,6 +64,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
protected final int propertySpan;
private final CascadeStyle[] cascade;
private final FetchMode[] joinedFetch;
private final int discriminatorColumnSpan;
private final boolean isAggregate;
private final boolean isKey;
@ -85,11 +91,12 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
this.isKey = component.isKey();
this.propertySpan = component.getPropertySpan();
this.originalPropertyOrder = originalPropertyOrder;
this.propertyNames = new String[propertySpan];
this.propertyTypes = new Type[propertySpan];
this.propertyNullability = new boolean[propertySpan];
this.cascade = new CascadeStyle[propertySpan];
this.joinedFetch = new FetchMode[propertySpan];
final Value discriminator = component.getDiscriminator();
this.propertyNames = new String[propertySpan + ( component.isPolymorphic() ? 1 : 0 )];
this.propertyTypes = new Type[propertySpan + ( component.isPolymorphic() ? 1 : 0 )];
this.propertyNullability = new boolean[propertySpan + ( component.isPolymorphic() ? 1 : 0 )];
this.cascade = new CascadeStyle[propertySpan + ( component.isPolymorphic() ? 1 : 0 )];
this.joinedFetch = new FetchMode[propertySpan + ( component.isPolymorphic() ? 1 : 0 )];
int i = 0;
for ( Property property : component.getProperties() ) {
@ -106,6 +113,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
}
i++;
}
if ( discriminator != null ) {
this.discriminatorColumnSpan = discriminator.getColumnSpan();
this.propertyNames[i] = DISCRIMINATOR_ROLE_NAME;
this.propertyTypes[i] = discriminator.getType();
this.propertyNullability[i] = false;
this.cascade[i] = CascadeStyles.NONE;
this.joinedFetch[i] = FetchMode.SELECT;
}
else {
this.discriminatorColumnSpan = 0;
}
}
private boolean isAggregate() {
@ -122,6 +140,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
for ( int i = 0; i < propertySpan; i++ ) {
span += propertyTypes[i].getColumnSpan( mapping );
}
span += discriminatorColumnSpan;
return span;
}
@ -480,7 +499,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
values[i] = propertyTypes[i].deepCopy( values[i], factory );
}
final Object result = instantiator().instantiate( () -> values, factory );
final Object result = instantiator( component ).instantiate( () -> values, factory );
//not absolutely necessary, but helps for some
//equals()/hashCode() implementations
@ -516,7 +535,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
);
if ( target == null || !isMutable() ) {
return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() );
return instantiator( original ).instantiate( () -> replacedValues, session.getSessionFactory() );
}
else {
setPropertyValues( target, replacedValues );
@ -549,7 +568,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
);
if ( target == null || !isMutable() ) {
return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() );
return instantiator( original ).instantiate( () -> replacedValues, session.getSessionFactory() );
}
else {
setPropertyValues( target, replacedValues );
@ -576,9 +595,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return null;
}
else {
final Object[] values = getPropertyValues( value );
for ( int i = 0; i < propertyTypes.length; i++ ) {
values[i] = propertyTypes[i].disassemble( values[i], session, owner );
final boolean polymorphic = embeddableTypeDescriptor().isPolymorphic();
final Object[] values = new Object[propertySpan + (polymorphic ? 1 : 0)];
final Object[] propertyValues = getPropertyValues( value );
int i = 0;
for ( ; i < propertySpan; i++ ) {
values[i] = propertyTypes[i].disassemble( propertyValues[i], session, owner );
}
if ( polymorphic ) {
final EmbeddableDiscriminatorMapping discriminatorMapping = embeddableTypeDescriptor().getDiscriminatorMapping();
final Object discriminatorValue = discriminatorMapping.getDiscriminatorValue( value.getClass().getName() );
values[i] = discriminatorMapping.disassemble( discriminatorValue, session );
}
return values;
}
@ -591,9 +618,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return null;
}
else {
final Object[] values = getPropertyValues( value );
for ( int i = 0; i < propertyTypes.length; i++ ) {
values[i] = propertyTypes[i].disassemble( values[i], sessionFactory );
final boolean polymorphic = embeddableTypeDescriptor().isPolymorphic();
final Object[] values = new Object[propertySpan + (polymorphic ? 1 : 0)];
final Object[] propertyValues = getPropertyValues( value );
int i = 0;
for ( ; i < propertyTypes.length; i++ ) {
values[i] = propertyTypes[i].disassemble( propertyValues[i], sessionFactory );
}
if ( polymorphic ) {
final EmbeddableDiscriminatorMapping discriminatorMapping = embeddableTypeDescriptor().getDiscriminatorMapping();
final Object discriminatorValue = discriminatorMapping.getDiscriminatorValue( value.getClass().getName() );
values[i] = discriminatorMapping.disassemble( discriminatorValue, null );
}
return values;
}
@ -602,18 +637,23 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
@Override
public Object assemble(Serializable object, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
if ( object == null ) {
return null;
}
else {
final Object[] values = (Object[]) object;
final Object[] assembled = new Object[values.length];
for ( int i = 0; i < propertyTypes.length; i++ ) {
final boolean polymorphic = embeddableTypeDescriptor().isPolymorphic();
final Object[] assembled = new Object[values.length - ( polymorphic ? 1 : 0 )];
int i = 0;
for ( ; i < assembled.length; i++ ) {
assembled[i] = propertyTypes[i].assemble( (Serializable) values[i], session, owner );
}
final Object instance = instantiator().instantiate( () -> assembled, session.getFactory() );
final EmbeddableRepresentationStrategy representationStrategy = embeddableTypeDescriptor().getRepresentationStrategy();
final EmbeddableInstantiator instantiator = polymorphic ?
representationStrategy.getInstantiatorForDiscriminator( values[i] ) :
representationStrategy.getInstantiator();
final Object instance = instantiator.instantiate( () -> assembled, session.getFactory() );
final PropertyAccess parentInjectionAccess = mappingModelPart.getParentInjectionAttributePropertyAccess();
if ( parentInjectionAccess != null ) {
@ -708,7 +748,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
values = (Object[]) jdbcValueExtractor().extract( statement, startIndex, session );
}
else {
values = new Object[propertySpan];
final boolean polymorphic = embeddableTypeDescriptor().isPolymorphic();
values = new Object[propertySpan + ( polymorphic ? 1 : 0 )];
int currentIndex = startIndex;
boolean notNull = false;
for ( int i = 0; i < propertySpan; i++ ) {
@ -729,6 +770,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
currentIndex += propertyType.getColumnSpan( session.getFactory() );
}
if ( polymorphic ) {
values[currentIndex] = embeddableTypeDescriptor().getDiscriminatorMapping()
.getJdbcMapping()
.getJdbcValueExtractor()
.extract(
statement,
currentIndex,
session
);
}
if ( !notNull ) {
values = null;
}
@ -745,7 +797,16 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
}
private Object resolve(Object[] value, SharedSessionContractImplementor session) throws HibernateException {
return instantiator().instantiate( () -> value, session.getFactory() );
final EmbeddableRepresentationStrategy representationStrategy = embeddableTypeDescriptor().getRepresentationStrategy();
final EmbeddableInstantiator instantiator;
if ( embeddableTypeDescriptor().isPolymorphic() ) {
// the discriminator here is the composite class name because it gets converted to the domain type when extracted
instantiator = representationStrategy.getInstantiatorForClass( (String) value[value.length - 1] );
}
else {
instantiator = representationStrategy.getInstantiator();
}
return instantiator.instantiate( () -> value, session.getFactory() );
}
private EmbeddableMappingType embeddableTypeDescriptor() {
@ -756,8 +817,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return embeddableTypeDescriptor().getAggregateMapping().getJdbcMapping().getJdbcValueExtractor();
}
protected final EmbeddableInstantiator instantiator() {
return embeddableTypeDescriptor().getRepresentationStrategy().getInstantiator();
protected final EmbeddableInstantiator instantiator(Object compositeInstance) {
final EmbeddableRepresentationStrategy representationStrategy = embeddableTypeDescriptor().getRepresentationStrategy();
if ( embeddableTypeDescriptor().isPolymorphic() ) {
final String compositeClassName = compositeInstance != null ?
compositeInstance.getClass().getName() :
componentClass.getName();
return representationStrategy.getInstantiatorForClass( compositeClassName );
}
else {
return representationStrategy.getInstantiator();
}
}
@Override
@ -801,7 +871,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
public Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session)
throws HibernateException {
if ( !isMutable() ) {
return instantiator().instantiate( () -> values, session.getSessionFactory() );
return instantiator( component ).instantiate( () -> values, session.getSessionFactory() );
}
return CompositeTypeImplementor.super.replacePropertyValues( component, values, session );
}

View File

@ -91,6 +91,6 @@ public class UserComponentType<T> extends ComponentType {
@Override
public Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session)
throws HibernateException {
return instantiator().instantiate( () -> values, session.getSessionFactory() );
return instantiator( component ).instantiate( () -> values, session.getSessionFactory() );
}
}

View File

@ -15,6 +15,7 @@ import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.dialect.StructAttributeValues;
import org.hibernate.dialect.StructHelper;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -34,6 +35,8 @@ import org.hibernate.type.internal.BasicTypeImpl;
import org.hibernate.type.internal.ParameterizedTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.dialect.StructHelper.instantiate;
/**
* Descriptor for {@link Types#ARRAY ARRAY} handling.
*
@ -151,18 +154,16 @@ public class ArrayJdbcType implements JdbcType {
if ( array != null && getElementJdbcType() instanceof AggregateJdbcType ) {
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) getElementJdbcType();
final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
final EmbeddableInstantiator instantiator = embeddableMappingType.getRepresentationStrategy()
.getInstantiator();
final Object rawArray = array.getArray();
final Object[] domainObjects = new Object[Array.getLength( rawArray )];
for ( int i = 0; i < domainObjects.length; i++ ) {
final Object[] aggregateRawValues = aggregateJdbcType.extractJdbcValues( Array.get( rawArray, i ), options );
final Object[] attributeValues = StructHelper.getAttributeValues(
final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
embeddableMappingType,
aggregateRawValues,
options
);
domainObjects[i] = instantiator.instantiate( () -> attributeValues, options.getSessionFactory() );
domainObjects[i] = instantiate( embeddableMappingType, attributeValues, options.getSessionFactory() );
}
return extractor.getJavaType().wrap( domainObjects, options );
}

View File

@ -0,0 +1,161 @@
/*
* 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.inheritance.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.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
BasicEmbeddableInheritanceTest.TestEntity.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@SessionFactory
public class BasicEmbeddableInheritanceTest {
@Test
public void testFind(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 1L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 1 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ParentEmbeddable.class );
} );
}
@Test
public void testQueryEntity(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.createQuery(
"from TestEntity where id = 2",
TestEntity.class
).getSingleResult();
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result.getEmbeddable() ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@Test
public void testQueryEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select embeddable from TestEntity where id = 4",
ParentEmbeddable.class
).getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "embeddable_4" );
assertThat( result ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result ).getChildOneProp() ).isEqualTo( 4 );
assertThat( ( (SubChildOneEmbeddable) result ).getSubChildOneProp() ).isEqualTo( 4.0 );
} );
}
@Test
public void testQueryJoinedEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select e from TestEntity t join t.embeddable e where t.id = 2",
ParentEmbeddable.class
).getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@Test
public void testUpdate(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_5" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 5 );
// update values
result.getEmbeddable().setParentProp( "embeddable_5_new" );
( (ChildOneEmbeddable) result.getEmbeddable() ).setChildOneProp( 55 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_5_new" );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 55 );
result.setEmbeddable( new SubChildOneEmbeddable( "embeddable_6", 6, 6.0 ) );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_6" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 6 );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getSubChildOneProp() ).isEqualTo( 6.0 );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new TestEntity( 1L, new ChildOneEmbeddable( "embeddable_1", 1 ) ) );
session.persist( new TestEntity( 2L, new ChildTwoEmbeddable( "embeddable_2", 2L ) ) );
session.persist( new TestEntity( 3L, new ParentEmbeddable( "embeddable_3" ) ) );
session.persist( new TestEntity( 4L, new SubChildOneEmbeddable( "embeddable_4", 4, 4.0 ) ) );
session.persist( new TestEntity( 5L, new ChildOneEmbeddable( "embeddable_5", 5 ) ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
//tag::embeddable-inheritance-entity-example[]
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@Embedded
private ParentEmbeddable embeddable;
// ...
//end::embeddable-inheritance-entity-example[]
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
//tag::embeddable-inheritance-entity-example[]
}
//end::embeddable-inheritance-entity-example[]
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.inheritance.embeddable;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;
/**
* @author Marco Belladelli
*/
//tag::embeddable-inheritance-child-one-example[]
@Embeddable
@DiscriminatorValue( "child_one" )
class ChildOneEmbeddable extends ParentEmbeddable {
private Integer childOneProp;
// ...
//end::embeddable-inheritance-child-one-example[]
public ChildOneEmbeddable() {
}
public ChildOneEmbeddable(String parentProp, Integer childOneProp) {
super( parentProp );
this.childOneProp = childOneProp;
}
public Integer getChildOneProp() {
return childOneProp;
}
public void setChildOneProp(Integer childOneProp) {
this.childOneProp = childOneProp;
}
//tag::embeddable-inheritance-child-one-example[]
}
//end::embeddable-inheritance-child-one-example[]

View File

@ -0,0 +1,33 @@
/*
* 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.inheritance.embeddable;
import jakarta.persistence.Embeddable;
/**
* @author Marco Belladelli
*/
@Embeddable
class ChildTwoEmbeddable extends ParentEmbeddable {
private Long childTwoProp;
public ChildTwoEmbeddable() {
}
public ChildTwoEmbeddable(String parentProp, Long childTwoProp) {
super( parentProp );
this.childTwoProp = childTwoProp;
}
public Long getChildTwoProp() {
return childTwoProp;
}
public void setChildTwoProp(Long childTwoProp) {
this.childTwoProp = childTwoProp;
}
}

View File

@ -0,0 +1,150 @@
/*
* 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.inheritance.embeddable;
import java.util.ArrayList;
import java.util.List;
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.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
ElementCollectionEmbeddableInheritanceTest.TestEntity.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@SessionFactory
public class ElementCollectionEmbeddableInheritanceTest {
@Test
public void testFind(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 1L );
assertThat( result.getEmbeddables() ).hasSize( 2 );
result.getEmbeddables().forEach( embeddable -> {
if ( embeddable instanceof ChildOneEmbeddable ) {
assertThat( embeddable.getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( embeddable ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) embeddable ).getChildOneProp() ).isEqualTo( 1 );
}
else {
assertThat( embeddable.getParentProp() ).isEqualTo( "embeddable_3" );
assertThat( embeddable ).isExactlyInstanceOf( ParentEmbeddable.class );
}
} );
} );
}
@Test
public void testQueryEntity(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.createQuery(
"from TestEntity where id = 2",
TestEntity.class
).getSingleResult();
assertThat( result.getEmbeddables() ).hasSize( 1 );
final ParentEmbeddable embeddable = result.getEmbeddables().get( 0 );
assertThat( embeddable.getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( embeddable ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) embeddable ).getChildOneProp() ).isEqualTo( 2 );
assertThat( ( (SubChildOneEmbeddable) embeddable ).getSubChildOneProp() ).isEqualTo( 2.0 );
} );
}
@Test
public void testUpdate(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddables() ).hasSize( 2 );
result.getEmbeddables().forEach( embeddable -> {
if ( embeddable instanceof ChildTwoEmbeddable ) {
assertThat( embeddable ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) embeddable ).getChildTwoProp() ).isEqualTo( 4L );
}
else {
assertThat( embeddable ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) embeddable ).getChildOneProp() ).isEqualTo( 5 );
// update values
embeddable.setParentProp( "embeddable_5_new" );
( (ChildOneEmbeddable) embeddable ).setChildOneProp( 55 );
}
} );
result.getEmbeddables().add( new SubChildOneEmbeddable( "embeddable_6", 6, 6.0 ) );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
result.getEmbeddables().forEach( embeddable -> {
if ( embeddable instanceof SubChildOneEmbeddable ) {
assertThat( embeddable ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( embeddable.getParentProp() ).isEqualTo( "embeddable_6" );
assertThat( ( (SubChildOneEmbeddable) embeddable ).getSubChildOneProp() ).isEqualTo( 6.0 );
}
else if ( embeddable instanceof ChildOneEmbeddable ) {
assertThat( embeddable ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( embeddable.getParentProp() ).isEqualTo( "embeddable_5_new" );
assertThat( ( (ChildOneEmbeddable) embeddable ).getChildOneProp() ).isEqualTo( 55 );
}
} );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity testEntity1 = new TestEntity( 1L );
testEntity1.getEmbeddables().add( new ChildOneEmbeddable( "embeddable_1", 1 ) );
testEntity1.getEmbeddables().add( new ParentEmbeddable( "embeddable_3" ) );
session.persist( testEntity1 );
final TestEntity testEntity2 = new TestEntity( 2L );
testEntity2.getEmbeddables().add( new SubChildOneEmbeddable( "embeddable_2", 2, 2.0 ) );
session.persist( testEntity2 );
final TestEntity testEntity3 = new TestEntity( 3L );
testEntity3.getEmbeddables().add( new ChildTwoEmbeddable( "embeddable_4", 4L ) );
testEntity3.getEmbeddables().add( new ChildOneEmbeddable( "embeddable_5", 5 ) );
session.persist( testEntity3 );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@ElementCollection
private List<ParentEmbeddable> embeddables = new ArrayList<>();
public TestEntity() {
}
public TestEntity(Long id) {
this.id = id;
}
public List<ParentEmbeddable> getEmbeddables() {
return embeddables;
}
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.inheritance.embeddable;
import org.hibernate.cache.spi.CacheImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Cacheable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
EmbeddableInheritance2LCTest.TestEntity.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@ServiceRegistry( settings = @Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" ) )
@SessionFactory( generateStatistics = true )
public class EmbeddableInheritance2LCTest {
@Test
public void testFind(SessionFactoryScope scope) {
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
statistics.clear();
final CacheImplementor cache = scope.getSessionFactory().getCache();
cache.evictEntityData();
scope.inTransaction( session -> {
// load the entity in cache
session.find( TestEntity.class, 1L );
assertThat( statistics.getSecondLevelCachePutCount() ).isEqualTo( 1 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 1L );
assertThat( statistics.getSecondLevelCacheHitCount() ).isEqualTo( 1 );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 1 );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getSubChildOneProp() ).isEqualTo( 1.0 );
} );
}
@Test
public void testQuery(SessionFactoryScope scope) {
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
statistics.clear();
final CacheImplementor cache = scope.getSessionFactory().getCache();
cache.evictEntityData();
scope.inTransaction( session -> {
session.createQuery( "from TestEntity where id = 2", TestEntity.class ).getSingleResult();
assertThat( statistics.getSecondLevelCachePutCount() ).isEqualTo( 1 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 2L );
assertThat( statistics.getSecondLevelCacheHitCount() ).isEqualTo( 1 );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result.getEmbeddable() ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new TestEntity( 1L, new SubChildOneEmbeddable( "embeddable_1", 1, 1.0 ) ) );
session.persist( new TestEntity( 2L, new ChildTwoEmbeddable( "embeddable_2", 2L ) ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
@Entity( name = "TestEntity" )
@Cacheable
static class TestEntity {
@Id
private Long id;
@Embedded
private ParentEmbeddable embeddable;
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
}
}

View File

@ -0,0 +1,338 @@
/*
* 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.inheritance.embeddable;
import java.util.ArrayList;
import java.util.List;
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.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
ParentEmbeddable.class,
EmbeddableInheritanceAssciationsTest.TestEntity.class,
EmbeddableInheritanceAssciationsTest.AssociatedEntity.class,
EmbeddableInheritanceAssciationsTest.AssociationChildOne.class,
EmbeddableInheritanceAssciationsTest.AssociationSubChildOne.class,
EmbeddableInheritanceAssciationsTest.AssociationChildTwo.class,
EmbeddableInheritanceAssciationsTest.AssociationChildThree.class,
} )
@SessionFactory
public class EmbeddableInheritanceAssciationsTest {
@Test
public void testManyToOne(SessionFactoryScope scope) {
scope.inTransaction( session -> {
// insert
final AssociatedEntity associated = session.find( AssociatedEntity.class, 1L );
session.persist( new TestEntity( 1L, new AssociationChildOne( "embeddable_1", associated ) ) );
} );
scope.inTransaction( session -> {
// queries
final AssociatedEntity result = session.createQuery(
"select embeddable.manyToOne from TestEntity where id = 1",
AssociatedEntity.class
).getSingleResult();
assertThat( result.getId() ).isEqualTo( 1L );
assertThat( result.getName() ).isEqualTo( "associated_1" );
} );
scope.inTransaction( session -> {
// find
final TestEntity result = session.find( TestEntity.class, 1L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( AssociationChildOne.class );
final AssociationChildOne embeddable = (AssociationChildOne) result.getEmbeddable();
assertThat( embeddable.getManyToOne().getId() ).isEqualTo( 1L );
assertThat( embeddable.getManyToOne().getName() ).isEqualTo( "associated_1" );
// update
final AssociatedEntity newAssociated = new AssociatedEntity( 11L, "associated_1_new" );
session.persist( newAssociated );
embeddable.setManyToOne( newAssociated );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 1L );
final AssociationChildOne embeddable = (AssociationChildOne) result.getEmbeddable();
assertThat( embeddable.getManyToOne().getId() ).isEqualTo( 11L );
assertThat( embeddable.getManyToOne().getName() ).isEqualTo( "associated_1_new" );
} );
}
@Test
public void testManyToOneSubtype(SessionFactoryScope scope) {
scope.inTransaction( session -> {
// insert
final AssociatedEntity associated = session.find( AssociatedEntity.class, 2L );
session.persist( new TestEntity( 2L, new AssociationSubChildOne( "embeddable_2", associated ) ) );
} );
scope.inTransaction( session -> {
// find
final TestEntity result = session.find( TestEntity.class, 2L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( AssociationSubChildOne.class );
final AssociationSubChildOne embeddable = (AssociationSubChildOne) result.getEmbeddable();
assertThat( embeddable.getManyToOne().getId() ).isEqualTo( 2L );
assertThat( embeddable.getManyToOne().getName() ).isEqualTo( "associated_2" );
} );
}
@Test
public void testManyToMany(SessionFactoryScope scope) {
scope.inTransaction( session -> {
// insert
final AssociationChildTwo associationChildTwo = new AssociationChildTwo( "embeddable_3" );
associationChildTwo.getManyToMany().add( session.find( AssociatedEntity.class, 3L ) );
associationChildTwo.getManyToMany().add( session.find( AssociatedEntity.class, 4L ) );
session.persist( new TestEntity( 3L, associationChildTwo ) );
} );
scope.inTransaction( session -> {
// queries
final List<AssociatedEntity> resultList = session.createQuery(
"select embeddable.manyToMany from TestEntity",
AssociatedEntity.class
).getResultList();
final Integer size = session.createQuery(
"select size(embeddable.manyToMany) from TestEntity where id = 3",
Integer.class
).getSingleResult();
assertThat( resultList ).hasSize( 2 ).hasSize( size )
.extracting( AssociatedEntity::getName )
.containsOnly( "associated_3", "associated_4" );
} );
scope.inTransaction( session -> {
// find
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( AssociationChildTwo.class );
final AssociationChildTwo embeddable = (AssociationChildTwo) result.getEmbeddable();
assertThat( embeddable.getManyToMany() ).hasSize( 2 )
.extracting( AssociatedEntity::getName )
.containsOnly( "associated_3", "associated_4" );
// update
embeddable.getManyToMany().remove( 1 );
final AssociatedEntity newAssociated = new AssociatedEntity( 44L, "associated_4_new" );
session.persist( newAssociated );
embeddable.getManyToMany().add( newAssociated );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
final AssociationChildTwo embeddable = (AssociationChildTwo) result.getEmbeddable();
assertThat( embeddable.getManyToMany() ).hasSize( 2 )
.extracting( AssociatedEntity::getName )
.containsOnly( "associated_3", "associated_4_new" );
} );
}
@Test
public void testOneToMany(SessionFactoryScope scope) {
scope.inTransaction( session -> {
// insert
final AssociationChildThree associationChildTwo = new AssociationChildThree( "embeddable_3" );
associationChildTwo.getOneToMany().add( session.find( AssociatedEntity.class, 5L ) );
associationChildTwo.getOneToMany().add( session.find( AssociatedEntity.class, 6L ) );
session.persist( new TestEntity( 4L, associationChildTwo ) );
} );
scope.inTransaction( session -> {
// queries
final List<AssociatedEntity> resultList = session.createQuery(
"select embeddable.oneToMany from TestEntity",
AssociatedEntity.class
).getResultList();
final Integer size = session.createQuery(
"select size(embeddable.oneToMany) from TestEntity where id = 4",
Integer.class
).getSingleResult();
assertThat( resultList ).hasSize( 2 ).hasSize( size )
.extracting( AssociatedEntity::getName )
.containsOnly( "associated_5", "associated_6" );
} );
scope.inTransaction( session -> {
// find
final TestEntity result = session.find( TestEntity.class, 4L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( AssociationChildThree.class );
final AssociationChildThree embeddable = (AssociationChildThree) result.getEmbeddable();
assertThat( embeddable.getOneToMany() ).hasSize( 2 )
.extracting( AssociatedEntity::getName )
.containsOnly( "associated_5", "associated_6" );
// update
embeddable.getOneToMany().remove( 0 );
final AssociatedEntity newAssociated = new AssociatedEntity( 55L, "associated_5_new" );
session.persist( newAssociated );
embeddable.getOneToMany().add( 0, newAssociated );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 4L );
final AssociationChildThree embeddable = (AssociationChildThree) result.getEmbeddable();
assertThat( embeddable.getOneToMany() ).hasSize( 2 )
.extracting( AssociatedEntity::getName )
.containsOnly( "associated_5_new", "associated_6" );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new AssociatedEntity( 1L, "associated_1" ) );
session.persist( new AssociatedEntity( 2L, "associated_2" ) );
session.persist( new AssociatedEntity( 3L, "associated_3" ) );
session.persist( new AssociatedEntity( 4L, "associated_4" ) );
session.persist( new AssociatedEntity( 5L, "associated_5" ) );
session.persist( new AssociatedEntity( 6L, "associated_6" ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.createQuery( "from TestEntity where size(embeddable.oneToMany) > 0", TestEntity.class )
.getResultList()
.forEach( t -> ( (AssociationChildThree) t.getEmbeddable() ).getOneToMany().clear() );
session.flush();
session.createMutationQuery( "delete from TestEntity" ).executeUpdate();
session.createMutationQuery( "delete from AssociatedEntity" ).executeUpdate();
} );
}
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@Embedded
private ParentEmbeddable embeddable;
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
}
@Entity( name = "AssociatedEntity" )
static class AssociatedEntity {
@Id
private Long id;
private String name;
public AssociatedEntity() {
}
public AssociatedEntity(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
@Embeddable
@DiscriminatorValue( "child_one" )
static class AssociationChildOne extends ParentEmbeddable {
@ManyToOne
private AssociatedEntity manyToOne;
public AssociationChildOne() {
}
public AssociationChildOne(String parentProp, AssociatedEntity manyToOne) {
super( parentProp );
this.manyToOne = manyToOne;
}
public AssociatedEntity getManyToOne() {
return manyToOne;
}
public void setManyToOne(AssociatedEntity manyToOne) {
this.manyToOne = manyToOne;
}
}
@Embeddable
@DiscriminatorValue( "sub_child_one" )
static class AssociationSubChildOne extends AssociationChildOne {
public AssociationSubChildOne() {
}
public AssociationSubChildOne(String parentProp, AssociatedEntity manyToOne) {
super( parentProp, manyToOne );
}
}
@Embeddable
@DiscriminatorValue( "child_two" )
static class AssociationChildTwo extends ParentEmbeddable {
@ManyToMany
private List<AssociatedEntity> manyToMany = new ArrayList<>();
public AssociationChildTwo() {
}
public AssociationChildTwo(String parentProp) {
super( parentProp );
}
public List<AssociatedEntity> getManyToMany() {
return manyToMany;
}
}
@Embeddable
@DiscriminatorValue( "child_three" )
static class AssociationChildThree extends ParentEmbeddable {
@OneToMany( fetch = FetchType.EAGER )
@JoinColumn
private List<AssociatedEntity> oneToMany = new ArrayList<>();
public AssociationChildThree() {
}
public AssociationChildThree(String parentProp) {
super( parentProp );
}
public List<AssociatedEntity> getOneToMany() {
return oneToMany;
}
}
}

View File

@ -0,0 +1,176 @@
/*
* 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.inheritance.embeddable;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.testing.jdbc.SQLStatementInspector;
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.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
EmbeddableInheritanceAttributeOverrideTest.TestEntity.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@SessionFactory( useCollectingStatementInspector = true )
public class EmbeddableInheritanceAttributeOverrideTest {
@Test
public void testQuery(SessionFactoryScope scope) {
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
inspector.clear();
scope.inTransaction( session -> {
final TestEntity result = session.createQuery(
"from TestEntity where id = 1",
TestEntity.class
).getSingleResult();
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_disc", 1 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 1 );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result.getEmbeddable() ).getChildTwoProp() ).isEqualTo( 1L );
} );
inspector.clear();
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select embeddable from TestEntity where id = 2",
ParentEmbeddable.class
).getSingleResult();
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_disc", 1 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 1 );
assertThat( result.getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result ).getSubChildOneProp() ).isEqualTo( 2.0 );
} );
}
@Test
public void testFindElementCollection(SessionFactoryScope scope) {
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
inspector.clear();
scope.inTransaction( session -> {
final TestEntity testEntity = session.find( TestEntity.class, 1L );
assertThat( testEntity.getEmbeddables() ).hasSize( 2 ).allSatisfy( e -> {
if ( e instanceof ChildOneEmbeddable ) {
assertThat( e ).isExactlyInstanceOf( ChildOneEmbeddable.class )
.extracting( ParentEmbeddable::getParentProp ).isEqualTo( "collection_1" );
}
else {
assertThat( e ).isExactlyInstanceOf( ChildTwoEmbeddable.class )
.extracting( ParentEmbeddable::getParentProp ).isEqualTo( "collection_2" );
}
} );
inspector.assertExecutedCount( 2 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "embeddables_disc", 1 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "child_one_col", 1 );
} );
}
@Test
public void testUpdate(SessionFactoryScope scope) {
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3" );
inspector.clear();
// update values
( (ChildTwoEmbeddable) result.getEmbeddable() ).setChildTwoProp( 33L );
} );
inspector.assertIsUpdate( 0 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "child_two_col", 1 );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( ( (ChildTwoEmbeddable) result.getEmbeddable() ).getChildTwoProp() ).isEqualTo( 33L );
inspector.clear();
result.setEmbeddable( new SubChildOneEmbeddable( "embeddable_3_new", 3, 3.0 ) );
} );
inspector.assertIsUpdate( 0 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "embeddable_disc", 1 );
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "sub_child_one_col", 1 );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3_new" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 3 );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getSubChildOneProp() ).isEqualTo( 3.0 );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity testEntity = new TestEntity( 1L, new ChildTwoEmbeddable( "embeddable_1", 1L ) );
testEntity.getEmbeddables().add( new ChildOneEmbeddable( "collection_1", 1 ) );
testEntity.getEmbeddables().add( new ChildTwoEmbeddable( "collection_2", 1L ) );
session.persist( testEntity );
session.persist( new TestEntity( 2L, new SubChildOneEmbeddable( "embeddable_2", 2, 2.0 ) ) );
session.persist( new TestEntity( 3L, new ChildTwoEmbeddable( "embeddable_3", 3L ) ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@Embedded
@AttributeOverride( name = "{discriminator}", column = @Column( name = "embeddable_disc" ) )
@AttributeOverride( name = "subChildOneProp", column = @Column( name = "sub_child_one_col" ) )
@AttributeOverride( name = "childTwoProp", column = @Column( name = "child_two_col" ) )
private ParentEmbeddable embeddable;
@ElementCollection
@AttributeOverride( name = "{discriminator}", column = @Column( name = "embeddables_disc" ) )
@AttributeOverride( name = "childOneProp", column = @Column( name = "child_one_col" ) )
private List<ParentEmbeddable> embeddables = new ArrayList<>();
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
public List<ParentEmbeddable> getEmbeddables() {
return embeddables;
}
}
}

View File

@ -0,0 +1,162 @@
/*
* 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.inheritance.embeddable;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@DomainModel( annotatedClasses = {
JsonAggregateEmbeddableInheritanceTest.TestEntity.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@SessionFactory
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsJsonAggregate.class )
public class JsonAggregateEmbeddableInheritanceTest {
@Test
public void testFind(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 1L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 1 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ParentEmbeddable.class );
} );
}
@Test
public void testQueryEntity(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.createQuery(
"from TestEntity where id = 2",
TestEntity.class
).getSingleResult();
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result.getEmbeddable() ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@Test
public void testQueryEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select embeddable from TestEntity where id = 4",
ParentEmbeddable.class
).getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "embeddable_4" );
assertThat( result ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result ).getChildOneProp() ).isEqualTo( 4 );
assertThat( ( (SubChildOneEmbeddable) result ).getSubChildOneProp() ).isEqualTo( 4.0 );
} );
}
@Test
public void testQueryJoinedEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select e from TestEntity t join t.embeddable e where t.id = 2",
ParentEmbeddable.class
).getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@Test
public void testUpdate(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_5" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 5 );
// update values
result.getEmbeddable().setParentProp( "embeddable_5_new" );
( (ChildOneEmbeddable) result.getEmbeddable() ).setChildOneProp( 55 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_5_new" );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 55 );
result.setEmbeddable( new SubChildOneEmbeddable( "embeddable_6", 6, 6.0 ) );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_6" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 6 );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getSubChildOneProp() ).isEqualTo( 6.0 );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new TestEntity( 1L, new ChildOneEmbeddable( "embeddable_1", 1 ) ) );
session.persist( new TestEntity( 2L, new ChildTwoEmbeddable( "embeddable_2", 2L ) ) );
session.persist( new TestEntity( 3L, new ParentEmbeddable( "embeddable_3" ) ) );
session.persist( new TestEntity( 4L, new SubChildOneEmbeddable( "embeddable_4", 4, 4.0 ) ) );
session.persist( new TestEntity( 5L, new ChildOneEmbeddable( "embeddable_5", 5 ) ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@Embedded
@JdbcTypeCode( SqlTypes.JSON )
private ParentEmbeddable embeddable;
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.inheritance.embeddable;
import java.io.Serializable;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;
/**
* @author Marco Belladelli
*/
//tag::embeddable-inheritance-parent-example[]
@Embeddable
@DiscriminatorValue( "parent" )
@DiscriminatorColumn( name = "embeddable_type" )
class ParentEmbeddable implements Serializable {
private String parentProp;
// ...
//end::embeddable-inheritance-parent-example[]
public ParentEmbeddable() {
}
public ParentEmbeddable(String parentProp) {
this.parentProp = parentProp;
}
public String getParentProp() {
return parentProp;
}
public void setParentProp(String parentProp) {
this.parentProp = parentProp;
}
//tag::embeddable-inheritance-parent-example[]
}
//end::embeddable-inheritance-parent-example[]

View File

@ -0,0 +1,343 @@
/*
* 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.inheritance.embeddable;
import java.util.Set;
import org.hibernate.annotations.Struct;
import org.hibernate.boot.ResourceStreamLocator;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.spi.AdditionalMappingContributions;
import org.hibernate.boot.spi.AdditionalMappingContributor;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.PostgresPlusDialect;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.procedure.ProcedureParameter;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ParameterMode;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Marco Belladelli
*/
@BootstrapServiceRegistry(
javaServices = @BootstrapServiceRegistry.JavaService(
role = AdditionalMappingContributor.class,
impl = StructAggregateEmbeddableInheritanceTest.class
),
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel( annotatedClasses = {
StructAggregateEmbeddableInheritanceTest.TestEntity.class,
ParentEmbeddable.class,
ChildOneEmbeddable.class,
SubChildOneEmbeddable.class,
ChildTwoEmbeddable.class,
} )
@SessionFactory
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsStructAggregate.class )
public class StructAggregateEmbeddableInheritanceTest implements AdditionalMappingContributor {
@Test
public void testFind(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 1L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_1" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 1 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 3L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_3" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ParentEmbeddable.class );
} );
}
@Test
public void testQueryEntity(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.createQuery(
"from TestEntity where id = 2",
TestEntity.class
).getSingleResult();
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result.getEmbeddable() ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@Test
public void testQueryEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select embeddable from TestEntity where id = 4",
ParentEmbeddable.class
).getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "embeddable_4" );
assertThat( result ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result ).getChildOneProp() ).isEqualTo( 4 );
assertThat( ( (SubChildOneEmbeddable) result ).getSubChildOneProp() ).isEqualTo( 4.0 );
} );
}
@Test
public void testQueryJoinedEmbeddable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ParentEmbeddable result = session.createQuery(
"select e from TestEntity t join t.embeddable e where t.id = 2",
ParentEmbeddable.class
).getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "embeddable_2" );
assertThat( result ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result ).getChildTwoProp() ).isEqualTo( 2L );
} );
}
@Test
public void testUpdate(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_5" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( ChildOneEmbeddable.class );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 5 );
// update values
result.getEmbeddable().setParentProp( "embeddable_5_new" );
( (ChildOneEmbeddable) result.getEmbeddable() ).setChildOneProp( 55 );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_5_new" );
assertThat( ( (ChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 55 );
result.setEmbeddable( new SubChildOneEmbeddable( "embeddable_6", 6, 6.0 ) );
} );
scope.inTransaction( session -> {
final TestEntity result = session.find( TestEntity.class, 5L );
assertThat( result.getEmbeddable().getParentProp() ).isEqualTo( "embeddable_6" );
assertThat( result.getEmbeddable() ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getChildOneProp() ).isEqualTo( 6 );
assertThat( ( (SubChildOneEmbeddable) result.getEmbeddable() ).getSubChildOneProp() ).isEqualTo( 6.0 );
} );
}
@Test
public void testFunction(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final ProcedureCall structFunction = session.createStoredProcedureCall( "structFunction" )
.markAsFunctionCall( ParentEmbeddable.class );
//noinspection unchecked
final ParentEmbeddable result = (ParentEmbeddable) structFunction.getSingleResult();
assertThat( result.getParentProp() ).isEqualTo( "function_embeddable" );
assertThat( result ).isExactlyInstanceOf( SubChildOneEmbeddable.class );
assertThat( ( (SubChildOneEmbeddable) result ).getChildOneProp() ).isEqualTo( 1 );
assertThat( ( (SubChildOneEmbeddable) result ).getSubChildOneProp() ).isEqualTo( 1.0 );
} );
}
@Test
@SkipForDialect( dialectClass = PostgreSQLDialect.class, majorVersion = 10, reason = "Procedures were only introduced in version 11" )
@SkipForDialect( dialectClass = PostgresPlusDialect.class, majorVersion = 10, reason = "Procedures were only introduced in version 11" )
@SkipForDialect( dialectClass = DB2Dialect.class, reason = "DB2 does not support struct types in procedures" )
public void testProcedure(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Dialect dialect = session.getJdbcServices().getDialect();
final ParameterMode parameterMode;
if ( dialect instanceof PostgreSQLDialect ) {
parameterMode = ParameterMode.INOUT;
}
else {
parameterMode = ParameterMode.OUT;
}
final ProcedureCall structFunction = session.createStoredProcedureCall( "structProcedure" );
final ProcedureParameter<ParentEmbeddable> resultParameter = structFunction.registerParameter(
"structType",
ParentEmbeddable.class,
parameterMode
);
structFunction.setParameter( resultParameter, null );
final ParentEmbeddable result = structFunction.getOutputs().getOutputParameterValue( resultParameter );
assertThat( result ).isInstanceOf( ParentEmbeddable.class );
assertThat( result.getParentProp() ).isEqualTo( "procedure_embeddable" );
assertThat( result ).isExactlyInstanceOf( ChildTwoEmbeddable.class );
assertThat( ( (ChildTwoEmbeddable) result ).getChildTwoProp() ).isEqualTo( 2 );
} );
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new TestEntity( 1L, new ChildOneEmbeddable( "embeddable_1", 1 ) ) );
session.persist( new TestEntity( 2L, new ChildTwoEmbeddable( "embeddable_2", 2L ) ) );
session.persist( new TestEntity( 3L, new ParentEmbeddable( "embeddable_3" ) ) );
session.persist( new TestEntity( 4L, new SubChildOneEmbeddable( "embeddable_4", 4, 4.0 ) ) );
session.persist( new TestEntity( 5L, new ChildOneEmbeddable( "embeddable_5", 5 ) ) );
} );
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
}
@Override
public void contribute(
AdditionalMappingContributions contributions,
InFlightMetadataCollector metadata,
ResourceStreamLocator resourceStreamLocator,
MetadataBuildingContext buildingContext) {
final Namespace namespace = new Namespace(
PhysicalNamingStrategyStandardImpl.INSTANCE,
null,
new Namespace.Name( null, null )
);
//---------------------------------------------------------
// PostgreSQL
//---------------------------------------------------------
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"PostgreSQL structFunction",
namespace,
"create function structFunction() returns inheritance_embeddable as $$ declare result inheritance_embeddable; begin result.parentProp = 'function_embeddable'; result.childOneProp = 1; result.subChildOneProp = 1.0; result.childTwoProp = null; result.embeddable_type = 'sub_child_one'; return result; end $$ language plpgsql",
"drop function structFunction",
Set.of( PostgreSQLDialect.class.getName() )
)
);
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"PostgreSQL structProcedure",
namespace,
"create procedure structProcedure(INOUT result inheritance_embeddable) AS $$ declare res inheritance_embeddable; begin res.parentProp = 'procedure_embeddable'; res.childOneProp = null; res.subChildOneProp = null; res.childTwoProp = 2; res.embeddable_type = 'ChildTwoEmbeddable'; result = res; end $$ language plpgsql",
"drop procedure structProcedure",
Set.of( PostgreSQLDialect.class.getName() )
)
);
//---------------------------------------------------------
// PostgrePlus
//---------------------------------------------------------
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"PostgrePlus structFunction",
namespace,
"create function structFunction() returns inheritance_embeddable as $$ declare result inheritance_embeddable; begin result.parentProp = 'function_embeddable'; result.childOneProp = 1; result.subChildOneProp = 1.0; result.childTwoProp = null; result.embeddable_type = 'sub_child_one'; return result; end $$ language plpgsql",
"drop function structFunction",
Set.of( PostgresPlusDialect.class.getName() )
)
);
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"PostgrePlus structProcedure",
namespace,
"create procedure structProcedure(result INOUT inheritance_embeddable) AS $$ declare res inheritance_embeddable; begin res.parentProp = 'procedure_embeddable'; res.childOneProp = null; res.subChildOneProp = null; res.childTwoProp = 2; res.embeddable_type = 'ChildTwoEmbeddable'; result = res; end $$ language plpgsql",
"drop procedure structProcedure",
Set.of( PostgresPlusDialect.class.getName() )
)
);
//---------------------------------------------------------
// DB2
//---------------------------------------------------------
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"DB2 structFunction",
namespace,
"create function structFunction() returns inheritance_embeddable language sql RETURN select inheritance_embeddable()..parentProp('function_embeddable')..childOneProp(1)..subChildOneProp(1.0)..embeddable_type('sub_child_one') from (values (1)) t",
"drop function structFunction",
Set.of( DB2Dialect.class.getName() )
)
);
//---------------------------------------------------------
// Oracle
//---------------------------------------------------------
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"Oracle structFunction",
namespace,
"create function structFunction return inheritance_embeddable is result inheritance_embeddable; begin " +
"result := inheritance_embeddable(" +
"parentProp => 'function_embeddable'," +
"childOneProp => 1," +
"subChildOneProp => 1.0," +
"childTwoProp => null," +
"embeddable_type => 'sub_child_one'" +
"); return result; end;",
"drop function structFunction",
Set.of( OracleDialect.class.getName() )
)
);
contributions.contributeAuxiliaryDatabaseObject(
new NamedAuxiliaryDatabaseObject(
"Oracle structProcedure",
namespace,
"create procedure structProcedure(result OUT inheritance_embeddable) AS begin " +
"result := inheritance_embeddable(" +
"parentProp => 'procedure_embeddable'," +
"childOneProp => null," +
"subChildOneProp => null," +
"childTwoProp => 2," +
"embeddable_type => 'ChildTwoEmbeddable'" +
"); end;",
"drop procedure structProcedure",
Set.of( OracleDialect.class.getName() )
)
);
}
@Entity( name = "TestEntity" )
static class TestEntity {
@Id
private Long id;
@Embedded
@Struct( name = "inheritance_embeddable" )
private ParentEmbeddable embeddable;
public TestEntity() {
}
public TestEntity(Long id, ParentEmbeddable embeddable) {
this.id = id;
this.embeddable = embeddable;
}
public ParentEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(ParentEmbeddable embeddable) {
this.embeddable = embeddable;
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.inheritance.embeddable;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Embeddable;
/**
* @author Marco Belladelli
*/
//tag::embeddable-inheritance-sub-child-one-example[]
@Embeddable
@DiscriminatorValue( "sub_child_one" )
class SubChildOneEmbeddable extends ChildOneEmbeddable {
private Double subChildOneProp;
// ...
//end::embeddable-inheritance-sub-child-one-example[]
public SubChildOneEmbeddable() {
}
public SubChildOneEmbeddable(String parentProp, Integer childOneProp, Double subChildOneProp) {
super( parentProp, childOneProp );
this.subChildOneProp = subChildOneProp;
}
public Double getSubChildOneProp() {
return subChildOneProp;
}
public void setSubChildOneProp(Double subChildOneProp) {
this.subChildOneProp = subChildOneProp;
}
//tag::embeddable-inheritance-sub-child-one-example[]
}
//end::embeddable-inheritance-sub-child-one-example[]

View File

@ -0,0 +1,21 @@
/*
* 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.jpa.metamodel.attributeInSuper;
import java.io.Serializable;
import jakarta.persistence.MappedSuperclass;
/**
* @author Steve Ebersole
*/
@MappedSuperclass
public class AbstractWorkOrderId implements Serializable {
private String workOrder;
private Long plantId;
/* other stuffs */
}

View File

@ -12,7 +12,7 @@ import jakarta.persistence.Embeddable;
* @author Steve Ebersole
*/
@Embeddable
public class WorkOrderComponentId extends WorkOrderId {
public class WorkOrderComponentId extends AbstractWorkOrderId {
private Long lineNumber;
/* other stuffs */
}

View File

@ -6,17 +6,11 @@
*/
package org.hibernate.orm.test.jpa.metamodel.attributeInSuper;
import java.io.Serializable;
import jakarta.persistence.Embeddable;
import jakarta.persistence.MappedSuperclass;
/**
* @author Steve Ebersole
*/
@Embeddable
@MappedSuperclass
public class WorkOrderId implements Serializable {
private String workOrder;
private Long plantId;
/* other stuffs */
public class WorkOrderId extends AbstractWorkOrderId {
}

View File

@ -149,3 +149,54 @@ or obtain a substring by start and end index.
`stringPath[2]` is syntax sugar for `substring(stringPath, 2, 1)` and returns a `Character`.
`stringPath[2:3]` is syntax sugar for `substring(stringPath, 2, 3-2+1)`,
where `3-2+1` is the expression to determine the desired string length.
[[embeddable-inheritance]]
== Embeddable Inheritance
Another new feature of this version is discriminator-based inheritance for `@Embeddable` types. An `@Embeddable` class
may be extended by other `@Embeddable` classes, in which case the `@Embedded` properties using that type will
rely on an additional discriminator column to store information about the composite value's subtype.
When retrieving the inherited embedded property, Hibernate will read the discriminator value and instantiate the
correct `@Embeddable` subtype with its corresponding properties.
For example, a mapping like this:
[source,java]
----
@Embeddable
@DiscriminatorValue( "parent" )
@DiscriminatorColumn( name = "embeddable_type" )
class ParentEmbeddable implements Serializable {
private String parentProp;
// ...
}
@Embeddable
@DiscriminatorValue( "child_one" )
class ChildOneEmbeddable extends ParentEmbeddable {
private Integer childOneProp;
// ...
}
@Entity
class TestEntity {
@Embedded
private ParentEmbeddable embeddable;
// ...
}
----
Will result in the following table structure:
[source,sql]
----
create table TestEntity (
-- ...
embeddable_type varchar(31) not null,
parentProp varchar(255),
childOneProp integer,
-- ...
)
----
For more detailed information please refer to the link:{user-guide-url}#embeddable-inheritance[Embeddable inheritance] user guide chapter.