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.
*
* 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<EntityHierarchy> 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<EntityHierarchy> unresolvedEntityHierarchies = new HashSet<EntityHierarchy>( );
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<? extends AttributeSource> 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<SourceIndex.AttributeSourceKey, SingularAttributeSource> 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<SourceIndex.AttributeSourceKey, PluralAttributeSource> map = sourceIndex.getPluralAttributeSources(
entityBinding.getEntityName(),
isInverse
@ -528,16 +574,19 @@ public class Binder {
for ( Map.Entry<SourceIndex.AttributeSourceKey, PluralAttributeSource> 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 );
}
}

View File

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

View File

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

View File

@ -63,7 +63,7 @@ public class SourceIndex {
private final Map<String, EntitySourceIndex> entitySourceIndexByEntityName = new HashMap<String, EntitySourceIndex>();
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>();
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<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>> identifierAttributeSourcesByNature =
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 =
new HashMap<SingularAttributeSource.Nature, Map<AttributeSourceKey, SingularAttributeSource>>();
// 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<AttributeSourceKey, PluralAttributeSource> nonInversePluralAttributeSourcesByKey =
new LinkedHashMap<AttributeSourceKey, PluralAttributeSource>();
private final Map<AttributeSourceKey, PluralAttributeSource> inversePluralAttributeSourcesByKey =
@ -364,16 +366,21 @@ public class SourceIndex {
}
private void resolveAssociationSources(final EntityBinding entityBinding) {
// Cycle through non-inverse plural attributes.
for ( Map.Entry<AttributeSourceKey,PluralAttributeSource> 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<AttributeSourceKey,SingularAttributeSource> 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<Map.Entry<AttributeSourceKey,SingularAttributeSource>> it = unresolvedSingularAttributeSourceMap.entrySet().iterator(); it.hasNext(); ) {
final Map.Entry<AttributeSourceKey,SingularAttributeSource> entry = it.next();
final AttributeSourceKey attributeSourceKey = entry.getKey();

View File

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

View File

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