Skip to content

Commit 7c69c7f

Browse files
committed
HHH-8855 HHH-8640 corrected EntityGraph loadplan strategy, support inheritance in SubGraphs
1 parent d0a699d commit 7c69c7f

File tree

10 files changed

+325
-136
lines changed

10 files changed

+325
-136
lines changed

hibernate-core/src/main/java/org/hibernate/engine/query/spi/EntityGraphQueryHint.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
2728
import javax.persistence.AttributeNode;
2829
import javax.persistence.EntityGraph;
2930
import javax.persistence.Subgraph;
@@ -36,6 +37,7 @@
3637
import org.hibernate.hql.internal.ast.tree.FromElementFactory;
3738
import org.hibernate.persister.collection.QueryableCollection;
3839
import org.hibernate.sql.JoinType;
40+
import org.hibernate.type.CollectionType;
3941
import org.hibernate.type.EntityType;
4042
import org.hibernate.type.Type;
4143

@@ -85,6 +87,8 @@ private List<FromElement> getFromElements(
8587

8688
final String attributeName = attributeNode.getAttributeName();
8789
final String className = origin.getClassName();
90+
// TODO: This is ignored by collection types and probably wrong for entity types. Presumably it screws
91+
// with inheritance.
8892
final String role = className + "." + attributeName;
8993
final String classAlias = origin.getClassAlias();
9094
final String originTableAlias = origin.getTableAlias();
@@ -120,16 +124,17 @@ private List<FromElement> getFromElements(
120124
);
121125
}
122126
else if ( propertyType.isCollectionType() ) {
127+
CollectionType collectionType = (CollectionType) propertyType;
123128
final String[] columns = origin.toColumns( originTableAlias, attributeName, false );
124129

125130
final FromElementFactory fromElementFactory = new FromElementFactory(
126131
fromClause, origin,
127132
attributeName, classAlias, columns, false
128133
);
129134
final QueryableCollection queryableCollection = walker.getSessionFactoryHelper()
130-
.requireQueryableCollection( role );
135+
.requireQueryableCollection( collectionType.getRole() );
131136
fromElement = fromElementFactory.createCollection(
132-
queryableCollection, role, JoinType.LEFT_OUTER_JOIN, true, false
137+
queryableCollection, collectionType.getRole(), JoinType.LEFT_OUTER_JOIN, true, false
133138
);
134139
}
135140
}

hibernate-core/src/main/java/org/hibernate/loader/plan/build/internal/AbstractEntityGraphVisitationStrategy.java

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@
4949
import org.hibernate.loader.plan.spi.Return;
5050
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
5151
import org.hibernate.persister.walking.spi.AttributeDefinition;
52-
import org.hibernate.persister.walking.spi.CollectionDefinition;
5352
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
5453
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
55-
import org.hibernate.persister.walking.spi.CompositionDefinition;
5654
import org.hibernate.persister.walking.spi.EntityDefinition;
5755
import org.hibernate.persister.walking.spi.WalkingException;
5856
import org.jboss.logging.Logger;
@@ -73,6 +71,7 @@
7371
* it is not, then depends on which property is used to apply this entity graph.
7472
*
7573
* @author Strong Liu <[email protected]>
74+
* @author Brett Meyer
7675
*/
7776
public abstract class AbstractEntityGraphVisitationStrategy
7877
extends AbstractLoadPlanBuildingAssociationVisitationStrategy {
@@ -87,10 +86,14 @@ public abstract class AbstractEntityGraphVisitationStrategy
8786
protected static final FetchStrategy DEFAULT_EAGER = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
8887
protected static final FetchStrategy DEFAULT_LAZY = new FetchStrategy( FetchTiming.DELAYED, FetchStyle.SELECT );
8988
protected final LoadQueryInfluencers loadQueryInfluencers;
90-
protected final ArrayDeque<GraphNodeImplementor> graphStack = new ArrayDeque<GraphNodeImplementor>();
91-
protected final ArrayDeque<AttributeNodeImplementor> attributeStack = new ArrayDeque<AttributeNodeImplementor>();
92-
//the attribute nodes defined in the current graph node (entity graph or subgraph) we're working on
93-
protected Map<String, AttributeNodeImplementor> attributeNodeImplementorMap = Collections.emptyMap();
89+
// Queue containing entity/sub graphs to be visited.
90+
private final ArrayDeque<GraphNodeImplementor> graphStack = new ArrayDeque<GraphNodeImplementor>();
91+
// Queue containing attributes being visited, used eventually to determine the fetch strategy.
92+
private final ArrayDeque<AttributeNodeImplementor> attributeStack = new ArrayDeque<AttributeNodeImplementor>();
93+
// Queue of maps containing the current graph node's attributes. Used for fast lookup, instead of iterating
94+
// over graphStack.peekLast().attributeImplementorNodes().
95+
private final ArrayDeque<Map<String, AttributeNodeImplementor>> attributeMapStack
96+
= new ArrayDeque<Map<String, AttributeNodeImplementor>>();
9497
private EntityReturn rootEntityReturn;
9598
private final LockMode lockMode;
9699

@@ -106,24 +109,21 @@ protected AbstractEntityGraphVisitationStrategy(
106109
public void start() {
107110
super.start();
108111
graphStack.addLast( getRootEntityGraph() );
109-
attributeNodeImplementorMap = buildAttributeNodeMap();
110112
}
111113

112114
@Override
113115
public void finish() {
114116
super.finish();
115117
graphStack.removeLast();
116-
attributeNodeImplementorMap = Collections.emptyMap();
117118
//applying a little internal stack checking
118-
if ( !graphStack.isEmpty() || !attributeStack.isEmpty() || !attributeNodeImplementorMap.isEmpty() ) {
119+
if ( !graphStack.isEmpty() || !attributeStack.isEmpty() || !attributeMapStack.isEmpty() ) {
119120
throw new WalkingException( "Internal stack error" );
120121
}
121122
}
122123

123124
@Override
124125
public void startingEntity(final EntityDefinition entityDefinition) {
125-
//TODO check if the passed in entity definition is the same as the root entity graph (a.k.a they are came from same entity class)?
126-
//this maybe the root entity graph or a sub graph.
126+
attributeMapStack.addLast( buildAttributeNodeMap() );
127127
super.startingEntity( entityDefinition );
128128
}
129129

@@ -143,6 +143,12 @@ protected Map<String, AttributeNodeImplementor> buildAttributeNodeMap() {
143143
return attributeNodeImplementorMap;
144144
}
145145

146+
@Override
147+
public void finishingEntity(final EntityDefinition entityDefinition) {
148+
attributeMapStack.removeLast();
149+
super.finishingEntity( entityDefinition );
150+
}
151+
146152
/**
147153
* I'm using NULL-OBJECT pattern here, for attributes that not existing in the EntityGraph,
148154
* a predefined NULL-ATTRIBUTE-NODE is pushed to the stack.
@@ -156,12 +162,13 @@ protected Map<String, AttributeNodeImplementor> buildAttributeNodeMap() {
156162
*/
157163
@Override
158164
public boolean startingAttribute(AttributeDefinition attributeDefinition) {
165+
Map<String, AttributeNodeImplementor> attributeMap = attributeMapStack.peekLast();
159166
final String attrName = attributeDefinition.getName();
160167
AttributeNodeImplementor attributeNode = NON_EXIST_ATTRIBUTE_NODE;
161168
GraphNodeImplementor subGraphNode = NON_EXIST_SUBGRAPH_NODE;
162169
//the attribute is in the EntityGraph, so, let's continue
163-
if ( attributeNodeImplementorMap.containsKey( attrName ) ) {
164-
attributeNode = attributeNodeImplementorMap.get( attrName );
170+
if ( attributeMap.containsKey( attrName ) ) {
171+
attributeNode = attributeMap.get( attrName );
165172
//here we need to check if there is a subgraph (or sub key graph if it is an indexed attribute )
166173
Map<Class, Subgraph> subGraphs = attributeNode.getSubgraphs();
167174
Class javaType = attributeDefinition.getType().getReturnedClass();
@@ -183,52 +190,26 @@ public void finishingAttribute(final AttributeDefinition attributeDefinition) {
183190
super.finishingAttribute( attributeDefinition );
184191
}
185192

186-
@Override
187-
protected boolean handleAssociationAttribute(
188-
final AssociationAttributeDefinition attributeDefinition) {
189-
return super.handleAssociationAttribute( attributeDefinition );
190-
}
191-
192-
@Override
193-
protected boolean handleCompositeAttribute(
194-
final AttributeDefinition attributeDefinition) {
195-
return super.handleCompositeAttribute( attributeDefinition );
196-
}
197-
198-
199-
@Override
200-
public void startingComposite(final CompositionDefinition compositionDefinition) {
201-
super.startingComposite( compositionDefinition );
202-
}
203-
204-
205-
@Override
206-
public void finishingComposite(final CompositionDefinition compositionDefinition) {
207-
super.finishingComposite( compositionDefinition );
208-
}
209-
210-
211-
@Override
212-
public void startingCollection(final CollectionDefinition collectionDefinition) {
213-
super.startingCollection( collectionDefinition );
214-
}
215-
216-
@Override
217-
public void finishingCollection(final CollectionDefinition collectionDefinition) {
218-
super.finishingCollection( collectionDefinition );
219-
}
220-
221193

222194
@Override
223195
public void startingCollectionElements(
224196
final CollectionElementDefinition elementDefinition) {
197+
AttributeNodeImplementor attributeNode = attributeStack.peekLast();
198+
GraphNodeImplementor subGraphNode = NON_EXIST_SUBGRAPH_NODE;
199+
Map<Class, Subgraph> subGraphs = attributeNode.getSubgraphs();
200+
Class javaType = elementDefinition.getType().getReturnedClass();
201+
if ( !subGraphs.isEmpty() && subGraphs.containsKey( javaType ) ) {
202+
subGraphNode = (GraphNodeImplementor) subGraphs.get( javaType );
203+
}
204+
graphStack.addLast( subGraphNode );
225205
super.startingCollectionElements( elementDefinition );
226206
}
227207

228208
@Override
229209
public void finishingCollectionElements(
230210
final CollectionElementDefinition elementDefinition) {
231211
super.finishingCollectionElements( elementDefinition );
212+
graphStack.removeLast();
232213
}
233214

234215

hibernate-entitymanager/src/main/java/org/hibernate/jpa/graph/internal/SubgraphImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public List<AttributeNode<?>> getAttributeNodes() {
122122
@Override
123123
@SuppressWarnings("unchecked")
124124
protected Attribute<T,?> resolveAttribute(String attributeName) {
125-
final Attribute<T,?> attribute = managedType.getDeclaredAttribute( attributeName );
125+
final Attribute<T,?> attribute = managedType.getAttribute( attributeName );
126126
if ( attribute == null ) {
127127
throw new IllegalArgumentException(
128128
String.format(
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* JBoss, Home of Professional Open Source
5+
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
6+
* as indicated by the @authors tag. All rights reserved.
7+
* See the copyright.txt in the distribution for a
8+
* full listing of individual contributors.
9+
*
10+
* This copyrighted material is made available to anyone wishing to use,
11+
* modify, copy, or redistribute it subject to the terms and conditions
12+
* of the GNU Lesser General Public License, v. 2.1.
13+
* This program is distributed in the hope that it will be useful, but WITHOUT A
14+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15+
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16+
* You should have received a copy of the GNU Lesser General Public License,
17+
* v.2.1 along with this distribution; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19+
* MA 02110-1301, USA.
20+
*/
21+
package org.hibernate.jpa.test.graphs;
22+
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
26+
import javax.persistence.ElementCollection;
27+
import javax.persistence.Entity;
28+
import javax.persistence.FetchType;
29+
import javax.persistence.GeneratedValue;
30+
import javax.persistence.Id;
31+
import javax.persistence.OneToMany;
32+
import javax.persistence.OneToOne;
33+
34+
35+
/**
36+
* @author Brett Meyer
37+
*/
38+
@Entity
39+
public class Company {
40+
@Id @GeneratedValue
41+
public long id;
42+
43+
@OneToMany
44+
public Set<Employee> employees = new HashSet<Employee>();
45+
46+
@OneToOne(fetch = FetchType.LAZY)
47+
public Location location;
48+
49+
@ElementCollection
50+
public Set<Market> markets = new HashSet<Market>();
51+
52+
@ElementCollection(fetch = FetchType.EAGER)
53+
public Set<String> phoneNumbers = new HashSet<String>();
54+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* JBoss, Home of Professional Open Source
5+
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
6+
* as indicated by the @authors tag. All rights reserved.
7+
* See the copyright.txt in the distribution for a
8+
* full listing of individual contributors.
9+
*
10+
* This copyrighted material is made available to anyone wishing to use,
11+
* modify, copy, or redistribute it subject to the terms and conditions
12+
* of the GNU Lesser General Public License, v. 2.1.
13+
* This program is distributed in the hope that it will be useful, but WITHOUT A
14+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15+
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16+
* You should have received a copy of the GNU Lesser General Public License,
17+
* v.2.1 along with this distribution; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19+
* MA 02110-1301, USA.
20+
*/
21+
package org.hibernate.jpa.test.graphs;
22+
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
26+
import javax.persistence.Entity;
27+
import javax.persistence.GeneratedValue;
28+
import javax.persistence.Id;
29+
import javax.persistence.Inheritance;
30+
import javax.persistence.InheritanceType;
31+
import javax.persistence.ManyToMany;
32+
33+
34+
/**
35+
* @author Brett Meyer
36+
*/
37+
@Entity
38+
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
39+
public class Employee {
40+
@Id @GeneratedValue
41+
public long id;
42+
43+
@ManyToMany
44+
public Set<Manager> managers = new HashSet<Manager>();
45+
46+
@ManyToMany
47+
public Set<Employee> friends = new HashSet<Employee>();
48+
}

0 commit comments

Comments
 (0)