HHH-1152 Discriminator based inheritance for embeddable types
This commit is contained in:
parent
8b5cdba5bc
commit
1c11dea006
|
@ -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)
|
||||
)
|
|
@ -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[]
|
||||
----
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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).
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 + ')';
|
||||
|
|
|
@ -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() ) ];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ public interface EmbeddableValuedModelPart extends ValuedModelPart, Fetchable, F
|
|||
|
||||
@Override
|
||||
default int getNumberOfFetchables() {
|
||||
return getEmbeddableTypeDescriptor().getNumberOfAttributeMappings();
|
||||
return getEmbeddableTypeDescriptor().getNumberOfFetchables();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -295,7 +295,7 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
|
|||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
public BasicFetch<?> generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
|
|
|
@ -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() );
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -50,4 +50,8 @@ public interface EmbeddableInitializer extends FetchParentAccess {
|
|||
}
|
||||
|
||||
void resolveState(RowProcessingState rowProcessingState);
|
||||
|
||||
default Object getDiscriminatorValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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[]
|
||||
}
|
|
@ -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[]
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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[]
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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[]
|
|
@ -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 */
|
||||
}
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue