From c0fa63579b87e0f6ff1251da1e9d42425e340e42 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 21 May 2013 16:08:54 -0700 Subject: [PATCH] HHH-8056 : Create EntityHierarchyHelper to handle processing an EntityHierarchy --- .../hibernate/metamodel/internal/Binder.java | 63 ++++++++++++++++--- .../internal/EntityHierarchyHelper.java | 4 ++ .../metamodel/internal/MetadataImpl.java | 1 + .../metamodel/internal/SourceIndex.java | 15 ++++- .../PluralAttributeSourceImpl.java | 17 ++--- .../AbstractPluralAttributeSourceImpl.java | 1 + 6 files changed, 83 insertions(+), 18 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java index d077a358a2..cd0878c2dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java @@ -227,6 +227,9 @@ public class Binder { /** * 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 */ public void addEntityHierarchies(final Iterable entityHierarchies) { @@ -250,31 +253,45 @@ public class Binder { } 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(); - // 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() ); + // At this point, SourceIndex has all necessary information. + + // Bind all composite attribute containers. This excludes composite sub-attributes. applyToAllEntityHierarchies( bindSingularAttributesExecutor( SingularAttributeSource.Nature.COMPOSITE ) ); - // bind singular attributes + // bind basic singular attributes, including composite sub-attributes that are 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 ) ); + + // bind one-to-one attributes, including composite sub-attributes that are 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( true ) ); - // Bind unique constraints after all attributes have been bound. - // TODO: Add impl note about why... + // Bind unique constraints after all attributes have been bound and the + // columns used by attributes is already determined. applyToAllEntityHierarchies( bindUniqueConstraintsExecutor() ); // 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. // if so, mark the many-to-one as a logical one-to-one. + // TODO: when does this have to be done. } private InheritanceType inheritanceType() { @@ -336,6 +353,10 @@ public class Binder { 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 unresolvedEntityHierarchies = new HashSet( ); for ( final EntityHierarchy entityHierarchy : entityHierarchiesByRootEntityName.values() ) { 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; while( !unresolvedEntityHierarchies.isEmpty() && unresolvedEntityHierarchies.size() < oldSize ) { oldSize = unresolvedEntityHierarchies.size(); @@ -361,11 +387,14 @@ public class Binder { } } } + // If any entity hierarchies cannot be resolved, then throw exception. if ( ! unresolvedEntityHierarchies.isEmpty() ) { 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) { final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource(); 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 subAttributeSources) { for ( AttributeSource attributeSource : subAttributeSources ) { 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( final EntityBinding entityBinding, final SingularAttributeSource.Nature nature) { + // Get the map of all attributes for the entity binding of the specified nature. Map map = sourceIndex.getSingularAttributeSources( entityBinding.getEntityName(), nature @@ -437,6 +470,7 @@ public class Binder { final AttributeBindingContainer attributeBindingContainer = locateAttributeBindingContainer( entityBinding, attributeSourceKey.containerPath() ); if ( nature == SingularAttributeSource.Nature.COMPOSITE ) { + // This only creates the composite attribute container. createAggregatedCompositeAttribute( attributeBindingContainer, (ComponentAttributeSource) attributeSource, @@ -457,35 +491,45 @@ public class Binder { } } else { + // The container is the EntityBinding itself. bindAttribute( attributeBindingContainer, attributeSource ); } } } + // All sub-attributes must be bound before it's type and ComponentMetamodel can be determined. private void completeCompositeAttributeBindingIfPossible( CompositeAttributeBinding compositeAttributeBinding, ComponentAttributeSource compositeAttributeSource ) { + // Find out the number of sub-attributes, excluding the parent attribute. final int nAttributeSourcesExcludingParent = compositeAttributeBinding.getParentReference() != null ? compositeAttributeSource.attributeSources().size() - 1 : compositeAttributeSource.attributeSources().size(); if ( compositeAttributeBinding.attributeBindingSpan() == nAttributeSourcesExcludingParent ) { + // All sub-attribute bindings are present; now check if all sub-attributes have + // their type resolved. boolean allResolved = true; for ( AttributeBinding attributeBinding : compositeAttributeBinding.attributeBindings() ) { if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() == null ) { + // Something is not resolved. allResolved = false; break; } } if ( allResolved ) { + // All are resolved, so we can bind the type. typeHelper.bindAggregatedCompositeAttributeType( false, (Aggregate) compositeAttributeBinding.getAttribute().getSingularAttributeType(), null, // TODO: don't have the default value at this point; shouldn't be needed... compositeAttributeBinding ); + // Now check the container. if ( compositeAttributeBinding.getContainer() instanceof CompositeAttributeBindingContainer ) { + // The container is also a CompositeAttributeBindingContainer. + // We need this process for the container. final CompositeAttributeBinding parentCompositeAttributeBinding = (CompositeAttributeBinding) compositeAttributeBinding.seekEntityBinding().locateAttributeBinding( ( compositeAttributeBinding.getContainer() ).getPathBase() @@ -517,10 +561,12 @@ public class Binder { }; } + // TODO: may want bind plural attributes of a particular element nature. private void bindPluralAttributes( final EntityBinding entityBinding, final EntitySource entitySource, final boolean isInverse) { + // Get the map for inverse or non-inverse (as specified) plural attributes Map map = sourceIndex.getPluralAttributeSources( entityBinding.getEntityName(), isInverse @@ -528,16 +574,19 @@ public class Binder { for ( Map.Entry entry : map.entrySet() ){ final SourceIndex.AttributeSourceKey attributeSourceKey = entry.getKey(); final PluralAttributeSource attributeSource = entry.getValue(); + // Bind the attribute into the appropriate container. final AttributeBindingContainer attributeBindingContainer = locateAttributeBindingContainer( entityBinding, attributeSourceKey.containerPath() ); bindAttribute( attributeBindingContainer, attributeSource ); if ( attributeBindingContainer instanceof CompositeAttributeBinding ) { + // We just bound a sub-attribute into a CompositeAttributeBinding. final CompositeAttributeBinding compositeAttributeBinding = (CompositeAttributeBinding) attributeBindingContainer; final ComponentAttributeSource compositeAttributeSource = (ComponentAttributeSource) sourceIndex.attributeSource( entityBinding.getEntityName(), compositeAttributeBinding.getPathBase() ); + // Resolve the type if types are resolved for all sub-attributes now. completeCompositeAttributeBindingIfPossible( compositeAttributeBinding, compositeAttributeSource ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityHierarchyHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityHierarchyHelper.java index 506f8b5cd1..4c81aeb667 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityHierarchyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityHierarchyHelper.java @@ -70,6 +70,10 @@ public class EntityHierarchyHelper { * Apply executors to a single entity hierarchy. * * @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( final EntityHierarchy entityHierarchy, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java index 95691436cd..53c7434f71 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java @@ -473,6 +473,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable { private void processMappings(MetadataSourceProcessor[] metadataSourceProcessors) { final Binder binder = new Binder( this, identifierGeneratorFactory ); + // Add all hierarchies first, before binding. for ( MetadataSourceProcessor processor : metadataSourceProcessors ) { binder.addEntityHierarchies( processor.extractEntityHierarchies() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/SourceIndex.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/SourceIndex.java index 1979f0ab75..12760e07ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/SourceIndex.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/SourceIndex.java @@ -63,7 +63,7 @@ public class SourceIndex { private final Map entitySourceIndexByEntityName = new HashMap(); private final Map attributeSourcesByKey = new HashMap(); - private final Map mappedByAttributeNamesByOwnerAttributeNames = + private final Map mappedByAttributeKeysByOwnerAttributeKeys = new HashMap(); public void indexEntitySource(final EntitySource entitySource) { @@ -98,7 +98,7 @@ public class SourceIndex { public AttributeSource locateAttributeSourceOwnedBy(final String entityName, final String attributePath) { AttributeSourceKey ownerKey = new AttributeSourceKey( entityName, attributePath ); - AttributeSourceKey mappedByKey = mappedByAttributeNamesByOwnerAttributeNames.get( ownerKey ); + AttributeSourceKey mappedByKey = mappedByAttributeKeysByOwnerAttributeKeys.get( ownerKey ); return mappedByKey == null ? null : attributeSourcesByKey.get( mappedByKey ); } @@ -173,7 +173,7 @@ public class SourceIndex { } void addMappedByAssociationByOwnerAssociation(AttributeSourceKey ownerKey, AttributeSourceKey ownedKey) { - mappedByAttributeNamesByOwnerAttributeNames.put( + mappedByAttributeKeysByOwnerAttributeKeys.put( ownerKey, ownedKey ); @@ -270,10 +270,12 @@ public class SourceIndex { private final EntitySource entitySource; private final Map> identifierAttributeSourcesByNature = new HashMap>(); + // TODO: split out inverse and non-inverse SingularAttributeSource maps. private final Map> singularAttributeSourcesByNature = new HashMap>(); // TODO: the following should not need to be LinkedHashMap, but it appears that some unit tests // depend on the ordering + // TODO: rework nonInversePluralAttributeSourcesByKey and inversePluralAttributeSourcesByKey private final Map nonInversePluralAttributeSourcesByKey = new LinkedHashMap(); private final Map inversePluralAttributeSourcesByKey = @@ -364,16 +366,21 @@ public class SourceIndex { } private void resolveAssociationSources(final EntityBinding entityBinding) { + // Cycle through non-inverse plural attributes. for ( Map.Entry entry : inversePluralAttributeSourcesByKey.entrySet() ) { final AttributeSourceKey pluralAttributeSourceKey = entry.getKey(); final PluralAttributeSource pluralAttributeSource = entry.getValue(); 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( new PluralAttributeElementSourceResolver.PluralAttributeElementSourceResolutionContext() { @Override public AttributeSource resolveAttributeSource(String referencedEntityName, String mappedBy) { AttributeSourceKey ownerAttributeSourceKey = new AttributeSourceKey( referencedEntityName, mappedBy ); AttributeSource ownerAttributeSource = sourceIndex.attributeSource( referencedEntityName, mappedBy ); + // TODO: is this needed? if so, make more obvious and rename method. sourceIndex.addMappedByAssociationByOwnerAssociation( ownerAttributeSourceKey, pluralAttributeSourceKey @@ -387,6 +394,8 @@ public class SourceIndex { final Map unresolvedSingularAttributeSourceMap = singularAttributeSourcesByNature.get( null ); if ( unresolvedSingularAttributeSourceMap != null ) { + // cycle through unresolved SingularAttributeSource. + // TODO: rework so approach is similar to one-to-many/many-to-many resolution. for ( Iterator> it = unresolvedSingularAttributeSourceMap.entrySet().iterator(); it.hasNext(); ) { final Map.Entry entry = it.next(); final AttributeSourceKey attributeSourceKey = entry.getKey(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java index a0a5bc0562..451625cf76 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java @@ -79,7 +79,7 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab this.nature = associationAttribute.getPluralAttributeNature(); if ( associationAttribute.getMappedBy() == null ) { this.ownerAttributeSource = this; - this.elementSource = determineOwnerElementSource( this, associationAttribute, entityClass ); + this.elementSource = determineElementSource( this, associationAttribute, entityClass ); } this.filterSources = determineFilterSources(associationAttribute); } @@ -166,7 +166,7 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab } } - private static PluralAttributeElementSource determineOwnerElementSource( + private static PluralAttributeElementSource determineElementSource( AttributeSource ownerAttributeSource, PluralAssociationAttribute associationAttribute, ConfiguredClass entityClass) { @@ -353,17 +353,18 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab @Override public PluralAttributeElementSource resolvePluralAttributeElementSource( PluralAttributeElementSourceResolutionContext context) { - if ( associationAttribute.getMappedBy() == null ) { - return elementSource; - } - else { + if ( elementSource == null ) { + // elementSource has not been initialized, so we need to resolve it using the + // association owner. + // Get the owner attribute source that maps the opposite side of the association. ownerAttributeSource = context.resolveAttributeSource( associationAttribute.getReferencedEntityType(), associationAttribute.getMappedBy() ); - elementSource = determineOwnerElementSource( ownerAttributeSource, associationAttribute, entityClass ); - return elementSource; + // Initialize resolved entitySource. + elementSource = determineElementSource( ownerAttributeSource, associationAttribute, entityClass ); } + return elementSource; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java index 9ecc38d908..b3d99aa7ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java @@ -165,6 +165,7 @@ public abstract class AbstractPluralAttributeSourceImpl @Override public PluralAttributeElementSource resolvePluralAttributeElementSource(PluralAttributeElementSourceResolutionContext context) { + // elementSource is already resolved; nothing to do. return elementSource; }