HHH-9055 - Binding support for IdClass and MapsId needs a complete review

This commit is contained in:
Steve Ebersole 2014-04-24 15:35:26 -05:00
parent bd7605da01
commit 7e5b0c4aa5
1 changed files with 2 additions and 193 deletions

View File

@ -23,22 +23,16 @@
*/ */
package org.hibernate.metamodel.internal.binder; package org.hibernate.metamodel.internal.binder;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.id.EntityIdentifierNature;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.metamodel.source.spi.AggregatedCompositeIdentifierSource; import org.hibernate.metamodel.source.spi.AggregatedCompositeIdentifierSource;
import org.hibernate.metamodel.source.spi.AttributeSource; import org.hibernate.metamodel.source.spi.AttributeSource;
@ -49,7 +43,6 @@ import org.hibernate.metamodel.source.spi.EntitySource;
import org.hibernate.metamodel.source.spi.IdentifiableTypeSource; import org.hibernate.metamodel.source.spi.IdentifiableTypeSource;
import org.hibernate.metamodel.source.spi.IdentifierSource; import org.hibernate.metamodel.source.spi.IdentifierSource;
import org.hibernate.metamodel.source.spi.IndexedPluralAttributeSource; import org.hibernate.metamodel.source.spi.IndexedPluralAttributeSource;
import org.hibernate.metamodel.source.spi.MapsIdSource;
import org.hibernate.metamodel.source.spi.NonAggregatedCompositeIdentifierSource; import org.hibernate.metamodel.source.spi.NonAggregatedCompositeIdentifierSource;
import org.hibernate.metamodel.source.spi.PluralAttributeIndexSourceResolver; import org.hibernate.metamodel.source.spi.PluralAttributeIndexSourceResolver;
import org.hibernate.metamodel.source.spi.PluralAttributeSource; import org.hibernate.metamodel.source.spi.PluralAttributeSource;
@ -74,10 +67,7 @@ import org.jboss.logging.Logger;
* Indexing the entities in each hierarchy : {@link #indexHierarchy} * Indexing the entities in each hierarchy : {@link #indexHierarchy}
* </li> * </li>
* <li> * <li>
* Accessing all hierarchies:<ul> * Accessing all hierarchies: {@link #getAllHierarchySources()}
* <li>{@link #getAllHierarchySources()}</li>
* <li>{@link #getIdDependencyOrderedHierarchySources()}</li>
* </ul>
* </li> * </li>
* <li> * <li>
* Accessing the attributes in various ways:<ul> * Accessing the attributes in various ways:<ul>
@ -136,8 +126,7 @@ public class SourceIndex {
/** /**
* Obtains source information about all entity hierarchies. * Obtains source information about all entity hierarchies.
* <p/> * <p/>
* Note that a Collection is returned because no order is undefined; * Note that a Collection is returned because no order is defined.
* see {@link #getIdDependencyOrderedHierarchySources()}
* *
* @return Source information about all entity hierarchies. * @return Source information about all entity hierarchies.
*/ */
@ -145,186 +134,6 @@ public class SourceIndex {
return entityHierarchiesByRootEntityName.values(); return entityHierarchiesByRootEntityName.values();
} }
/**
* Used to hold the essential ordering information for a hierarchy
*/
public static class HierarchyByIdDependencyGraphNode {
private final String hierarchyKey;
private final EntityIdentifierNature nature;
private boolean containsUnknownTarget = false;
private final Set<String> targets;
public HierarchyByIdDependencyGraphNode(String hierarchyKey, EntityIdentifierNature nature) {
this.hierarchyKey = hierarchyKey;
this.nature = nature;
this.targets = new HashSet<String>();
}
public void toggleUnknownTarget() {
// todo : how to best utilize containsUnknownTarget in sorting?
containsUnknownTarget = true;
}
}
/**
* Comparator of HierarchyByIdDependencyGraphNode instances used to help
* in ordering the hierarchies.
*/
public static class HierarchyByIdDependencyGraphNodeComparator
implements Comparator<HierarchyByIdDependencyGraphNode> {
@Override
public int compare(
HierarchyByIdDependencyGraphNode o1,
HierarchyByIdDependencyGraphNode o2) {
if ( o1.hierarchyKey.equals( o2.hierarchyKey ) ) {
return 0;
}
final boolean o1HasDeps = !o1.targets.isEmpty();
final boolean o2HasDeps = !o2.targets.isEmpty();
if ( !o1HasDeps ) {
// o1 has no id dependencies,
if ( o2HasDeps ) {
// but o2 does: o1 comes BEFORE o2
return -1;
}
else {
// neither has id dependencies
return o1.nature == EntityIdentifierNature.SIMPLE
? -1
: 1;
}
}
// to get here, we know the o1 hierarchy has one or more id dependencies
if ( !o2HasDeps ) {
// but, o2 did not : 01 comes AFTER o2
return 1;
}
if ( o2.targets.contains( o1.hierarchyKey ) ) {
// o2 "depends on" o1 : o1 needs to come BEFORE 02
return -1;
}
else {
// otherwise : put o1 AFTER o2
return 1;
}
}
}
/**
* Obtains source information about all entity hierarchies. Attempts a best
* effort to order the hierarchy according to identifier dependencies.
*
* @return Source information about all entity hierarchies, ordered.
*
* @return The ordered hierarchy source information.
*/
public List<EntityHierarchySource> getIdDependencyOrderedHierarchySources() {
// it is important that this be called only after all hierarchies have been
// applied and indexed!! Duh :)
//
// Also, this is likely an expensive operation and it is best it be
// called just once (like while processing identifiers).
final TreeSet<HierarchyByIdDependencyGraphNode> nodeSet =
new TreeSet<HierarchyByIdDependencyGraphNode>( new HierarchyByIdDependencyGraphNodeComparator() );
for ( Map.Entry<String, EntityHierarchySource> hierarchySourceEntry :
entityHierarchiesByRootEntityName.entrySet() ) {
final HierarchyByIdDependencyGraphNode node = new HierarchyByIdDependencyGraphNode(
hierarchySourceEntry.getKey(),
hierarchySourceEntry.getValue().getIdentifierSource().getNature()
);
collectRootEntityNamesOnWhichIdDepends( hierarchySourceEntry.getValue(), node );
nodeSet.add( node );
}
final List<EntityHierarchySource> rtn = new ArrayList<EntityHierarchySource>();
for ( HierarchyByIdDependencyGraphNode node : nodeSet ) {
rtn.add( entityHierarchiesByRootEntityName.get( node.hierarchyKey ) );
}
return rtn;
}
private void collectRootEntityNamesOnWhichIdDepends(
EntityHierarchySource entityHierarchySource,
HierarchyByIdDependencyGraphNode node) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// First, identifier attributes (key-many-to-one)
//
// id attribute(s) can only be basic or to-one types.. assume the source producer
// handle at least that properly
if ( node.nature == EntityIdentifierNature.SIMPLE ) {
final SimpleIdentifierSource identifierSource = (SimpleIdentifierSource) entityHierarchySource.getIdentifierSource();
if ( identifierSource.getIdentifierAttributeSource().getSingularAttributeNature() == SingularAttributeNature.BASIC ) {
return;
}
addDependency( node, (ToOneAttributeSource) identifierSource.getIdentifierAttributeSource() );
}
else if ( node.nature == EntityIdentifierNature.AGGREGATED_COMPOSITE ) {
final AggregatedCompositeIdentifierSource identifierSource =
(AggregatedCompositeIdentifierSource) entityHierarchySource.getIdentifierSource();
for ( AttributeSource attributeSource : identifierSource.getIdentifierAttributeSource()
.getEmbeddableSource()
.attributeSources() ) {
final SingularAttributeSource sAttSource = (SingularAttributeSource) attributeSource;
if ( sAttSource.getSingularAttributeNature() == SingularAttributeNature.BASIC ) {
continue;
}
addDependency( node, (ToOneAttributeSource) sAttSource );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Then @MapsId + to-one
//
// We make a natural assumption that persistent subclasses cannot (re)define @MapsId...
for ( MapsIdSource mapsIdSource : identifierSource.getMapsIdSources() ) {
final ToOneAttributeSource toOneAttributeSource = mapsIdSource.getAssociationAttributeSource();
addDependency( node, toOneAttributeSource );
}
}
else if ( node.nature == EntityIdentifierNature.NON_AGGREGATED_COMPOSITE ) {
final NonAggregatedCompositeIdentifierSource identifierSource =
(NonAggregatedCompositeIdentifierSource) entityHierarchySource.getIdentifierSource();
for ( SingularAttributeSource attributeSource : identifierSource.getAttributeSourcesMakingUpIdentifier() ) {
if ( attributeSource.getSingularAttributeNature() == SingularAttributeNature.BASIC ) {
continue;
}
addDependency( node, (ToOneAttributeSource) attributeSource );
}
}
}
private void addDependency(
HierarchyByIdDependencyGraphNode node,
ToOneAttributeSource attributeSource) {
final String entityName = attributeSource.getReferencedEntityName();
if ( entityName == null ) {
// best effort.. just return
node.toggleUnknownTarget();
return;
}
final String rootEntityName = entitySourceIndexByEntityName.get( entityName )
.entitySource
.getHierarchy()
.getRoot()
.getEntityName();
node.targets.add( rootEntityName );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Accessing attributes // Accessing attributes