/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.graphs;
import java.util.Iterator;
import java.util.Set;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.junit.Test;
import static org.junit.Assert.*;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.loader.plan.build.internal.FetchGraphLoadPlanBuildingStrategy;
import org.hibernate.loader.plan.build.internal.LoadGraphLoadPlanBuildingStrategy;
import org.hibernate.loader.plan.build.internal.AbstractLoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter;
import org.hibernate.loader.plan.build.spi.MetamodelDrivenLoadPlanBuilder;
import org.hibernate.loader.plan.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan.spi.Join;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.QuerySpace;
import org.hibernate.persister.entity.EntityPersister;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public class EntityGraphLoadPlanBuilderTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Cat.class, Person.class, Country.class, Dog.class, ExpressCompany.class };
}
@Entity
public static class Dog {
@Id
String name;
@ElementCollection
Set<String> favorites;
}
@Entity
public static class Cat {
@Id
String name;
@ManyToOne(fetch = FetchType.LAZY)
Person owner;
}
@Entity
public static class Person {
@Id
String name;
@OneToMany(mappedBy = "owner")
Set<Cat> pets;
@Embedded
Address homeAddress;
}
@Embeddable
public static class Address {
@ManyToOne
Country country;
}
@Entity
public static class ExpressCompany {
@Id
String name;
@ElementCollection
Set<Address> shipAddresses;
}
@Entity
public static class Country {
@Id
String name;
}
/**
* EntityGraph(1):
*
* Cat
*
* LoadPlan:
*
* Cat
*
* ---------------------
*
* EntityGraph(2):
*
* Cat
* owner -- Person
*
* LoadPlan:
*
* Cat
* owner -- Person
* address --- Address
*/
@Test
public void testBasicFetchLoadPlanBuilding() {
EntityManager em = getOrCreateEntityManager();
EntityGraph eg = em.createEntityGraph( Cat.class );
LoadPlan plan = buildLoadPlan( eg, Mode.FETCH, Cat.class );
LoadPlanTreePrinter.INSTANCE.logTree( plan, new AliasResolutionContextImpl( sfi() ) );
QuerySpace rootQuerySpace = plan.getQuerySpaces().getRootQuerySpaces().get( 0 );
assertFalse(
"With fetchgraph property and an empty EntityGraph, there should be no join at all",
rootQuerySpace.getJoins().iterator().hasNext()
);
// -------------------------------------------------- another a little more complicated case
eg = em.createEntityGraph( Cat.class );
eg.addSubgraph( "owner", Person.class );
plan = buildLoadPlan( eg, Mode.FETCH, Cat.class );
LoadPlanTreePrinter.INSTANCE.logTree( plan, new AliasResolutionContextImpl( sfi() ) );
rootQuerySpace = plan.getQuerySpaces().getRootQuerySpaces().get( 0 );
Iterator<Join> iterator = rootQuerySpace.getJoins().iterator();
assertTrue(
"With fetchgraph property and an empty EntityGraph, there should be no join at all", iterator.hasNext()
);
Join personJoin = iterator.next();
assertNotNull( personJoin );
QuerySpace.Disposition disposition = personJoin.getRightHandSide().getDisposition();
assertEquals(
"This should be an entity join which fetches Person", QuerySpace.Disposition.ENTITY, disposition
);
iterator = personJoin.getRightHandSide().getJoins().iterator();
assertTrue( "The composite address should be fetched", iterator.hasNext() );
Join addressJoin = iterator.next();
assertNotNull( addressJoin );
disposition = addressJoin.getRightHandSide().getDisposition();
assertEquals( QuerySpace.Disposition.COMPOSITE, disposition );
assertFalse( iterator.hasNext() );
assertFalse(
"The ManyToOne attribute in composite should not be fetched",
addressJoin.getRightHandSide().getJoins().iterator().hasNext()
);
em.close();
}
/**
* EntityGraph(1):
*
* Cat
*
* LoadPlan:
*
* Cat
*
* ---------------------
*
* EntityGraph(2):
*
* Cat
* owner -- Person
*
* LoadPlan:
*
* Cat
* owner -- Person
* address --- Address
* country -- Country
*/
@Test
public void testBasicLoadLoadPlanBuilding() {
EntityManager em = getOrCreateEntityManager();
EntityGraph eg = em.createEntityGraph( Cat.class );
LoadPlan plan = buildLoadPlan( eg, Mode.LOAD, Cat.class );
LoadPlanTreePrinter.INSTANCE.logTree( plan, new AliasResolutionContextImpl( sfi() ) );
QuerySpace rootQuerySpace = plan.getQuerySpaces().getRootQuerySpaces().get( 0 );
assertFalse(
"With fetchgraph property and an empty EntityGraph, there should be no join at all",
rootQuerySpace.getJoins().iterator().hasNext()
);
// -------------------------------------------------- another a little more complicated case
eg = em.createEntityGraph( Cat.class );
eg.addSubgraph( "owner", Person.class );
plan = buildLoadPlan( eg, Mode.LOAD, Cat.class );
LoadPlanTreePrinter.INSTANCE.logTree( plan, new AliasResolutionContextImpl( sfi() ) );
rootQuerySpace = plan.getQuerySpaces().getRootQuerySpaces().get( 0 );
Iterator<Join> iterator = rootQuerySpace.getJoins().iterator();
assertTrue(
"With fetchgraph property and an empty EntityGraph, there should be no join at all", iterator.hasNext()
);
Join personJoin = iterator.next();
assertNotNull( personJoin );
QuerySpace.Disposition disposition = personJoin.getRightHandSide().getDisposition();
assertEquals(
"This should be an entity join which fetches Person", QuerySpace.Disposition.ENTITY, disposition
);
iterator = personJoin.getRightHandSide().getJoins().iterator();
assertTrue( "The composite address should be fetched", iterator.hasNext() );
Join addressJoin = iterator.next();
assertNotNull( addressJoin );
disposition = addressJoin.getRightHandSide().getDisposition();
assertEquals( QuerySpace.Disposition.COMPOSITE, disposition );
iterator = addressJoin.getRightHandSide().getJoins().iterator();
assertTrue( iterator.hasNext() );
Join countryJoin = iterator.next();
assertNotNull( countryJoin );
disposition = countryJoin.getRightHandSide().getDisposition();
assertEquals( QuerySpace.Disposition.ENTITY, disposition );
assertFalse(
"The ManyToOne attribute in composite should not be fetched",
countryJoin.getRightHandSide().getJoins().iterator().hasNext()
);
em.close();
}
@Test
public void testBasicElementCollections() {
EntityManager em = getOrCreateEntityManager();
EntityGraph eg = em.createEntityGraph( Dog.class );
eg.addAttributeNodes( "favorites" );
LoadPlan loadLoadPlan = buildLoadPlan( eg, Mode.LOAD, Dog.class ); //WTF name!!!
LoadPlanTreePrinter.INSTANCE.logTree( loadLoadPlan, new AliasResolutionContextImpl( sfi() ) );
QuerySpace querySpace = loadLoadPlan.getQuerySpaces().getRootQuerySpaces().iterator().next();
Iterator<Join> iterator = querySpace.getJoins().iterator();
assertTrue( iterator.hasNext() );
Join collectionJoin = iterator.next();
assertEquals( QuerySpace.Disposition.COLLECTION, collectionJoin.getRightHandSide().getDisposition() );
assertFalse( iterator.hasNext() );
//----------------------------------------------------------------
LoadPlan fetchLoadPlan = buildLoadPlan( eg, Mode.FETCH, Dog.class );
LoadPlanTreePrinter.INSTANCE.logTree( fetchLoadPlan, new AliasResolutionContextImpl( sfi() ) );
querySpace = fetchLoadPlan.getQuerySpaces().getRootQuerySpaces().iterator().next();
iterator = querySpace.getJoins().iterator();
assertTrue( iterator.hasNext() );
collectionJoin = iterator.next();
assertEquals( QuerySpace.Disposition.COLLECTION, collectionJoin.getRightHandSide().getDisposition() );
assertFalse( iterator.hasNext() );
em.close();
}
@Test
public void testEmbeddedCollection() {
EntityManager em = getOrCreateEntityManager();
EntityGraph eg = em.createEntityGraph( ExpressCompany.class );
eg.addAttributeNodes( "shipAddresses" );
LoadPlan loadLoadPlan = buildLoadPlan( eg, Mode.LOAD, ExpressCompany.class ); //WTF name!!!
LoadPlanTreePrinter.INSTANCE.logTree( loadLoadPlan, new AliasResolutionContextImpl( sfi() ) );
QuerySpace querySpace = loadLoadPlan.getQuerySpaces().getRootQuerySpaces().iterator().next();
Iterator<Join> iterator = querySpace.getJoins().iterator();
assertTrue( iterator.hasNext() );
Join collectionJoin = iterator.next();
assertEquals( QuerySpace.Disposition.COLLECTION, collectionJoin.getRightHandSide().getDisposition() );
assertFalse( iterator.hasNext() );
iterator = collectionJoin.getRightHandSide().getJoins().iterator();
assertTrue( iterator.hasNext() );
Join collectionElementJoin = iterator.next();
assertFalse( iterator.hasNext() );
assertEquals( QuerySpace.Disposition.COMPOSITE, collectionElementJoin.getRightHandSide().getDisposition() );
iterator = collectionElementJoin.getRightHandSide().getJoins().iterator();
assertTrue( iterator.hasNext() );
Join countryJoin = iterator.next();
assertFalse( iterator.hasNext() );
assertEquals( QuerySpace.Disposition.ENTITY, countryJoin.getRightHandSide().getDisposition() );
//----------------------------------------------------------------
LoadPlan fetchLoadPlan = buildLoadPlan( eg, Mode.FETCH, ExpressCompany.class );
LoadPlanTreePrinter.INSTANCE.logTree( fetchLoadPlan, new AliasResolutionContextImpl( sfi() ) );
querySpace = fetchLoadPlan.getQuerySpaces().getRootQuerySpaces().iterator().next();
iterator = querySpace.getJoins().iterator();
assertTrue( iterator.hasNext() );
collectionJoin = iterator.next();
assertEquals( QuerySpace.Disposition.COLLECTION, collectionJoin.getRightHandSide().getDisposition() );
assertFalse( iterator.hasNext() );
iterator = collectionJoin.getRightHandSide().getJoins().iterator();
assertTrue( iterator.hasNext() );
collectionElementJoin = iterator.next();
assertFalse( iterator.hasNext() );
assertEquals( QuerySpace.Disposition.COMPOSITE, collectionElementJoin.getRightHandSide().getDisposition() );
iterator = collectionElementJoin.getRightHandSide().getJoins().iterator();
assertFalse( iterator.hasNext() );
//----------------------------------------------------------------
em.close();
}
private SessionFactoryImplementor sfi() {
return entityManagerFactory().unwrap( SessionFactoryImplementor.class );
}
private LoadPlan buildLoadPlan(EntityGraph entityGraph, Mode mode, Class clazz) {
LoadQueryInfluencers loadQueryInfluencers = new LoadQueryInfluencers( sfi() );
if ( Mode.FETCH == mode ) {
loadQueryInfluencers.setFetchGraph( entityGraph );
}
else {
loadQueryInfluencers.setLoadGraph( entityGraph );
}
EntityPersister ep = (EntityPersister) sfi().getClassMetadata( clazz );
AbstractLoadPlanBuildingAssociationVisitationStrategy strategy = Mode.FETCH == mode ? new FetchGraphLoadPlanBuildingStrategy(
sfi(), loadQueryInfluencers, LockMode.NONE
) : new LoadGraphLoadPlanBuildingStrategy( sfi(), loadQueryInfluencers, LockMode.NONE );
return MetamodelDrivenLoadPlanBuilder.buildRootEntityLoadPlan( strategy, ep );
}
public static enum Mode {FETCH, LOAD}
}