/*
* 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.test.loadplans.plans;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.build.internal.returns.CollectionFetchableElementEntityGraph;
import org.hibernate.loader.plan.spi.BidirectionalEntityReference;
import org.hibernate.loader.plan.spi.CollectionAttributeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.FetchSource;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.test.annotations.Country;
import org.hibernate.test.annotations.cid.keymanytoone.Card;
import org.hibernate.test.annotations.cid.keymanytoone.CardField;
import org.hibernate.test.annotations.cid.keymanytoone.Key;
import org.hibernate.test.annotations.cid.keymanytoone.PrimaryKey;
import org.hibernate.test.annotations.collectionelement.Boy;
import org.hibernate.test.annotations.collectionelement.Matrix;
import org.hibernate.test.annotations.collectionelement.TestCourse;
import org.hibernate.test.loadplans.process.EncapsulatedCompositeIdResultSetProcessorTest;
import org.junit.Test;
import static junit.framework.Assert.assertNotNull;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
//import org.hibernate.loader.plan.spi.BidirectionalEntityFetch;
/**
* Used to assert that "fetch graphs" between JoinWalker and LoadPlan are same.
*
* @author Steve Ebersole
*/
public class LoadPlanStructureAssertionTest extends BaseUnitTestCase {
@Test
public void testJoinedOneToOne() {
// tests the mappings defined in org.hibernate.test.onetoone.joined.JoinedSubclassOneToOneTest
Configuration cfg = new Configuration();
cfg.addResource( "org/hibernate/test/onetoone/joined/Person.hbm.xml" );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
// doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( org.hibernate.test.onetoone.joined.Person.class ) );
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( org.hibernate.test.onetoone.joined.Entity.class ) );
// doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( org.hibernate.test.onetoone.joined.Address.class ) );
}
finally {
sf.close();
}
}
@Test
public void testSpecialOneToOne() {
// tests the mappings defined in org.hibernate.test.onetoone.joined.JoinedSubclassOneToOneTest
Configuration cfg = new Configuration();
cfg.addResource( "org/hibernate/test/onetoone/formula/Person.hbm.xml" );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( org.hibernate.test.onetoone.formula.Person.class ) );
}
finally {
sf.close();
}
}
@Test
public void testEncapsulatedCompositeIdNoFetches1() {
// CardField is an entity with a composite identifier mapped via a @EmbeddedId class (CardFieldPK) defining
// a @ManyToOne
Configuration cfg = new Configuration();
cfg.addAnnotatedClass( EncapsulatedCompositeIdResultSetProcessorTest.CardField.class );
cfg.addAnnotatedClass( EncapsulatedCompositeIdResultSetProcessorTest.Card.class );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( EncapsulatedCompositeIdResultSetProcessorTest.CardField.class ) );
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( EncapsulatedCompositeIdResultSetProcessorTest.Card.class ) );
}
finally {
sf.close();
}
}
@Test
public void testEncapsulatedCompositeIdNoFetches2() {
// Parent is an entity with a composite identifier mapped via a @EmbeddedId class (ParentPK) which is defined
// using just basic types (strings, ints, etc)
Configuration cfg = new Configuration();
cfg.addAnnotatedClass( EncapsulatedCompositeIdResultSetProcessorTest.Parent.class );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( EncapsulatedCompositeIdResultSetProcessorTest.Parent.class ) );
}
finally {
sf.close();
}
}
@Test
public void testEncapsulatedCompositeIdWithFetches1() {
Configuration cfg = new Configuration();
cfg.addAnnotatedClass( Card.class );
cfg.addAnnotatedClass( CardField.class );
cfg.addAnnotatedClass( Key.class );
cfg.addAnnotatedClass( PrimaryKey.class );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
final OuterJoinLoadable cardFieldPersister = (OuterJoinLoadable) sf.getClassMetadata( CardField.class );
doCompare( sf, cardFieldPersister );
final LoadPlan loadPlan = LoadPlanStructureAssertionHelper.INSTANCE.buildLoadPlan( sf, cardFieldPersister );
assertEquals( LoadPlan.Disposition.ENTITY_LOADER, loadPlan.getDisposition() );
assertEquals( 1, loadPlan.getReturns().size() );
final EntityReturn cardFieldReturn = assertTyping( EntityReturn.class, loadPlan.getReturns().get( 0 ) );
assertEquals( 0, cardFieldReturn.getFetches().length );
// CardField defines a composite pk with 2 many-to-ones : Card and Key (the id description acts as the composite);
// because it is an @EmbeddedId, the ID provided by the application is used "as is"
// and fetches are not included in the load plan.
assertFalse( cardFieldReturn.getIdentifierDescription().hasFetches() );
// we need the readers ordered in a certain manner. Here specifically: Fetch(Card), Fetch(Key), Return(CardField)
//
// additionally, we need Fetch(Card) and Fetch(Key) to be hydrated/semi-resolved before attempting to
// resolve the EntityKey for Return(CardField)
//
// together those sound like argument enough to continue keeping readers for "identifier fetches" as part of
// a special "identifier reader". generated aliases could help here too to remove cyclic-ness from the graph.
// but at any rate, we need to know still when this becomes circularity
}
finally {
sf.close();
}
}
@Test
public void testEncapsulatedCompositeIdWithFetches2() {
Configuration cfg = new Configuration();
cfg.addAnnotatedClass( Card.class );
cfg.addAnnotatedClass( CardField.class );
cfg.addAnnotatedClass( Key.class );
cfg.addAnnotatedClass( PrimaryKey.class );
final SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
final OuterJoinLoadable cardPersister = (OuterJoinLoadable) sf.getClassMetadata( Card.class );
doCompare( sf, cardPersister );
final LoadPlan cardLoadPlan = LoadPlanStructureAssertionHelper.INSTANCE.buildLoadPlan( sf, cardPersister );
assertEquals( LoadPlan.Disposition.ENTITY_LOADER, cardLoadPlan.getDisposition() );
assertEquals( 1, cardLoadPlan.getReturns().size() );
// Check the root EntityReturn(Card)
final EntityReturn cardReturn = assertTyping( EntityReturn.class, cardLoadPlan.getReturns().get( 0 ) );
assertFalse( cardReturn.getIdentifierDescription().hasFetches() );
// Card should have one fetch, the fields collection
assertEquals( 1, cardReturn.getFetches().length );
final CollectionAttributeFetch fieldsFetch = assertTyping( CollectionAttributeFetch.class, cardReturn.getFetches()[0] );
assertNotNull( fieldsFetch.getElementGraph() );
// the Card.fields collection has entity elements of type CardField...
final CollectionFetchableElementEntityGraph cardFieldElementGraph = assertTyping( CollectionFetchableElementEntityGraph.class, fieldsFetch.getElementGraph() );
// CardField should have no fetches
assertEquals( 0, cardFieldElementGraph.getFetches().length );
// But it should have 1 key-many-to-one fetch for Key (Card is already handled)
assertTrue( cardFieldElementGraph.getIdentifierDescription().hasFetches() );
final FetchSource cardFieldElementGraphIdAsFetchSource = assertTyping(
FetchSource.class,
cardFieldElementGraph.getIdentifierDescription()
);
assertEquals( 1, cardFieldElementGraphIdAsFetchSource.getFetches().length );
assertEquals( 1, cardFieldElementGraphIdAsFetchSource.getBidirectionalEntityReferences().length );
BidirectionalEntityReference circularCardFetch = assertTyping(
BidirectionalEntityReference.class,
cardFieldElementGraphIdAsFetchSource.getBidirectionalEntityReferences()[0]
);
assertSame( circularCardFetch.getTargetEntityReference(), cardReturn );
// the fetch above is to the other key-many-to-one for CardField.primaryKey composite: key
EntityFetch keyFetch = assertTyping(
EntityFetch.class,
cardFieldElementGraphIdAsFetchSource.getFetches()[0]
);
assertEquals( Key.class.getName(), keyFetch.getEntityPersister().getEntityName() );
}
finally {
sf.close();
}
}
@Test
public void testManyToMany() {
Configuration cfg = new Configuration();
cfg.addResource( "org/hibernate/test/immutable/entitywithmutablecollection/inverse/ContractVariation.hbm.xml" );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( org.hibernate.test.immutable.entitywithmutablecollection.Contract.class ) );
}
finally {
sf.close();
}
}
@Test
public void testAnotherBasicCollection() {
Configuration cfg = new Configuration();
cfg.addAnnotatedClass( Boy.class );
cfg.addAnnotatedClass( Country.class );
cfg.addAnnotatedClass( TestCourse.class );
cfg.addAnnotatedClass( Matrix.class );
SessionFactoryImplementor sf = (SessionFactoryImplementor) cfg.buildSessionFactory();
try {
doCompare( sf, (OuterJoinLoadable) sf.getClassMetadata( Boy.class ) );
}
finally {
sf.close();
}
}
private void doCompare(SessionFactoryImplementor sf, OuterJoinLoadable persister) {
LoadPlanStructureAssertionHelper.INSTANCE.performBasicComparison( sf, persister );
}
}