HHH-8903 bi-directional fetches in entity graphs
This commit is contained in:
parent
fef6c5394a
commit
f815f70f51
|
@ -32,4 +32,5 @@ import javax.persistence.AttributeNode;
|
||||||
public interface GraphNodeImplementor {
|
public interface GraphNodeImplementor {
|
||||||
List<AttributeNodeImplementor<?>> attributeImplementorNodes();
|
List<AttributeNodeImplementor<?>> attributeImplementorNodes();
|
||||||
List<AttributeNode<?>> attributeNodes();
|
List<AttributeNode<?>> attributeNodes();
|
||||||
|
boolean containsAttribute(String name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,12 @@ import org.hibernate.graph.spi.AttributeNodeImplementor;
|
||||||
import org.hibernate.graph.spi.GraphNodeImplementor;
|
import org.hibernate.graph.spi.GraphNodeImplementor;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
|
import org.hibernate.loader.plan.spi.FetchSource;
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
import org.hibernate.loader.plan.spi.Return;
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
|
import org.hibernate.persister.entity.Joinable;
|
||||||
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
|
||||||
|
import org.hibernate.persister.walking.spi.AssociationKey;
|
||||||
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
import org.hibernate.persister.walking.spi.AttributeDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
|
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
|
||||||
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
|
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
|
||||||
|
@ -330,5 +333,30 @@ public abstract class AbstractEntityGraphVisitationStrategy
|
||||||
public List<AttributeNode<?>> attributeNodes() {
|
public List<AttributeNode<?>> attributeNodes() {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAttribute(String name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void foundCircularAssociation(AssociationAttributeDefinition attributeDefinition) {
|
||||||
|
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
|
||||||
|
if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) {
|
||||||
|
return; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bi-directional association & the owning side was already visited. If the current attribute node refers
|
||||||
|
// to it, fetch.
|
||||||
|
// ENTITY nature handled by super.
|
||||||
|
final GraphNodeImplementor graphNode = graphStack.peekLast();
|
||||||
|
if ( attributeDefinition.getAssociationNature() == AssociationAttributeDefinition.AssociationNature.COLLECTION
|
||||||
|
&& ! graphNode.equals( NON_EXIST_SUBGRAPH_NODE)
|
||||||
|
&& graphNode.containsAttribute( attributeDefinition.getName() )) {
|
||||||
|
currentSource().buildCollectionAttributeFetch( attributeDefinition, fetchStrategy );
|
||||||
|
}
|
||||||
|
|
||||||
|
super.foundCircularAssociation( attributeDefinition );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpandingFetchSource currentSource() {
|
protected ExpandingFetchSource currentSource() {
|
||||||
return fetchSourceStack.peekFirst();
|
return fetchSourceStack.peekFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.persistence.AttributeNode;
|
import javax.persistence.AttributeNode;
|
||||||
import javax.persistence.metamodel.Attribute;
|
import javax.persistence.metamodel.Attribute;
|
||||||
|
|
||||||
|
@ -36,7 +37,6 @@ import org.hibernate.graph.spi.GraphNodeImplementor;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.jpa.HibernateEntityManagerFactory;
|
import org.hibernate.jpa.HibernateEntityManagerFactory;
|
||||||
import org.hibernate.jpa.spi.HibernateEntityManagerFactoryAware;
|
import org.hibernate.jpa.spi.HibernateEntityManagerFactoryAware;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,7 @@ import org.jboss.logging.Logger;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractGraphNode<T> implements GraphNodeImplementor, HibernateEntityManagerFactoryAware{
|
public abstract class AbstractGraphNode<T> implements GraphNodeImplementor, HibernateEntityManagerFactoryAware {
|
||||||
private static final Logger log = Logger.getLogger( AbstractGraphNode.class );
|
private static final Logger log = Logger.getLogger( AbstractGraphNode.class );
|
||||||
|
|
||||||
private final HibernateEntityManagerFactory entityManagerFactory;
|
private final HibernateEntityManagerFactory entityManagerFactory;
|
||||||
|
@ -200,4 +200,9 @@ public abstract class AbstractGraphNode<T> implements GraphNodeImplementor, Hibe
|
||||||
public <X> SubgraphImpl<X> addKeySubgraph(String attributeName, Class<X> type) {
|
public <X> SubgraphImpl<X> addKeySubgraph(String attributeName, Class<X> type) {
|
||||||
return addAttribute( attributeName ).makeKeySubgraph( type );
|
return addAttribute( attributeName ).makeKeySubgraph( type );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAttribute(String name) {
|
||||||
|
return attributeNodeMap != null && attributeNodeMap.containsKey( name );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,10 +136,14 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
Bar bar = new Bar();
|
Bar bar = new Bar();
|
||||||
em.persist( bar );
|
em.persist( bar );
|
||||||
|
Baz baz = new Baz();
|
||||||
|
em.persist( baz );
|
||||||
|
|
||||||
Foo foo = new Foo();
|
Foo foo = new Foo();
|
||||||
foo.bar = bar;
|
foo.bar = bar;
|
||||||
|
foo.baz = baz;
|
||||||
bar.foos.add(foo);
|
bar.foos.add(foo);
|
||||||
|
baz.foos.add(foo);
|
||||||
em.persist( foo );
|
em.persist( foo );
|
||||||
|
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
|
@ -149,7 +153,8 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
EntityGraph<Foo> fooGraph = em.createEntityGraph( Foo.class );
|
EntityGraph<Foo> fooGraph = em.createEntityGraph( Foo.class );
|
||||||
fooGraph.addAttributeNodes("bar");
|
fooGraph.addAttributeNodes("bar");
|
||||||
Subgraph barGraph = fooGraph.addSubgraph("bar");
|
fooGraph.addAttributeNodes("baz");
|
||||||
|
Subgraph<Bar> barGraph = fooGraph.addSubgraph("bar", Bar.class);
|
||||||
barGraph.addAttributeNodes("foos");
|
barGraph.addAttributeNodes("foos");
|
||||||
|
|
||||||
Map<String, Object> properties = new HashMap<String, Object>();
|
Map<String, Object> properties = new HashMap<String, Object>();
|
||||||
|
@ -160,6 +165,9 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
assertTrue( Hibernate.isInitialized( result ) );
|
assertTrue( Hibernate.isInitialized( result ) );
|
||||||
assertTrue( Hibernate.isInitialized( result.bar ) );
|
assertTrue( Hibernate.isInitialized( result.bar ) );
|
||||||
assertTrue( Hibernate.isInitialized( result.bar.foos) );
|
assertTrue( Hibernate.isInitialized( result.bar.foos) );
|
||||||
|
assertTrue( Hibernate.isInitialized( result.baz ) );
|
||||||
|
// sanity check -- ensure the only bi-directional fetch was the one identified by the graph
|
||||||
|
assertFalse( Hibernate.isInitialized( result.baz.foos) );
|
||||||
|
|
||||||
em.getTransaction().commit();
|
em.getTransaction().commit();
|
||||||
em.close();
|
em.close();
|
||||||
|
@ -249,6 +257,9 @@ public class EntityGraphTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
public Integer id;
|
public Integer id;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "bar")
|
||||||
|
public Set<Foo> foos = new HashSet<Foo>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue