HHH-15604 Identically-named association in entity root and elementcollection of embeddables leads to assertion error
This commit is contained in:
parent
cfc7b7ba66
commit
00018731f8
|
@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
import org.hibernate.spi.DotIdentifierSequence;
|
||||
|
||||
/**
|
||||
* Access to a group of ModelPart by name or for iteration.
|
||||
|
@ -39,4 +40,25 @@ public interface ModelPartContainer extends ModelPart {
|
|||
}
|
||||
return modelPartContainer.findSubPart( path.substring( nextStart ), null );
|
||||
}
|
||||
|
||||
default ModelPart findByPath(DotIdentifierSequence path) {
|
||||
ModelPartContainer modelPartContainer = this;
|
||||
final DotIdentifierSequence endPart;
|
||||
if ( path.getParent() != null ) {
|
||||
final DotIdentifierSequence[] parts = path.getParts();
|
||||
final int end = parts.length - 1;
|
||||
for ( int i = 0; i < end; i++ ) {
|
||||
DotIdentifierSequence part = parts[i];
|
||||
modelPartContainer = (ModelPartContainer) modelPartContainer.findSubPart(
|
||||
part.getLocalName(),
|
||||
null
|
||||
);
|
||||
}
|
||||
endPart = parts[end];
|
||||
}
|
||||
else {
|
||||
endPart = path;
|
||||
}
|
||||
return modelPartContainer.findSubPart( endPart.getLocalName(), null );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Collection;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.spi.DotIdentifierSequence;
|
||||
|
||||
/**
|
||||
* The path for a selectable.
|
||||
|
@ -19,7 +20,7 @@ import org.hibernate.Incubating;
|
|||
* @author Christian Beikov
|
||||
*/
|
||||
@Incubating
|
||||
public class SelectablePath implements Serializable {
|
||||
public class SelectablePath implements Serializable, DotIdentifierSequence {
|
||||
private final SelectablePath parent;
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
@ -36,6 +37,18 @@ public class SelectablePath implements Serializable {
|
|||
this.index = parent.index + 1;
|
||||
}
|
||||
|
||||
public static SelectablePath parse(String path) {
|
||||
if ( path == null || path.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
final String[] parts = path.split( "\\." );
|
||||
SelectablePath selectablePath = new SelectablePath( parts[0] );
|
||||
for ( int i = 1; i < parts.length; i++ ) {
|
||||
selectablePath = selectablePath.append( parts[i] );
|
||||
}
|
||||
return selectablePath;
|
||||
}
|
||||
|
||||
public SelectablePath[] getParts() {
|
||||
final SelectablePath[] array = new SelectablePath[index + 1];
|
||||
parts( array );
|
||||
|
@ -72,14 +85,26 @@ public class SelectablePath implements Serializable {
|
|||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectablePath getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectablePath append(String selectableName) {
|
||||
return new SelectablePath( this, selectableName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullPath() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder( name.length() * index );
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.hibernate.internal.util.IndexedConsumer;
|
|||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.ManyToOne;
|
||||
import org.hibernate.mapping.OneToOne;
|
||||
|
@ -49,9 +50,10 @@ import org.hibernate.metamodel.mapping.ModelPartContainer;
|
|||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||
import org.hibernate.metamodel.mapping.VirtualModelPart;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.collection.AbstractCollectionPersister;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
|
@ -151,7 +153,7 @@ public class ToOneAttributeMapping
|
|||
Capture the other side's name of a possibly bidirectional association to allow resolving circular fetches.
|
||||
It may be null if the referenced property is a non-entity.
|
||||
*/
|
||||
private final String bidirectionalAttributeName;
|
||||
private final SelectablePath bidirectionalAttributePath;
|
||||
private final TableGroupProducer declaringTableGroupProducer;
|
||||
|
||||
private ForeignKeyDescriptor foreignKeyDescriptor;
|
||||
|
@ -217,6 +219,8 @@ public class ToOneAttributeMapping
|
|||
this.unwrapProxy = bootValue.isUnwrapProxy();
|
||||
this.entityMappingType = entityMappingType;
|
||||
|
||||
this.navigableRole = navigableRole;
|
||||
this.declaringTableGroupProducer = resolveDeclaringTableGroupProducer( declaringEntityPersister, navigableRole );
|
||||
if ( bootValue instanceof ManyToOne ) {
|
||||
final ManyToOne manyToOne = (ManyToOne) bootValue;
|
||||
this.notFoundAction = ( (ManyToOne) bootValue ).getNotFoundAction();
|
||||
|
@ -229,7 +233,10 @@ public class ToOneAttributeMapping
|
|||
final PersistentClass entityBinding = manyToOne.getMetadata()
|
||||
.getEntityBinding( manyToOne.getReferencedEntityName() );
|
||||
if ( referencedPropertyName == null ) {
|
||||
String bidirectionalAttributeName = null;
|
||||
SelectablePath bidirectionalAttributeName = null;
|
||||
final String propertyPath = bootValue.getPropertyName() == null
|
||||
? name
|
||||
: bootValue.getPropertyName();
|
||||
if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE ) {
|
||||
// Handle join table cases
|
||||
for ( Join join : entityBinding.getJoinClosure() ) {
|
||||
|
@ -238,47 +245,35 @@ public class ToOneAttributeMapping
|
|||
&& join.getTable() == manyToOne.getTable()
|
||||
&& equal( join.getKey(), manyToOne ) ) {
|
||||
//noinspection deprecation
|
||||
bidirectionalAttributeName = join.getPropertyIterator().next().getName();
|
||||
bidirectionalAttributeName = SelectablePath.parse( join.getPropertyIterator().next().getName() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Simple one-to-one mapped by cases
|
||||
if ( bidirectionalAttributeName == null ) {
|
||||
for ( Property property : entityBinding.getPropertyClosure() ) {
|
||||
final Value value = property.getValue();
|
||||
if ( value instanceof OneToOne ) {
|
||||
final OneToOne oneToOne = (OneToOne) value;
|
||||
if ( name.equals( oneToOne.getMappedByProperty() )
|
||||
&& oneToOne.getReferencedEntityName()
|
||||
.equals( declaringType.getJavaType().getJavaType().getTypeName() ) ) {
|
||||
bidirectionalAttributeName = property.getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bidirectionalAttributeName = findBidirectionalOneToOneAttributeName(
|
||||
propertyPath,
|
||||
declaringType,
|
||||
null,
|
||||
entityBinding.getPropertyClosure()
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( Property property : entityBinding.getPropertyClosure() ) {
|
||||
final Value value = property.getValue();
|
||||
if ( value instanceof Collection ) {
|
||||
final Collection collection = (Collection) value;
|
||||
if ( name.equals(collection.getMappedByProperty() )
|
||||
&& collection.getElement().getType().getName()
|
||||
.equals( declaringType.getJavaType().getJavaType().getTypeName() ) ) {
|
||||
bidirectionalAttributeName = property.getName();
|
||||
break;
|
||||
bidirectionalAttributeName = findBidirectionalOneToManyAttributeName(
|
||||
propertyPath,
|
||||
declaringType,
|
||||
null,
|
||||
entityBinding.getPropertyClosure()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.bidirectionalAttributeName = bidirectionalAttributeName;
|
||||
this.bidirectionalAttributePath = bidirectionalAttributeName;
|
||||
}
|
||||
else {
|
||||
// Only set the bidirectional attribute name if the referenced property can actually be circular i.e. an entity type
|
||||
final Property property = entityBinding.getProperty( referencedPropertyName );
|
||||
this.bidirectionalAttributeName = property != null && property.getValue() instanceof EntityType
|
||||
? referencedPropertyName
|
||||
this.bidirectionalAttributePath = property != null && property.getValue() instanceof EntityType
|
||||
? SelectablePath.parse( referencedPropertyName )
|
||||
: null;
|
||||
}
|
||||
if ( bootValue.isNullable() ) {
|
||||
|
@ -293,7 +288,7 @@ public class ToOneAttributeMapping
|
|||
navigableRole.getParent().getParent().getFullPath().substring( declaringEntityPersister.getNavigableRole().getFullPath().length() + 1 ) );
|
||||
assert pluralAttribute != null;
|
||||
|
||||
final QueryableCollection persister = (QueryableCollection) pluralAttribute.getCollectionDescriptor();
|
||||
final AbstractCollectionPersister persister = (AbstractCollectionPersister) pluralAttribute.getCollectionDescriptor();
|
||||
isKeyTableNullable = !persister.getTableName().equals( targetTableName );
|
||||
}
|
||||
else {
|
||||
|
@ -355,21 +350,12 @@ public class ToOneAttributeMapping
|
|||
the navigable path is NavigablePath(Card.fields.{element}.{id}.card) and it does not contain the "primaryKey" part,
|
||||
so in order to recognize the bidirectionality the "primaryKey." is removed from the otherSidePropertyName value.
|
||||
*/
|
||||
// todo (6.0): find a better solution for the embeddable part name not in the NavigablePath
|
||||
final OneToOne oneToOne = (OneToOne) bootValue;
|
||||
String bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
|
||||
oneToOne.getMappedByProperty(),
|
||||
'.'
|
||||
);
|
||||
|
||||
if ( bidirectionalAttributeName == null ) {
|
||||
this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty(
|
||||
referencedPropertyName,
|
||||
'.'
|
||||
);
|
||||
if ( oneToOne.getMappedByProperty() == null ) {
|
||||
this.bidirectionalAttributePath = SelectablePath.parse( referencedPropertyName );
|
||||
}
|
||||
else {
|
||||
this.bidirectionalAttributeName = bidirectionalAttributeName;
|
||||
this.bidirectionalAttributePath = SelectablePath.parse( oneToOne.getMappedByProperty() );
|
||||
}
|
||||
notFoundAction = null;
|
||||
isKeyTableNullable = isNullable();
|
||||
|
@ -377,8 +363,7 @@ public class ToOneAttributeMapping
|
|||
isInternalLoadNullable = isNullable();
|
||||
}
|
||||
|
||||
this.navigableRole = navigableRole;
|
||||
this.declaringTableGroupProducer = resolveDeclaringTableGroupProducer( declaringEntityPersister );
|
||||
|
||||
if ( referencedPropertyName == null ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
|
@ -520,6 +505,75 @@ public class ToOneAttributeMapping
|
|||
}
|
||||
}
|
||||
|
||||
private static SelectablePath findBidirectionalOneToManyAttributeName(
|
||||
String propertyPath,
|
||||
ManagedMappingType declaringType,
|
||||
SelectablePath parentSelectablePath,
|
||||
java.util.Collection<Property> properties) {
|
||||
for ( Property property : properties ) {
|
||||
final Value value = property.getValue();
|
||||
if ( value instanceof Component ) {
|
||||
final SelectablePath bidirectionalAttributeName = findBidirectionalOneToManyAttributeName(
|
||||
propertyPath,
|
||||
declaringType,
|
||||
parentSelectablePath == null
|
||||
? SelectablePath.parse( property.getName() )
|
||||
: parentSelectablePath.append( property.getName() ),
|
||||
( (Component) value ).getProperties()
|
||||
);
|
||||
if ( bidirectionalAttributeName != null ) {
|
||||
return bidirectionalAttributeName;
|
||||
}
|
||||
}
|
||||
if ( value instanceof Collection ) {
|
||||
final Collection collection = (Collection) value;
|
||||
if ( propertyPath.equals( collection.getMappedByProperty() )
|
||||
&& collection.getElement().getType().getName()
|
||||
.equals( declaringType.getJavaType().getJavaType().getTypeName() ) ) {
|
||||
return parentSelectablePath == null
|
||||
? SelectablePath.parse( property.getName() )
|
||||
: parentSelectablePath.append( property.getName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SelectablePath findBidirectionalOneToOneAttributeName(
|
||||
String propertyPath,
|
||||
ManagedMappingType declaringType,
|
||||
SelectablePath parentSelectablePath,
|
||||
java.util.Collection<Property> properties) {
|
||||
for ( Property property : properties ) {
|
||||
final Value value = property.getValue();
|
||||
if ( value instanceof Component ) {
|
||||
final SelectablePath bidirectionalAttributeName = findBidirectionalOneToOneAttributeName(
|
||||
propertyPath,
|
||||
declaringType,
|
||||
parentSelectablePath == null
|
||||
? SelectablePath.parse( property.getName() )
|
||||
: parentSelectablePath.append( property.getName() ),
|
||||
( (Component) value ).getProperties()
|
||||
);
|
||||
if ( bidirectionalAttributeName != null ) {
|
||||
return bidirectionalAttributeName;
|
||||
}
|
||||
}
|
||||
else if ( value instanceof OneToOne ) {
|
||||
final OneToOne oneToOne = (OneToOne) value;
|
||||
if (declaringTableGroupProducer.getNavigableRole().getLocalName().equals( oneToOne.getReferencedEntityName() )
|
||||
&& propertyPath.equals( oneToOne.getMappedByProperty() )
|
||||
&& oneToOne.getReferencedEntityName()
|
||||
.equals( declaringType.getJavaType().getJavaType().getTypeName() ) ) {
|
||||
return parentSelectablePath == null
|
||||
? SelectablePath.parse( property.getName() )
|
||||
: parentSelectablePath.append( property.getName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FetchTiming adjustFetchTiming(FetchTiming mappedFetchTiming, ToOne bootValue) {
|
||||
if ( bootValue instanceof ManyToOne ) {
|
||||
if ( ( (ManyToOne) bootValue ).getNotFoundAction() != null ) {
|
||||
|
@ -529,9 +583,9 @@ public class ToOneAttributeMapping
|
|||
return mappedFetchTiming;
|
||||
}
|
||||
|
||||
private TableGroupProducer resolveDeclaringTableGroupProducer(EntityPersister declaringEntityPersister) {
|
||||
private static TableGroupProducer resolveDeclaringTableGroupProducer(EntityPersister declaringEntityPersister, NavigableRole navigableRole) {
|
||||
// Also handle cases where a collection contains an embeddable, that contains an association
|
||||
NavigableRole parentRole = getNavigableRole().getParent();
|
||||
NavigableRole parentRole = navigableRole.getParent();
|
||||
String collectionRole = null;
|
||||
do {
|
||||
final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact(
|
||||
|
@ -579,7 +633,7 @@ public class ToOneAttributeMapping
|
|||
this.targetKeyPropertyName = original.targetKeyPropertyName;
|
||||
this.targetKeyPropertyNames = original.targetKeyPropertyNames;
|
||||
this.cardinality = original.cardinality;
|
||||
this.bidirectionalAttributeName = original.bidirectionalAttributeName;
|
||||
this.bidirectionalAttributePath = original.bidirectionalAttributePath;
|
||||
this.declaringTableGroupProducer = declaringTableGroupProducer;
|
||||
this.isInternalLoadNullable = original.isInternalLoadNullable;
|
||||
}
|
||||
|
@ -676,7 +730,7 @@ public class ToOneAttributeMapping
|
|||
public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) {
|
||||
assert identifyingColumnsTableExpression != null;
|
||||
this.foreignKeyDescriptor = foreignKeyDescriptor;
|
||||
if ( cardinality == Cardinality.ONE_TO_ONE && bidirectionalAttributeName != null ) {
|
||||
if ( cardinality == Cardinality.ONE_TO_ONE && bidirectionalAttributePath != null ) {
|
||||
this.sideNature = ForeignKeyDescriptor.Nature.TARGET;
|
||||
}
|
||||
else {
|
||||
|
@ -795,7 +849,7 @@ public class ToOneAttributeMapping
|
|||
final AssociationKey associationKey = foreignKeyDescriptor.getAssociationKey();
|
||||
|
||||
if ( creationState.isAssociationKeyVisited( associationKey )
|
||||
|| bidirectionalAttributeName != null && !creationState.isRegisteringVisitedAssociationKeys() ) {
|
||||
|| bidirectionalAttributePath != null && !creationState.isRegisteringVisitedAssociationKeys() ) {
|
||||
NavigablePath parentNavigablePath = fetchablePath.getParent();
|
||||
assert parentNavigablePath.equals( fetchParent.getNavigablePath() );
|
||||
// The parent navigable path is {fk} if we are creating the domain result for the foreign key for a circular fetch
|
||||
|
@ -906,7 +960,8 @@ public class ToOneAttributeMapping
|
|||
ModelPart parentModelPart,
|
||||
NavigablePath fetchablePath,
|
||||
DomainResultCreationState creationState) {
|
||||
if ( bidirectionalAttributeName == null ) {
|
||||
|
||||
if ( bidirectionalAttributePath == null ) {
|
||||
/*
|
||||
check if mappedBy is on the other side of the association
|
||||
*/
|
||||
|
@ -929,7 +984,7 @@ public class ToOneAttributeMapping
|
|||
*/
|
||||
if ( parentModelPart instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) parentModelPart;
|
||||
if ( toOneAttributeMapping.bidirectionalAttributeName != null ) {
|
||||
if ( toOneAttributeMapping.bidirectionalAttributePath != null ) {
|
||||
return toOneAttributeMapping.isBidirectionalAttributeName(
|
||||
fetchablePath,
|
||||
this,
|
||||
|
@ -954,7 +1009,31 @@ public class ToOneAttributeMapping
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if ( cardinality == Cardinality.MANY_TO_ONE ) {
|
||||
else if ( parentNavigablePath.getParent() != null && creationState.resolveModelPart( parentNavigablePath.getParent() ) instanceof EmbeddedCollectionPart ) {//todo: handle recursively?
|
||||
/*
|
||||
class EntityA{
|
||||
@OneToOne(mappedBy = "identicallyNamedAssociation", fetch = FetchType.EAGER)
|
||||
private EntityB b;
|
||||
}
|
||||
|
||||
class EntityB {
|
||||
@OneToOne
|
||||
private EntityA identicallyNamedAssociation;
|
||||
|
||||
private EmbeddableB embeddable;
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
class EmbeddableB {
|
||||
>>>>>>>> this association is not bidirectional <<<<<<<<
|
||||
@OneToOne
|
||||
private EntityA identicallyNamedAssociation;
|
||||
}
|
||||
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
else if ( cardinality == Cardinality.MANY_TO_ONE ) {
|
||||
/*
|
||||
class Child {
|
||||
@OneToOne(mappedBy = "biologicalChild")
|
||||
|
@ -973,12 +1052,12 @@ public class ToOneAttributeMapping
|
|||
final NavigablePath grandparentNavigablePath = parentNavigablePath.getParent();
|
||||
if ( parentNavigablePath.getLocalName().equals( CollectionPart.Nature.ELEMENT.getName() )
|
||||
&& grandparentNavigablePath != null
|
||||
&& grandparentNavigablePath.getLocalName().equals( bidirectionalAttributeName ) ) {
|
||||
&& grandparentNavigablePath.isSuffix( bidirectionalAttributePath ) ) {
|
||||
final NavigablePath parentPath = grandparentNavigablePath.getParent();
|
||||
// This can be null for a collection loader
|
||||
if ( parentPath == null ) {
|
||||
return grandparentNavigablePath.getFullPath().equals(
|
||||
entityMappingType.findSubPart( bidirectionalAttributeName ).getNavigableRole().getFullPath()
|
||||
entityMappingType.findByPath( bidirectionalAttributePath ).getNavigableRole().getFullPath()
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -999,11 +1078,7 @@ public class ToOneAttributeMapping
|
|||
}
|
||||
return false;
|
||||
}
|
||||
return parentNavigablePath.getLocalName().equals( bidirectionalAttributeName );
|
||||
}
|
||||
|
||||
public String getBidirectionalAttributeName(){
|
||||
return bidirectionalAttributeName;
|
||||
return parentNavigablePath.isSuffix( bidirectionalAttributePath );
|
||||
}
|
||||
|
||||
private Fetch createCircularBiDirectionalFetch(
|
||||
|
@ -1419,7 +1494,7 @@ public class ToOneAttributeMapping
|
|||
else {
|
||||
// case 1.1
|
||||
// Make sure the entity identifier is not a target key property i.e. this really is a unique key mapping
|
||||
return bidirectionalAttributeName != null && (
|
||||
return bidirectionalAttributePath != null && (
|
||||
!( entityMappingType.getIdentifierMapping() instanceof SingleAttributeIdentifierMapping )
|
||||
|| !targetKeyPropertyNames.contains(
|
||||
entityMappingType.getIdentifierMapping().getAttributeName()
|
||||
|
@ -1503,8 +1578,8 @@ public class ToOneAttributeMapping
|
|||
DomainResultCreationState creationState) {
|
||||
final boolean added = creationState.registerVisitedAssociationKey( foreignKeyDescriptor.getAssociationKey() );
|
||||
AssociationKey additionalAssociationKey = null;
|
||||
if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE && bidirectionalAttributeName != null ) {
|
||||
final ModelPart bidirectionalModelPart = entityMappingType.findSubPart( bidirectionalAttributeName );
|
||||
if ( cardinality == Cardinality.LOGICAL_ONE_TO_ONE && bidirectionalAttributePath != null ) {
|
||||
final ModelPart bidirectionalModelPart = entityMappingType.findByPath( bidirectionalAttributePath );
|
||||
// Add the inverse association key side as well to be able to resolve to a CircularFetch
|
||||
if ( bidirectionalModelPart instanceof ToOneAttributeMapping ) {
|
||||
assert bidirectionalModelPart.getPartMappingType() == declaringTableGroupProducer;
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
*/
|
||||
package org.hibernate.spi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.metamodel.mapping.SelectablePath;
|
||||
|
||||
/**
|
||||
* A compound name.
|
||||
* <p>
|
||||
|
@ -52,6 +57,19 @@ public interface DotIdentifierSequence {
|
|||
*/
|
||||
DotIdentifierSequence append(String subPathName);
|
||||
|
||||
default DotIdentifierSequence[] getParts() {
|
||||
final List<DotIdentifierSequence> list = new ArrayList<>();
|
||||
parts( list );
|
||||
return list.toArray(new DotIdentifierSequence[0]);
|
||||
}
|
||||
|
||||
private void parts(List<DotIdentifierSequence> list) {
|
||||
if ( getParent() != null ) {
|
||||
getParent().parts( list );
|
||||
}
|
||||
list.add( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this sequence node the root of the sequence?
|
||||
* <p>
|
||||
|
|
|
@ -192,6 +192,19 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given path is a suffix of this path
|
||||
*/
|
||||
public boolean isSuffix(DotIdentifierSequence dotIdentifierSequence) {
|
||||
if ( dotIdentifierSequence == null ) {
|
||||
return true;
|
||||
}
|
||||
if ( !getLocalName().equals( dotIdentifierSequence.getLocalName() ) ) {
|
||||
return false;
|
||||
}
|
||||
return getParent() != null && getParent().isSuffix( dotIdentifierSequence.getParent() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this path is part of the given path's parent
|
||||
*/
|
||||
|
|
|
@ -41,4 +41,8 @@ public interface FetchParentAccess extends Initializer {
|
|||
* @apiNote If already resolved, the callback is triggered immediately
|
||||
*/
|
||||
void registerResolutionListener(Consumer<Object> resolvedParentConsumer);
|
||||
|
||||
default FetchParentAccess getFetchParentAccess(){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,6 +177,11 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
return;
|
||||
}
|
||||
|
||||
if ( isParentInstanceNull() ) {
|
||||
compositeInstance = NULL_MARKER;
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT: This method might be called multiple times for the same role for a single row.
|
||||
// EmbeddableAssembler calls it as part of its `#assemble` and the RowReader calls it
|
||||
// as part of its normal Initializer handling
|
||||
|
@ -263,7 +268,6 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
compositeInstance = createCompositeInstance(
|
||||
navigablePath,
|
||||
representationStrategy,
|
||||
processingState,
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
|
@ -274,6 +278,30 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
);
|
||||
}
|
||||
|
||||
private boolean isParentInstanceNull() {
|
||||
if ( embedded instanceof CompositeIdentifierMapping ) {
|
||||
return false;
|
||||
}
|
||||
FetchParentAccess parentAccess = fetchParentAccess;
|
||||
|
||||
while ( parentAccess != null && parentAccess.isEmbeddableInitializer() ) {
|
||||
if ( parentAccess.getInitializedPart() instanceof CompositeIdentifierMapping ) {
|
||||
return false;
|
||||
}
|
||||
parentAccess = parentAccess.getFetchParentAccess();
|
||||
}
|
||||
|
||||
if ( parentAccess == null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final EntityInitializer entityInitializer = parentAccess.asEntityInitializer();
|
||||
if ( entityInitializer != null && entityInitializer.getParentKey() == null ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void extractRowState(RowProcessingState processingState) {
|
||||
stateAllNull = true;
|
||||
final boolean isKey = ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
|
||||
|
@ -340,7 +368,6 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
|
|||
private Object createCompositeInstance(
|
||||
NavigablePath navigablePath,
|
||||
EmbeddableRepresentationStrategy representationStrategy,
|
||||
RowProcessingState processingState,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
if ( !createEmptyCompositesEnabled && stateAllNull == TRUE ) {
|
||||
return NULL_MARKER;
|
||||
|
|
|
@ -45,7 +45,7 @@ public interface AggregateEmbeddableInitializer extends EmbeddableInitializer {
|
|||
return aggregateValuesArrayPositions;
|
||||
}
|
||||
else if ( fetchParentAccess instanceof EmbeddableInitializer ) {
|
||||
final FetchParentAccess parentFetchParentAccess = ( (EmbeddableInitializer) fetchParentAccess ).getFetchParentAccess();
|
||||
final FetchParentAccess parentFetchParentAccess = fetchParentAccess.getFetchParentAccess();
|
||||
return determineAggregateValuesArrayPositions( parentFetchParentAccess, structSelection );
|
||||
}
|
||||
return new int[] { structSelection.getValuesArrayPosition() };
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
package org.hibernate.sql.results.graph.entity;
|
||||
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.results.graph.FetchParentAccess;
|
||||
|
||||
/**
|
||||
* Initializer implementation for initializing entity references.
|
||||
|
|
|
@ -133,6 +133,11 @@ public abstract class AbstractBatchEntitySelectFetchInitializer extends Abstract
|
|||
return entityInitializer.getConcreteDescriptor().findAttributeMapping( propertyName ).getStateArrayPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister getConcreteDescriptor() {
|
||||
return concreteDescriptor;
|
||||
|
|
|
@ -244,6 +244,11 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPersister getConcreteDescriptor() {
|
||||
return getEntityDescriptor();
|
||||
|
|
|
@ -49,6 +49,11 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
|
|||
|
||||
protected boolean isInitialized;
|
||||
|
||||
@Override
|
||||
public FetchParentAccess getFetchParentAccess() {
|
||||
return parentAccess;
|
||||
}
|
||||
|
||||
protected Object entityInstance;
|
||||
|
||||
public EntitySelectFetchInitializer(
|
||||
|
|
Loading…
Reference in New Issue