HHH-9055 - Binding support for IdClass and MapsId needs a complete review
This commit is contained in:
parent
bd7605da01
commit
7e5b0c4aa5
|
@ -23,22 +23,16 @@
|
|||
*/
|
||||
package org.hibernate.metamodel.internal.binder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.id.EntityIdentifierNature;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.metamodel.source.spi.AggregatedCompositeIdentifierSource;
|
||||
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.IdentifierSource;
|
||||
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.PluralAttributeIndexSourceResolver;
|
||||
import org.hibernate.metamodel.source.spi.PluralAttributeSource;
|
||||
|
@ -74,10 +67,7 @@ import org.jboss.logging.Logger;
|
|||
* Indexing the entities in each hierarchy : {@link #indexHierarchy}
|
||||
* </li>
|
||||
* <li>
|
||||
* Accessing all hierarchies:<ul>
|
||||
* <li>{@link #getAllHierarchySources()}</li>
|
||||
* <li>{@link #getIdDependencyOrderedHierarchySources()}</li>
|
||||
* </ul>
|
||||
* Accessing all hierarchies: {@link #getAllHierarchySources()}
|
||||
* </li>
|
||||
* <li>
|
||||
* Accessing the attributes in various ways:<ul>
|
||||
|
@ -136,8 +126,7 @@ public class SourceIndex {
|
|||
/**
|
||||
* Obtains source information about all entity hierarchies.
|
||||
* <p/>
|
||||
* Note that a Collection is returned because no order is undefined;
|
||||
* see {@link #getIdDependencyOrderedHierarchySources()}
|
||||
* Note that a Collection is returned because no order is defined.
|
||||
*
|
||||
* @return Source information about all entity hierarchies.
|
||||
*/
|
||||
|
@ -145,186 +134,6 @@ public class SourceIndex {
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue