HHH-8056 : Create EntityHierarchyHelper to handle processing an EntityHierarchy

This commit is contained in:
Gail Badner 2013-05-21 16:08:54 -07:00
parent 9a02c9e52d
commit c0fa63579b
6 changed files with 83 additions and 18 deletions

View File

@ -227,6 +227,9 @@ public class Binder {
/** /**
* The entry point of {@linkplain Binder} class, adds all the entity hierarchy one by one. * The entry point of {@linkplain Binder} class, adds all the entity hierarchy one by one.
* *
* Indexes all {@link EntitySource} objects in an {@link EntityHierarchy} and
* creates all {@link EntityBinding}.
*
* @param entityHierarchies The entity hierarchies resolved from mappings * @param entityHierarchies The entity hierarchies resolved from mappings
*/ */
public void addEntityHierarchies(final Iterable<EntityHierarchy> entityHierarchies) { public void addEntityHierarchies(final Iterable<EntityHierarchy> entityHierarchies) {
@ -250,31 +253,45 @@ public class Binder {
} }
public void bindEntityHierarchies() { public void bindEntityHierarchies() {
// Bind everything except for (non-ID) attributes.
// Need to bind ID attributes before resolving associations.
// TODO: when we know the proper order for processing entity hierarchies,
// then applyToAllEntityHierarchies(...) can replace the following method.
bindEntityHierarchiesExcludingNonIdAttributeBindings(); bindEntityHierarchiesExcludingNonIdAttributeBindings();
// cannot resolve associations until after entity identifiers are defined. // Resolve associations:
// - determine if JPA @OneToOne translates to Hibernate's one-to-one or unique many-to-one;
// - determine if JPA @OneToMany translates to Hibernate's one-to-many or unique many-to-many.
applyToAllEntityHierarchies( resolveAssociationSourcesExecutor() ); applyToAllEntityHierarchies( resolveAssociationSourcesExecutor() );
// At this point, SourceIndex has all necessary information.
// Bind all composite attribute containers. This excludes composite sub-attributes.
applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.COMPOSITE ) ); applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.COMPOSITE ) );
// bind singular attributes // bind basic singular attributes, including composite sub-attributes that are basic.
applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.BASIC ) ); applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.BASIC ) );
// do many-to-one before one-to-one
// many-to-one needs to be bound before one-to-one (um, can't remember why).
// bind many-to-one attributes, including composite sub-attributes that are many-to-one.
applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.MANY_TO_ONE ) ); applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.MANY_TO_ONE ) );
// bind one-to-one attributes, including composite sub-attributes that are one-to-one.
applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.ONE_TO_ONE ) ); applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.ONE_TO_ONE ) );
// bind plural attributes (non-mappedBy first). // bind plural attributes (non-mappedBy first), including composite sub-attributes that are plural
applyToAllEntityHierarchies( bindPluralAttributesExecutor( false ) ); applyToAllEntityHierarchies( bindPluralAttributesExecutor( false ) );
applyToAllEntityHierarchies( bindPluralAttributesExecutor( true ) ); applyToAllEntityHierarchies( bindPluralAttributesExecutor( true ) );
// Bind unique constraints after all attributes have been bound. // Bind unique constraints after all attributes have been bound and the
// TODO: Add impl note about why... // columns used by attributes is already determined.
applyToAllEntityHierarchies( bindUniqueConstraintsExecutor() ); applyToAllEntityHierarchies( bindUniqueConstraintsExecutor() );
// TODO: check if any many-to-one attribute bindings with logicalOneToOne == false have all columns // TODO: check if any many-to-one attribute bindings with logicalOneToOne == false have all columns
// (and no formulas) contained in a defined unique key that only contains these columns. // (and no formulas) contained in a defined unique key that only contains these columns.
// if so, mark the many-to-one as a logical one-to-one. // if so, mark the many-to-one as a logical one-to-one.
// TODO: when does this have to be done.
} }
private InheritanceType inheritanceType() { private InheritanceType inheritanceType() {
@ -336,6 +353,10 @@ public class Binder {
bindSecondaryTables( entityBinding, entitySource ); bindSecondaryTables( entityBinding, entitySource );
} }
}; };
// TODO: need to determine the proper order for processing EntityHierarchy objects
// so that dependent EntityHierarchy is processed after the EntityHierarchy it
// is dependent on.
// For now, just delay processing the dependent entity hierarchies.
Set<EntityHierarchy> unresolvedEntityHierarchies = new HashSet<EntityHierarchy>( ); Set<EntityHierarchy> unresolvedEntityHierarchies = new HashSet<EntityHierarchy>( );
for ( final EntityHierarchy entityHierarchy : entityHierarchiesByRootEntityName.values() ) { for ( final EntityHierarchy entityHierarchy : entityHierarchiesByRootEntityName.values() ) {
if ( isIdentifierDependentOnOtherEntityHierarchy( entityHierarchy ) ) { if ( isIdentifierDependentOnOtherEntityHierarchy( entityHierarchy ) ) {
@ -346,6 +367,11 @@ public class Binder {
} }
} }
// The following is to try to resolve any dependent entity hierarchies.
// It runs through all the dependent entity hierarchies and resolves what it can.
// This process repeats until no more can be resolved.
// TODO: this will not be necessary once we know the proper order for
// processing entity hierarchies.
int oldSize = Integer.MAX_VALUE; int oldSize = Integer.MAX_VALUE;
while( !unresolvedEntityHierarchies.isEmpty() && unresolvedEntityHierarchies.size() < oldSize ) { while( !unresolvedEntityHierarchies.isEmpty() && unresolvedEntityHierarchies.size() < oldSize ) {
oldSize = unresolvedEntityHierarchies.size(); oldSize = unresolvedEntityHierarchies.size();
@ -361,11 +387,14 @@ public class Binder {
} }
} }
} }
// If any entity hierarchies cannot be resolved, then throw exception.
if ( ! unresolvedEntityHierarchies.isEmpty() ) { if ( ! unresolvedEntityHierarchies.isEmpty() ) {
throw new IllegalStateException( "could not resolve all EntityHierarchies." ); throw new IllegalStateException( "could not resolve all EntityHierarchies." );
} }
} }
// TODO: this will not be necessary once we know the proper order for
// processing entity hierarchies.
private boolean isIdentifierDependentOnOtherEntityHierarchy(EntityHierarchy entityHierarchy) { private boolean isIdentifierDependentOnOtherEntityHierarchy(EntityHierarchy entityHierarchy) {
final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource(); final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource();
final IdentifierSource identifierSource = rootEntitySource.getIdentifierSource(); final IdentifierSource identifierSource = rootEntitySource.getIdentifierSource();
@ -381,6 +410,8 @@ public class Binder {
} }
} }
// TODO: this will not be necessary once we know the proper order for
// processing entity hierarchies.
private boolean containsSingularAssociation(List<? extends AttributeSource> subAttributeSources) { private boolean containsSingularAssociation(List<? extends AttributeSource> subAttributeSources) {
for ( AttributeSource attributeSource : subAttributeSources ) { for ( AttributeSource attributeSource : subAttributeSources ) {
SingularAttributeSource singularAttributeSource = (SingularAttributeSource) attributeSource; SingularAttributeSource singularAttributeSource = (SingularAttributeSource) attributeSource;
@ -424,9 +455,11 @@ public class Binder {
}; };
} }
// TODO: create separate methods that are more clear for the cases.
private void bindSingularAttributes( private void bindSingularAttributes(
final EntityBinding entityBinding, final EntityBinding entityBinding,
final SingularAttributeSource.Nature nature) { final SingularAttributeSource.Nature nature) {
// Get the map of all attributes for the entity binding of the specified nature.
Map<SourceIndex.AttributeSourceKey, SingularAttributeSource> map = sourceIndex.getSingularAttributeSources( Map<SourceIndex.AttributeSourceKey, SingularAttributeSource> map = sourceIndex.getSingularAttributeSources(
entityBinding.getEntityName(), entityBinding.getEntityName(),
nature nature
@ -437,6 +470,7 @@ public class Binder {
final AttributeBindingContainer attributeBindingContainer = final AttributeBindingContainer attributeBindingContainer =
locateAttributeBindingContainer( entityBinding, attributeSourceKey.containerPath() ); locateAttributeBindingContainer( entityBinding, attributeSourceKey.containerPath() );
if ( nature == SingularAttributeSource.Nature.COMPOSITE ) { if ( nature == SingularAttributeSource.Nature.COMPOSITE ) {
// This only creates the composite attribute container.
createAggregatedCompositeAttribute( createAggregatedCompositeAttribute(
attributeBindingContainer, attributeBindingContainer,
(ComponentAttributeSource) attributeSource, (ComponentAttributeSource) attributeSource,
@ -457,35 +491,45 @@ public class Binder {
} }
} }
else { else {
// The container is the EntityBinding itself.
bindAttribute( attributeBindingContainer, attributeSource ); bindAttribute( attributeBindingContainer, attributeSource );
} }
} }
} }
// All sub-attributes must be bound before it's type and ComponentMetamodel can be determined.
private void completeCompositeAttributeBindingIfPossible( private void completeCompositeAttributeBindingIfPossible(
CompositeAttributeBinding compositeAttributeBinding, CompositeAttributeBinding compositeAttributeBinding,
ComponentAttributeSource compositeAttributeSource ComponentAttributeSource compositeAttributeSource
) { ) {
// Find out the number of sub-attributes, excluding the parent attribute.
final int nAttributeSourcesExcludingParent = final int nAttributeSourcesExcludingParent =
compositeAttributeBinding.getParentReference() != null ? compositeAttributeBinding.getParentReference() != null ?
compositeAttributeSource.attributeSources().size() - 1 : compositeAttributeSource.attributeSources().size() - 1 :
compositeAttributeSource.attributeSources().size(); compositeAttributeSource.attributeSources().size();
if ( compositeAttributeBinding.attributeBindingSpan() == nAttributeSourcesExcludingParent ) { if ( compositeAttributeBinding.attributeBindingSpan() == nAttributeSourcesExcludingParent ) {
// All sub-attribute bindings are present; now check if all sub-attributes have
// their type resolved.
boolean allResolved = true; boolean allResolved = true;
for ( AttributeBinding attributeBinding : compositeAttributeBinding.attributeBindings() ) { for ( AttributeBinding attributeBinding : compositeAttributeBinding.attributeBindings() ) {
if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() == null ) { if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() == null ) {
// Something is not resolved.
allResolved = false; allResolved = false;
break; break;
} }
} }
if ( allResolved ) { if ( allResolved ) {
// All are resolved, so we can bind the type.
typeHelper.bindAggregatedCompositeAttributeType( typeHelper.bindAggregatedCompositeAttributeType(
false, false,
(Aggregate) compositeAttributeBinding.getAttribute().getSingularAttributeType(), (Aggregate) compositeAttributeBinding.getAttribute().getSingularAttributeType(),
null, // TODO: don't have the default value at this point; shouldn't be needed... null, // TODO: don't have the default value at this point; shouldn't be needed...
compositeAttributeBinding compositeAttributeBinding
); );
// Now check the container.
if ( compositeAttributeBinding.getContainer() instanceof CompositeAttributeBindingContainer ) { if ( compositeAttributeBinding.getContainer() instanceof CompositeAttributeBindingContainer ) {
// The container is also a CompositeAttributeBindingContainer.
// We need this process for the container.
final CompositeAttributeBinding parentCompositeAttributeBinding = final CompositeAttributeBinding parentCompositeAttributeBinding =
(CompositeAttributeBinding) compositeAttributeBinding.seekEntityBinding().locateAttributeBinding( (CompositeAttributeBinding) compositeAttributeBinding.seekEntityBinding().locateAttributeBinding(
( compositeAttributeBinding.getContainer() ).getPathBase() ( compositeAttributeBinding.getContainer() ).getPathBase()
@ -517,10 +561,12 @@ public class Binder {
}; };
} }
// TODO: may want bind plural attributes of a particular element nature.
private void bindPluralAttributes( private void bindPluralAttributes(
final EntityBinding entityBinding, final EntityBinding entityBinding,
final EntitySource entitySource, final EntitySource entitySource,
final boolean isInverse) { final boolean isInverse) {
// Get the map for inverse or non-inverse (as specified) plural attributes
Map<SourceIndex.AttributeSourceKey, PluralAttributeSource> map = sourceIndex.getPluralAttributeSources( Map<SourceIndex.AttributeSourceKey, PluralAttributeSource> map = sourceIndex.getPluralAttributeSources(
entityBinding.getEntityName(), entityBinding.getEntityName(),
isInverse isInverse
@ -528,16 +574,19 @@ public class Binder {
for ( Map.Entry<SourceIndex.AttributeSourceKey, PluralAttributeSource> entry : map.entrySet() ){ for ( Map.Entry<SourceIndex.AttributeSourceKey, PluralAttributeSource> entry : map.entrySet() ){
final SourceIndex.AttributeSourceKey attributeSourceKey = entry.getKey(); final SourceIndex.AttributeSourceKey attributeSourceKey = entry.getKey();
final PluralAttributeSource attributeSource = entry.getValue(); final PluralAttributeSource attributeSource = entry.getValue();
// Bind the attribute into the appropriate container.
final AttributeBindingContainer attributeBindingContainer = final AttributeBindingContainer attributeBindingContainer =
locateAttributeBindingContainer( entityBinding, attributeSourceKey.containerPath() ); locateAttributeBindingContainer( entityBinding, attributeSourceKey.containerPath() );
bindAttribute( attributeBindingContainer, attributeSource ); bindAttribute( attributeBindingContainer, attributeSource );
if ( attributeBindingContainer instanceof CompositeAttributeBinding ) { if ( attributeBindingContainer instanceof CompositeAttributeBinding ) {
// We just bound a sub-attribute into a CompositeAttributeBinding.
final CompositeAttributeBinding compositeAttributeBinding = (CompositeAttributeBinding) attributeBindingContainer; final CompositeAttributeBinding compositeAttributeBinding = (CompositeAttributeBinding) attributeBindingContainer;
final ComponentAttributeSource compositeAttributeSource = final ComponentAttributeSource compositeAttributeSource =
(ComponentAttributeSource) sourceIndex.attributeSource( (ComponentAttributeSource) sourceIndex.attributeSource(
entityBinding.getEntityName(), entityBinding.getEntityName(),
compositeAttributeBinding.getPathBase() compositeAttributeBinding.getPathBase()
); );
// Resolve the type if types are resolved for all sub-attributes now.
completeCompositeAttributeBindingIfPossible( compositeAttributeBinding, compositeAttributeSource ); completeCompositeAttributeBindingIfPossible( compositeAttributeBinding, compositeAttributeSource );
} }
} }

View File

@ -70,6 +70,10 @@ public class EntityHierarchyHelper {
* Apply executors to a single entity hierarchy. * Apply executors to a single entity hierarchy.
* *
* @param entityHierarchy The entity hierarchy to be binded. * @param entityHierarchy The entity hierarchy to be binded.
* @param rootEntityExecutor The executor to be applied to the root {@link EntitySource}
* in the entity hierarchy.
* @param subEntityExecutor The executer to be applied to each {@link SubclassEntitySource}
* in the entity hierarchy.
*/ */
public void applyToEntityHierarchy( public void applyToEntityHierarchy(
final EntityHierarchy entityHierarchy, final EntityHierarchy entityHierarchy,

View File

@ -473,6 +473,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
private void processMappings(MetadataSourceProcessor[] metadataSourceProcessors) { private void processMappings(MetadataSourceProcessor[] metadataSourceProcessors) {
final Binder binder = new Binder( this, identifierGeneratorFactory ); final Binder binder = new Binder( this, identifierGeneratorFactory );
// Add all hierarchies first, before binding.
for ( MetadataSourceProcessor processor : metadataSourceProcessors ) { for ( MetadataSourceProcessor processor : metadataSourceProcessors ) {
binder.addEntityHierarchies( processor.extractEntityHierarchies() ); binder.addEntityHierarchies( processor.extractEntityHierarchies() );
} }

View File

@ -63,7 +63,7 @@ public class SourceIndex {
private final Map<String, EntitySourceIndex> entitySourceIndexByEntityName = new HashMap<String, EntitySourceIndex>(); private final Map<String, EntitySourceIndex> entitySourceIndexByEntityName = new HashMap<String, EntitySourceIndex>();
private final Map<AttributeSourceKey, AttributeSource> attributeSourcesByKey = new HashMap<AttributeSourceKey, AttributeSource>(); private final Map<AttributeSourceKey, AttributeSource> attributeSourcesByKey = new HashMap<AttributeSourceKey, AttributeSource>();
private final Map<AttributeSourceKey, AttributeSourceKey> mappedByAttributeNamesByOwnerAttributeNames = private final Map<AttributeSourceKey, AttributeSourceKey> mappedByAttributeKeysByOwnerAttributeKeys =
new HashMap<AttributeSourceKey, AttributeSourceKey>(); new HashMap<AttributeSourceKey, AttributeSourceKey>();
public void indexEntitySource(final EntitySource entitySource) { public void indexEntitySource(final EntitySource entitySource) {
@ -98,7 +98,7 @@ public class SourceIndex {
public AttributeSource locateAttributeSourceOwnedBy(final String entityName, final String attributePath) { public AttributeSource locateAttributeSourceOwnedBy(final String entityName, final String attributePath) {
AttributeSourceKey ownerKey = new AttributeSourceKey( entityName, attributePath ); AttributeSourceKey ownerKey = new AttributeSourceKey( entityName, attributePath );
AttributeSourceKey mappedByKey = mappedByAttributeNamesByOwnerAttributeNames.get( ownerKey ); AttributeSourceKey mappedByKey = mappedByAttributeKeysByOwnerAttributeKeys.get( ownerKey );
return mappedByKey == null ? null : attributeSourcesByKey.get( mappedByKey ); return mappedByKey == null ? null : attributeSourcesByKey.get( mappedByKey );
} }
@ -173,7 +173,7 @@ public class SourceIndex {
} }
void addMappedByAssociationByOwnerAssociation(AttributeSourceKey ownerKey, AttributeSourceKey ownedKey) { void addMappedByAssociationByOwnerAssociation(AttributeSourceKey ownerKey, AttributeSourceKey ownedKey) {
mappedByAttributeNamesByOwnerAttributeNames.put( mappedByAttributeKeysByOwnerAttributeKeys.put(
ownerKey, ownerKey,
ownedKey ownedKey
); );
@ -270,10 +270,12 @@ public class SourceIndex {
private final EntitySource entitySource; private final EntitySource entitySource;
private final Map<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>> identifierAttributeSourcesByNature = private final Map<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>> identifierAttributeSourcesByNature =
new HashMap<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>>(); new HashMap<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>>();
// TODO: split out inverse and non-inverse SingularAttributeSource maps.
private final Map<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>> singularAttributeSourcesByNature = private final Map<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>> singularAttributeSourcesByNature =
new HashMap<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>>(); new HashMap<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>>();
// TODO: the following should not need to be LinkedHashMap, but it appears that some unit tests // TODO: the following should not need to be LinkedHashMap, but it appears that some unit tests
// depend on the ordering // depend on the ordering
// TODO: rework nonInversePluralAttributeSourcesByKey and inversePluralAttributeSourcesByKey
private final Map<AttributeSourceKey, PluralAttributeSource> nonInversePluralAttributeSourcesByKey = private final Map<AttributeSourceKey, PluralAttributeSource> nonInversePluralAttributeSourcesByKey =
new LinkedHashMap<AttributeSourceKey, PluralAttributeSource>(); new LinkedHashMap<AttributeSourceKey, PluralAttributeSource>();
private final Map<AttributeSourceKey, PluralAttributeSource> inversePluralAttributeSourcesByKey = private final Map<AttributeSourceKey, PluralAttributeSource> inversePluralAttributeSourcesByKey =
@ -364,16 +366,21 @@ public class SourceIndex {
} }
private void resolveAssociationSources(final EntityBinding entityBinding) { private void resolveAssociationSources(final EntityBinding entityBinding) {
// Cycle through non-inverse plural attributes.
for ( Map.Entry<AttributeSourceKey,PluralAttributeSource> entry : inversePluralAttributeSourcesByKey.entrySet() ) { for ( Map.Entry<AttributeSourceKey,PluralAttributeSource> entry : inversePluralAttributeSourcesByKey.entrySet() ) {
final AttributeSourceKey pluralAttributeSourceKey = entry.getKey(); final AttributeSourceKey pluralAttributeSourceKey = entry.getKey();
final PluralAttributeSource pluralAttributeSource = entry.getValue(); final PluralAttributeSource pluralAttributeSource = entry.getValue();
if ( pluralAttributeSource.getMappedBy() != null ) { if ( pluralAttributeSource.getMappedBy() != null ) {
// This plural attribute is mappedBy the opposite side of the association,
// so it needs to be resolved.
// TODO: this should really just resolve PluralAttributeElementSource.Nature
pluralAttributeSource.resolvePluralAttributeElementSource( pluralAttributeSource.resolvePluralAttributeElementSource(
new PluralAttributeElementSourceResolver.PluralAttributeElementSourceResolutionContext() { new PluralAttributeElementSourceResolver.PluralAttributeElementSourceResolutionContext() {
@Override @Override
public AttributeSource resolveAttributeSource(String referencedEntityName, String mappedBy) { public AttributeSource resolveAttributeSource(String referencedEntityName, String mappedBy) {
AttributeSourceKey ownerAttributeSourceKey = new AttributeSourceKey( referencedEntityName, mappedBy ); AttributeSourceKey ownerAttributeSourceKey = new AttributeSourceKey( referencedEntityName, mappedBy );
AttributeSource ownerAttributeSource = sourceIndex.attributeSource( referencedEntityName, mappedBy ); AttributeSource ownerAttributeSource = sourceIndex.attributeSource( referencedEntityName, mappedBy );
// TODO: is this needed? if so, make more obvious and rename method.
sourceIndex.addMappedByAssociationByOwnerAssociation( sourceIndex.addMappedByAssociationByOwnerAssociation(
ownerAttributeSourceKey, ownerAttributeSourceKey,
pluralAttributeSourceKey pluralAttributeSourceKey
@ -387,6 +394,8 @@ public class SourceIndex {
final Map<AttributeSourceKey,SingularAttributeSource> unresolvedSingularAttributeSourceMap = final Map<AttributeSourceKey,SingularAttributeSource> unresolvedSingularAttributeSourceMap =
singularAttributeSourcesByNature.get( null ); singularAttributeSourcesByNature.get( null );
if ( unresolvedSingularAttributeSourceMap != null ) { if ( unresolvedSingularAttributeSourceMap != null ) {
// cycle through unresolved SingularAttributeSource.
// TODO: rework so approach is similar to one-to-many/many-to-many resolution.
for ( Iterator<Map.Entry<AttributeSourceKey,SingularAttributeSource>> it = unresolvedSingularAttributeSourceMap.entrySet().iterator(); it.hasNext(); ) { for ( Iterator<Map.Entry<AttributeSourceKey,SingularAttributeSource>> it = unresolvedSingularAttributeSourceMap.entrySet().iterator(); it.hasNext(); ) {
final Map.Entry<AttributeSourceKey,SingularAttributeSource> entry = it.next(); final Map.Entry<AttributeSourceKey,SingularAttributeSource> entry = it.next();
final AttributeSourceKey attributeSourceKey = entry.getKey(); final AttributeSourceKey attributeSourceKey = entry.getKey();

View File

@ -79,7 +79,7 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab
this.nature = associationAttribute.getPluralAttributeNature(); this.nature = associationAttribute.getPluralAttributeNature();
if ( associationAttribute.getMappedBy() == null ) { if ( associationAttribute.getMappedBy() == null ) {
this.ownerAttributeSource = this; this.ownerAttributeSource = this;
this.elementSource = determineOwnerElementSource( this, associationAttribute, entityClass ); this.elementSource = determineElementSource( this, associationAttribute, entityClass );
} }
this.filterSources = determineFilterSources(associationAttribute); this.filterSources = determineFilterSources(associationAttribute);
} }
@ -166,7 +166,7 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab
} }
} }
private static PluralAttributeElementSource determineOwnerElementSource( private static PluralAttributeElementSource determineElementSource(
AttributeSource ownerAttributeSource, AttributeSource ownerAttributeSource,
PluralAssociationAttribute associationAttribute, PluralAssociationAttribute associationAttribute,
ConfiguredClass entityClass) { ConfiguredClass entityClass) {
@ -353,17 +353,18 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab
@Override @Override
public PluralAttributeElementSource resolvePluralAttributeElementSource( public PluralAttributeElementSource resolvePluralAttributeElementSource(
PluralAttributeElementSourceResolutionContext context) { PluralAttributeElementSourceResolutionContext context) {
if ( associationAttribute.getMappedBy() == null ) { if ( elementSource == null ) {
return elementSource; // elementSource has not been initialized, so we need to resolve it using the
} // association owner.
else { // Get the owner attribute source that maps the opposite side of the association.
ownerAttributeSource = context.resolveAttributeSource( ownerAttributeSource = context.resolveAttributeSource(
associationAttribute.getReferencedEntityType(), associationAttribute.getReferencedEntityType(),
associationAttribute.getMappedBy() associationAttribute.getMappedBy()
); );
elementSource = determineOwnerElementSource( ownerAttributeSource, associationAttribute, entityClass ); // Initialize resolved entitySource.
return elementSource; elementSource = determineElementSource( ownerAttributeSource, associationAttribute, entityClass );
} }
return elementSource;
} }
} }

View File

@ -165,6 +165,7 @@ public abstract class AbstractPluralAttributeSourceImpl
@Override @Override
public PluralAttributeElementSource resolvePluralAttributeElementSource(PluralAttributeElementSourceResolutionContext context) { public PluralAttributeElementSource resolvePluralAttributeElementSource(PluralAttributeElementSourceResolutionContext context) {
// elementSource is already resolved; nothing to do.
return elementSource; return elementSource;
} }