HHH-15604 Identically-named association in entity root and elementcollection of embeddables leads to assertion error

This commit is contained in:
Andrea Boriero 2022-12-30 22:41:35 +01:00 committed by Christian Beikov
parent cfc7b7ba66
commit 00018731f8
12 changed files with 268 additions and 69 deletions

View File

@ -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 );
}
}

View File

@ -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 );

View File

@ -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;

View File

@ -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>

View File

@ -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
*/

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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() };

View File

@ -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.

View File

@ -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;

View File

@ -244,6 +244,11 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp
}
}
@Override
public FetchParentAccess getFetchParentAccess() {
return parentAccess;
}
@Override
public EntityPersister getConcreteDescriptor() {
return getEntityDescriptor();

View File

@ -49,6 +49,11 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl
protected boolean isInitialized;
@Override
public FetchParentAccess getFetchParentAccess() {
return parentAccess;
}
protected Object entityInstance;
public EntitySelectFetchInitializer(