Skip to content

Commit 0eba9c4

Browse files
committed
HHH-18714 add support for inheritance in entity graph
1 parent d702496 commit 0eba9c4

File tree

6 files changed

+591
-33
lines changed

6 files changed

+591
-33
lines changed

hibernate-core/src/main/java/org/hibernate/graph/internal/AbstractGraph.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* @author Steve Ebersole
3434
*/
3535
public abstract class AbstractGraph<J> extends AbstractGraphNode<J> implements GraphImplementor<J> {
36-
private final ManagedDomainType<J> managedType;
36+
protected final ManagedDomainType<J> managedType;
3737
private Map<PersistentAttribute<?,?>, AttributeNodeImplementor<?>> attrNodeMap;
3838

3939
public AbstractGraph(ManagedDomainType<J> managedType, boolean mutable) {
@@ -124,7 +124,12 @@ public AttributeNodeImplementor<?> addAttributeNode(AttributeNodeImplementor<?>
124124
@Override
125125
@SuppressWarnings("unchecked")
126126
public <AJ> AttributeNodeImplementor<AJ> findAttributeNode(String attributeName) {
127-
PersistentAttribute<? super J, ?> attribute = managedType.findAttributeInSuperTypes( attributeName );
127+
PersistentAttribute<?, ?> attribute = managedType.findAttributeInSuperTypes( attributeName );
128+
129+
if ( attribute == null ) {
130+
attribute = managedType.findSubTypesAttribute( attributeName );
131+
}
132+
128133
if ( attribute instanceof SqmPathSource && ( (SqmPathSource<?>) attribute ).isGeneric() ) {
129134
attribute = managedType.findConcreteGenericAttribute( attributeName );
130135
}

hibernate-core/src/main/java/org/hibernate/graph/internal/RootGraphImpl.java

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
*/
77
package org.hibernate.graph.internal;
88

9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.Objects;
12+
913
import org.hibernate.graph.SubGraph;
14+
import org.hibernate.graph.spi.AttributeNodeImplementor;
1015
import org.hibernate.graph.spi.GraphHelper;
1116
import org.hibernate.graph.spi.GraphImplementor;
1217
import org.hibernate.graph.spi.RootGraphImplementor;
1318
import org.hibernate.graph.spi.SubGraphImplementor;
1419
import org.hibernate.metamodel.model.domain.EntityDomainType;
20+
import org.hibernate.metamodel.model.domain.ManagedDomainType;
21+
import org.hibernate.metamodel.model.domain.PersistentAttribute;
22+
import org.hibernate.metamodel.model.domain.internal.DomainModelHelper;
1523

1624
/**
1725
* Implementation of the JPA-defined {@link jakarta.persistence.EntityGraph} interface.
@@ -22,18 +30,22 @@ public class RootGraphImpl<J> extends AbstractGraph<J> implements RootGraphImple
2230

2331
private final String name;
2432

33+
private final Map<ManagedDomainType<? extends J>, SubGraphImplementor<? extends J>> subclassSubgraphs;
34+
2535
public RootGraphImpl(String name, EntityDomainType<J> entityType, boolean mutable) {
2636
super( entityType, mutable );
2737
this.name = name;
38+
this.subclassSubgraphs = new HashMap<>();
2839
}
2940

3041
public RootGraphImpl(String name, EntityDomainType<J> entityType) {
3142
this( name, entityType, true );
3243
}
3344

3445
public RootGraphImpl(String name, GraphImplementor<J> original, boolean mutable) {
35-
super(original, mutable);
46+
super( original, mutable );
3647
this.name = name;
48+
this.subclassSubgraphs = new HashMap<>();
3749
}
3850

3951
@Override
@@ -43,22 +55,51 @@ public String getName() {
4355

4456
@Override
4557
public RootGraphImplementor<J> makeCopy(boolean mutable) {
46-
return new RootGraphImpl<>( null, this, mutable);
58+
return new RootGraphImpl<>( null, this, mutable );
4759
}
4860

4961
@Override
5062
public SubGraphImplementor<J> makeSubGraph(boolean mutable) {
51-
return new SubGraphImpl<>(this, mutable);
63+
return new SubGraphImpl<>( this, mutable );
5264
}
5365

5466
@Override
5567
public RootGraphImplementor<J> makeRootGraph(String name, boolean mutable) {
5668
return !mutable && !isMutable() ? this : super.makeRootGraph( name, mutable );
5769
}
5870

71+
public <S extends J> SubGraphImplementor<S> addTreatedSubgraph(Class<S> type) {
72+
ManagedDomainType<S> subTypeEntityDomainType = DomainModelHelper.findSubType( super.managedType, type );
73+
74+
SubGraphImplementor<S> subgraph = new SubGraphImpl<>( subTypeEntityDomainType, true );
75+
76+
subclassSubgraphs.putIfAbsent( subTypeEntityDomainType, subgraph );
77+
78+
return subgraph;
79+
}
80+
81+
@Override
82+
@SuppressWarnings({ "unchecked", "rawtypes" })
83+
public <AJ> AttributeNodeImplementor<AJ> findAttributeNode(PersistentAttribute<? extends J, AJ> attribute) {
84+
AttributeNodeImplementor<AJ> attributeNode = super.findAttributeNode( attribute );
85+
86+
if ( attributeNode != null ) {
87+
return attributeNode;
88+
}
89+
90+
return subclassSubgraphs.values()
91+
.stream()
92+
.map( subgraph -> ( (SubGraphImplementor) subgraph ).findAttributeNode( attribute ) )
93+
.filter( Objects::nonNull )
94+
.findFirst()
95+
.orElse( null );
96+
97+
}
98+
5999
@Override
100+
@SuppressWarnings({ "unchecked", "rawtypes" })
60101
public <T1> SubGraph<? extends T1> addSubclassSubgraph(Class<? extends T1> type) {
61-
throw new UnsupportedOperationException();
102+
return addTreatedSubgraph( (Class) type );
62103
}
63104

64105
@Override

hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.io.Serializable;
1111
import java.lang.reflect.Field;
1212
import java.util.ArrayList;
13+
import java.util.Arrays;
1314
import java.util.Collection;
1415
import java.util.Collections;
1516
import java.util.HashMap;
@@ -22,6 +23,7 @@
2223
import java.util.stream.Collectors;
2324

2425
import org.checkerframework.checker.nullness.qual.Nullable;
26+
2527
import org.hibernate.boot.model.NamedEntityGraphDefinition;
2628
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
2729
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
@@ -71,7 +73,6 @@
7173
import jakarta.persistence.metamodel.Type;
7274

7375
/**
74-
*
7576
* @author Steve Ebersole
7677
*/
7778
public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable {
@@ -104,7 +105,7 @@ private static class ImportInfo<T> {
104105
private final Map<Class<?>, String> entityProxyInterfaceMap = new HashMap<>();
105106

106107
private final Map<String, ImportInfo<?>> nameToImportMap = new ConcurrentHashMap<>();
107-
private final Map<String,Object> knownInvalidnameToImportMap = new ConcurrentHashMap<>();
108+
private final Map<String, Object> knownInvalidnameToImportMap = new ConcurrentHashMap<>();
108109

109110

110111
public JpaMetamodelImpl(
@@ -284,7 +285,7 @@ public Set<EmbeddableType<?>> getEmbeddables() {
284285

285286
@Override
286287
public @Nullable Set<String> getEnumTypesForValue(String enumValue) {
287-
return allowedEnumLiteralsToEnumTypeNames.get( enumValue);
288+
return allowedEnumLiteralsToEnumTypeNames.get( enumValue );
288289
}
289290

290291
@Override
@@ -362,8 +363,7 @@ public <T> List<RootGraphImplementor<? super T>> findEntityGraphsByJavaType(Clas
362363
final List<RootGraphImplementor<? super T>> results = new ArrayList<>();
363364
for ( RootGraphImplementor<?> entityGraph : entityGraphMap.values() ) {
364365
if ( entityGraph.appliesTo( entityType ) ) {
365-
@SuppressWarnings("unchecked")
366-
final RootGraphImplementor<? super T> result = (RootGraphImplementor<? super T>) entityGraph;
366+
@SuppressWarnings("unchecked") final RootGraphImplementor<? super T> result = (RootGraphImplementor<? super T>) entityGraph;
367367
results.add( result );
368368
}
369369
}
@@ -437,22 +437,59 @@ private void applyNamedEntityGraphs(Collection<NamedEntityGraphDefinition> named
437437
);
438438
}
439439

440-
final RootGraphImpl<Object> entityGraph = new RootGraphImpl<>( definition.getRegisteredName(), entityType );
441-
442440
final NamedEntityGraph namedEntityGraph = definition.getAnnotation();
441+
final RootGraphImpl<?> entityGraph =
442+
createEntityGraph(
443+
namedEntityGraph,
444+
definition.getRegisteredName(),
445+
entityType,
446+
namedEntityGraph.includeAllAttributes()
447+
);
443448

444-
if ( namedEntityGraph.includeAllAttributes() ) {
445-
for ( Attribute<? super Object, ?> attribute : entityType.getAttributes() ) {
446-
entityGraph.addAttributeNodes( attribute );
447-
}
448-
}
449+
entityGraphMap.put( definition.getRegisteredName(), entityGraph );
450+
}
451+
}
449452

450-
if ( namedEntityGraph.attributeNodes() != null ) {
451-
applyNamedAttributeNodes( namedEntityGraph.attributeNodes(), namedEntityGraph, entityGraph );
452-
}
453+
private <T> RootGraphImpl<T> createEntityGraph(
454+
NamedEntityGraph namedEntityGraph,
455+
String registeredName,
456+
EntityDomainType<T> entityType,
457+
boolean includeAllAttributes) {
458+
final RootGraphImpl<T> entityGraph =
459+
createRootGraph( registeredName, entityType, includeAllAttributes );
453460

454-
entityGraphMap.put( definition.getRegisteredName(), entityGraph );
461+
if ( namedEntityGraph.subclassSubgraphs() != null ) {
462+
Arrays
463+
.stream( namedEntityGraph.subclassSubgraphs() )
464+
.forEach( subclassSubgraph -> {
465+
var subgraph = entityGraph.addTreatedSubgraph( subclassSubgraph.type() );
466+
applyNamedAttributeNodes(
467+
subclassSubgraph.attributeNodes(),
468+
namedEntityGraph,
469+
subgraph
470+
);
471+
472+
} );
473+
}
474+
475+
if ( namedEntityGraph.attributeNodes() != null ) {
476+
applyNamedAttributeNodes( namedEntityGraph.attributeNodes(), namedEntityGraph, entityGraph );
477+
}
478+
479+
480+
return entityGraph;
481+
}
482+
483+
@SuppressWarnings("unchecked")
484+
private static <T> RootGraphImpl<T> createRootGraph(
485+
String name, EntityDomainType<T> entityType, boolean includeAllAttributes) {
486+
final RootGraphImpl<T> entityGraph = new RootGraphImpl<>( name, entityType );
487+
if ( includeAllAttributes ) {
488+
for ( Attribute<? super T, ?> attribute : entityType.getAttributes() ) {
489+
entityGraph.addAttributeNodes( (Attribute<T, ?>) attribute );
490+
}
455491
}
492+
return entityGraph;
456493
}
457494

458495
private void applyNamedAttributeNodes(
@@ -463,31 +500,33 @@ private void applyNamedAttributeNodes(
463500
final String value = namedAttributeNode.value();
464501
AttributeNodeImplementor<?> attributeNode = graphNode.addAttributeNode( value );
465502
if ( StringHelper.isNotEmpty( namedAttributeNode.subgraph() ) ) {
466-
final SubGraphImplementor<?> subgraph = attributeNode.makeSubGraph();
467503
applyNamedSubgraphs(
468504
namedEntityGraph,
469505
namedAttributeNode.subgraph(),
470-
subgraph
506+
attributeNode
471507
);
472508
}
473509
if ( StringHelper.isNotEmpty( namedAttributeNode.keySubgraph() ) ) {
474-
final SubGraphImplementor<?> subgraph = attributeNode.makeKeySubGraph();
475-
476510
applyNamedSubgraphs(
477511
namedEntityGraph,
478512
namedAttributeNode.keySubgraph(),
479-
subgraph
513+
attributeNode
480514
);
481515
}
482516
}
483517
}
484518

485-
private void applyNamedSubgraphs(
519+
@SuppressWarnings("unchecked")
520+
private <T> void applyNamedSubgraphs(
486521
NamedEntityGraph namedEntityGraph,
487522
String subgraphName,
488-
SubGraphImplementor<?> subgraph) {
523+
AttributeNodeImplementor<T> attributeNode) {
489524
for ( NamedSubgraph namedSubgraph : namedEntityGraph.subgraphs() ) {
490525
if ( subgraphName.equals( namedSubgraph.name() ) ) {
526+
var isDefaultSubgraphType = namedSubgraph.type().equals( void.class );
527+
SubGraphImplementor<?> subgraph = attributeNode
528+
.makeSubGraph( isDefaultSubgraphType ? null : namedSubgraph.type() );
529+
491530
applyNamedAttributeNodes(
492531
namedSubgraph.attributeNodes(),
493532
namedEntityGraph,
@@ -581,7 +620,10 @@ public <T> EntityDomainType<T> resolveEntityReference(Class<T> javaType) {
581620
}
582621
}
583622

584-
throw new EntityTypeException( "Could not resolve entity class '" + javaType.getName() + "'", javaType.getName() );
623+
throw new EntityTypeException(
624+
"Could not resolve entity class '" + javaType.getName() + "'",
625+
javaType.getName()
626+
);
585627
}
586628

587629
@Override
@@ -597,7 +639,7 @@ public void processJpa(
597639
JpaMetaModelPopulationSetting jpaMetaModelPopulationSetting,
598640
Collection<NamedEntityGraphDefinition> namedEntityGraphDefinitions,
599641
RuntimeModelCreationContext runtimeModelCreationContext) {
600-
bootMetamodel.getImports().forEach( ( k, v ) -> this.nameToImportMap.put( k, new ImportInfo<>( v, null ) ) );
642+
bootMetamodel.getImports().forEach( (k, v) -> this.nameToImportMap.put( k, new ImportInfo<>( v, null ) ) );
601643
this.entityProxyInterfaceMap.putAll( entityProxyInterfaceMap );
602644

603645
final MetadataContext context = new MetadataContext(

hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardEntityGraphTraversalStateImpl.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.hibernate.engine.FetchTiming;
1313
import org.hibernate.graph.GraphSemantic;
14+
import org.hibernate.graph.internal.SubGraphImpl;
1415
import org.hibernate.graph.spi.AttributeNodeImplementor;
1516
import org.hibernate.graph.spi.GraphImplementor;
1617
import org.hibernate.graph.spi.RootGraphImplementor;
@@ -20,6 +21,7 @@
2021
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
2122
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
2223
import org.hibernate.metamodel.model.domain.JpaMetamodel;
24+
import org.hibernate.metamodel.model.domain.ManagedDomainType;
2325
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
2426
import org.hibernate.sql.results.graph.FetchParent;
2527
import org.hibernate.sql.results.graph.Fetchable;
@@ -51,7 +53,7 @@ public void backtrack(TraversalResult previousContext) {
5153

5254
@Override
5355
public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, boolean exploreKeySubgraph) {
54-
assert !(fetchable instanceof CollectionPart);
56+
assert !( fetchable instanceof CollectionPart );
5557
if ( fetchable instanceof NonAggregatedIdentifierMapping ) {
5658
return new TraversalResult( currentGraphContext, new FetchStrategy( FetchTiming.IMMEDIATE, true ) );
5759
}
@@ -84,7 +86,7 @@ public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, bo
8486
subgraphMapKey = fetchable.getJavaType().getJavaTypeClass();
8587
}
8688
if ( subgraphMap != null && subgraphMapKey != null ) {
87-
currentGraphContext = subgraphMap.get( subgraphMapKey );
89+
currentGraphContext = getMergedSubGraphs( subgraphMap, subgraphMapKey );
8890
}
8991
}
9092
else if ( graphSemantic == GraphSemantic.FETCH ) {
@@ -96,6 +98,25 @@ else if ( graphSemantic == GraphSemantic.FETCH ) {
9698
return new TraversalResult( previousContextRoot, fetchStrategy );
9799
}
98100

101+
@SuppressWarnings({ "unchecked", "rawtypes" })
102+
private GraphImplementor<?> getMergedSubGraphs(
103+
Map<? extends Class<?>, ? extends SubGraphImplementor<?>> subgraphMap,
104+
Class<?> subgraphMapKey) {
105+
ManagedDomainType<?> managedDomainType = metamodel.findManagedType( subgraphMapKey );
106+
107+
if ( managedDomainType == null ) {
108+
return null;
109+
}
110+
111+
GraphImplementor mergedSubgraph = new SubGraphImpl<>( managedDomainType, true );
112+
113+
for ( GraphImplementor subgraph : subgraphMap.values() ) {
114+
mergedSubgraph.merge( subgraph );
115+
}
116+
117+
return mergedSubgraph;
118+
}
119+
99120
private Class<?> getEntityCollectionPartJavaClass(CollectionPart collectionPart) {
100121
if ( collectionPart instanceof EntityCollectionPart ) {
101122
EntityCollectionPart entityCollectionPart = (EntityCollectionPart) collectionPart;

0 commit comments

Comments
 (0)