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;
|
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
|
||||||
|
|
Loading…
Reference in New Issue