HHH-8903 bi-directional fetches in entity graphs

This commit is contained in:
Brett Meyer 2014-01-28 16:43:33 -05:00
parent fef6c5394a
commit f815f70f51
5 changed files with 49 additions and 4 deletions

View File

@ -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);
} }

View File

@ -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 );
}
} }

View File

@ -138,7 +138,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
return last; return last;
} }
private ExpandingFetchSource currentSource() { protected ExpandingFetchSource currentSource() {
return fetchSourceStack.peekFirst(); return fetchSourceStack.peekFirst();
} }

View File

@ -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 );
}
} }

View File

@ -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>();
} }
} }