HHH-8276 - Integrate LoadPlans into UniqueEntityLoader (PoC)

This commit is contained in:
Gail Badner 2013-08-12 12:10:13 -07:00 committed by Steve Ebersole
parent 438dd9c180
commit 4defc8a5d6
37 changed files with 470 additions and 130 deletions

View File

@ -36,10 +36,11 @@ import org.hibernate.loader.plan.spi.build.AbstractLoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.build.LoadPlanBuilderStrategy;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
@ -152,4 +153,9 @@ public class SingleRootReturnLoadPlanBuilderStrategy
StringHelper.unqualify( collectionRole )
);
}
@Override
public FetchSource registeredFetchSource(AssociationKey associationKey) {
return null;
}
}

View File

@ -44,7 +44,6 @@ import org.hibernate.loader.plan.spi.BidirectionalEntityFetch;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CollectionReference;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.CompositeElementGraph;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.CopyContext;
import org.hibernate.loader.plan.spi.EntityFetch;
@ -67,7 +66,6 @@ import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
@ -427,8 +425,9 @@ public abstract class AbstractLoadPlanBuilderStrategy implements LoadPlanBuilder
}
@Override
public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) {
public void foundCircularAssociation(AssociationAttributeDefinition attributeDefinition) {
// todo : use this information to create the BiDirectionalEntityFetch instances
final AssociationKey associationKey = attributeDefinition.getAssociationKey();
final FetchOwner fetchOwner = fetchedAssociationKeyOwnerMap.get( associationKey );
if ( fetchOwner == null ) {
throw new IllegalStateException(

View File

@ -34,14 +34,15 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.AbstractLoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingAssociationVisitationStrategy;
import org.hibernate.loader.plan2.build.spi.ExpandingFetchSource;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan2.spi.Return;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
import org.hibernate.persister.walking.spi.AttributeDefinition;
/**
* LoadPlanBuilderStrategy implementation used for building LoadPlans based on metamodel-defined fetching. Built
@ -129,11 +130,6 @@ public class FetchStyleLoadPlanBuildingAssociationVisitationStrategy
return false;
}
@Override
public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) {
// todo : implement this
}
// @Override
// protected EntityReturn buildRootEntityReturn(EntityDefinition entityDefinition) {
// final String entityName = entityDefinition.getEntityPersister().getEntityName();

View File

@ -23,20 +23,25 @@
*/
package org.hibernate.loader.plan2.build.internal.returns;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.spi.ExpandingEntityIdentifierDescription;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CompositeQuerySpace;
import org.hibernate.loader.plan2.spi.EntityIdentifierDescription;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.type.CompositeType;
/**
* @author Steve Ebersole
* @author Gail Badner
*/
public abstract class AbstractCompositeEntityIdentifierDescription
extends AbstractCompositeFetch
implements EntityIdentifierDescription, FetchSource, ExpandingEntityIdentifierDescription {
implements FetchSource, ExpandingEntityIdentifierDescription {
private final EntityReference entityReference;
@ -57,6 +62,55 @@ public abstract class AbstractCompositeEntityIdentifierDescription
@Override
public FetchSource getSource() {
// the source for this (as a Fetch) is the entity reference
return (FetchSource) entityReference.getIdentifierDescription();
return entityReference;
}
@Override
protected EntityFetch createEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
Join fetchedJoin,
LoadPlanBuildingContext loadPlanBuildingContext) {
// we have a key-many-to-one
//
// IMPL NOTE: we pass ourselves as the ExpandingFetchSource to collect fetches and later build
// the IdentifierDescription
// if `this` is a fetch and its owner is "the same" (bi-directionality) as the attribute to be join fetched
// we should wrap our FetchSource as an EntityFetch. That should solve everything except for the alias
// context lookups because of the different instances (because of wrapping). So somehow the consumer of this
// needs to be able to unwrap it to do the alias lookup, and would have to know to do that.
//
//
// we are processing the EntityReference(Address) identifier. we come across its key-many-to-one reference
// to Person. Now, if EntityReference(Address) is an instance of EntityFetch(Address) there is a strong
// likelihood that we have a bi-directionality and need to handle that specially.
//
// how to best (a) find the bi-directionality and (b) represent that?
final FetchSource registeredFetchSource = loadPlanBuildingContext.registeredFetchSource(
attributeDefinition.getAssociationKey()
);
if ( isKeyManyToOneBidirectionalAttributeDefinition( attributeDefinition, loadPlanBuildingContext ) ) {
return new KeyManyToOneBidirectionalEntityFetchImpl(
this,
attributeDefinition,
fetchStrategy,
fetchedJoin,
registeredFetchSource.resolveEntityReference()
);
}
else {
return super.createEntityFetch( attributeDefinition, fetchStrategy, fetchedJoin, loadPlanBuildingContext );
}
}
private boolean isKeyManyToOneBidirectionalAttributeDefinition(
AssociationAttributeDefinition attributeDefinition,
LoadPlanBuildingContext loadPlanBuildingContext) {
final FetchSource registeredFetchSource = loadPlanBuildingContext.registeredFetchSource(
attributeDefinition.getAssociationKey()
);
return registeredFetchSource != null && registeredFetchSource != getSource();
}
}

View File

@ -37,7 +37,9 @@ import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.CompositeQuerySpace;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
@ -52,6 +54,7 @@ import org.hibernate.type.Type;
/**
* @author Steve Ebersole
* @author Gail Badner
*/
public abstract class AbstractCompositeFetch implements CompositeFetch, ExpandingFetchSource {
private static final FetchStrategy FETCH_STRATEGY = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
@ -78,6 +81,30 @@ public abstract class AbstractCompositeFetch implements CompositeFetch, Expandin
return compositeQuerySpace;
}
@Override
public EntityReference resolveEntityReference() {
return resolveFetchSourceEntityReference( this );
}
private static EntityReference resolveFetchSourceEntityReference(CompositeFetch fetch) {
final FetchSource fetchSource = fetch.getSource();
if ( EntityReference.class.isInstance( fetchSource ) ) {
return (EntityReference) fetchSource;
}
else if ( CompositeFetch.class.isInstance( fetchSource ) ) {
return resolveFetchSourceEntityReference( (CompositeFetch) fetchSource );
}
throw new IllegalStateException(
String.format(
"Cannot resolve FetchOwner [%s] of Fetch [%s (%s)] to an EntityReference",
fetchSource,
fetch,
fetch.getPropertyPath()
)
);
}
@Override
public String getQuerySpaceUid() {
return compositeQuerySpace.getUid();
@ -117,14 +144,23 @@ public abstract class AbstractCompositeFetch implements CompositeFetch, Expandin
loadPlanBuildingContext.getQuerySpaces().generateImplicitUid(),
attributeDefinition.isNullable()
);
final EntityFetch fetch = new EntityFetchImpl(
final EntityFetch fetch = createEntityFetch( attributeDefinition, fetchStrategy, join, loadPlanBuildingContext );
addFetch( fetch );
return fetch;
}
protected EntityFetch createEntityFetch(
AssociationAttributeDefinition attributeDefinition,
FetchStrategy fetchStrategy,
Join fetchedJoin,
LoadPlanBuildingContext loadPlanBuildingContext) {
return new EntityFetchImpl(
this,
attributeDefinition,
fetchStrategy,
join
fetchedJoin
);
addFetch( fetch );
return fetch;
}
private void addFetch(Fetch fetch) {

View File

@ -116,6 +116,11 @@ public abstract class AbstractEntityReference implements EntityReference, Expand
);
}
@Override
public EntityReference resolveEntityReference() {
return this;
}
protected EntityQuerySpace getEntityQuerySpace() {
return entityQuerySpace;
}

View File

@ -24,6 +24,7 @@
package org.hibernate.loader.plan2.build.internal.returns;
import org.hibernate.loader.PropertyPath;
import org.hibernate.loader.plan2.build.internal.spaces.CollectionQuerySpaceImpl;
import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.persister.walking.spi.CollectionDefinition;
@ -35,7 +36,7 @@ public class CollectionReturnImpl extends AbstractCollectionReference implements
public CollectionReturnImpl(CollectionDefinition collectionDefinition, LoadPlanBuildingContext context) {
super(
context.getQuerySpaces().makeCollectionQuerySpace(
(CollectionQuerySpaceImpl) context.getQuerySpaces().makeCollectionQuerySpace(
context.getQuerySpaces().generateImplicitUid(),
collectionDefinition.getCollectionPersister()
),

View File

@ -54,4 +54,5 @@ public class EncapsulatedEntityIdentifierDescription
PropertyPath propertyPath) {
super( entityReference, compositeQuerySpace, compositeType, propertyPath );
}
}

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan2.build.internal.returns;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.loader.plan2.build.spi.ExpandingFetchSource;
import org.hibernate.loader.plan2.spi.BidirectionalEntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
/**
* Represents a key-many-to-one fetch that is bi-directionally join fetched.
* <p/>
* For example, consider an Order entity whose primary key is partially made up of the Customer entity to which
* it is associated. When we join fetch Customer -> Order(s) and then Order -> Customer we have a bi-directional
* fetch. This class would be used to represent the Order -> Customer part of that link.
*
* @author Steve Ebersole
*/
public class KeyManyToOneBidirectionalEntityFetchImpl extends EntityFetchImpl implements BidirectionalEntityFetch {
private final EntityReference targetEntityReference;
public KeyManyToOneBidirectionalEntityFetchImpl(
ExpandingFetchSource fetchSource,
AssociationAttributeDefinition fetchedAttribute,
FetchStrategy fetchStrategy,
Join fetchedJoin,
EntityReference targetEntityReference) {
super( fetchSource, fetchedAttribute, fetchStrategy, fetchedJoin );
this.targetEntityReference = targetEntityReference;
}
public EntityReference getTargetEntityReference() {
return targetEntityReference;
}
}

View File

@ -29,7 +29,6 @@ import org.hibernate.loader.plan2.build.spi.LoadPlanBuildingContext;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;

View File

@ -42,8 +42,8 @@ public class Helper {
public String[] determineRhsColumnNames(EntityType entityType, SessionFactoryImplementor sessionFactory) {
final Joinable persister = entityType.getAssociatedJoinable( sessionFactory );
return entityType.isReferenceToPrimaryKey()
? persister.getKeyColumnNames()
: ( (PropertyMapping) persister ).toColumns( entityType.getRHSUniqueKeyPropertyName() );
return entityType.getRHSUniqueKeyPropertyName() == null ?
persister.getKeyColumnNames() :
( (PropertyMapping) persister ).toColumns( entityType.getRHSUniqueKeyPropertyName() );
}
}

View File

@ -33,10 +33,10 @@ import org.jboss.logging.Logger;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan2.build.spi.ExpandingQuerySpaces;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.QuerySpace;
import org.hibernate.loader.plan2.spi.QuerySpaceUidNotRegisteredException;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
@ -105,7 +105,7 @@ public class QuerySpacesImpl implements ExpandingQuerySpaces {
}
@Override
public CollectionQuerySpaceImpl makeCollectionQuerySpace(String uid, CollectionPersister collectionPersister) {
public CollectionQuerySpace makeCollectionQuerySpace(String uid, CollectionPersister collectionPersister) {
if ( querySpaceByUid.containsKey( uid ) ) {
throw new IllegalStateException( "Encountered duplicate QuerySpace uid : " + uid );
}

View File

@ -75,7 +75,7 @@ import org.hibernate.type.Type;
*
* @author Steve Ebersole
*
* @see org.hibernate.loader.plan.spi.build.LoadPlanBuilderStrategy
* @see org.hibernate.loader.plan2.build.spi.LoadPlanBuildingAssociationVisitationStrategy
* @see org.hibernate.persister.walking.spi.AssociationVisitationStrategy
*/
public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
@ -126,6 +126,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
if ( FetchStackAware.class.isInstance( last ) ) {
( (FetchStackAware) last ).poppedFromStack();
}
return last;
}
@ -133,7 +134,6 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
return fetchSourceStack.peekFirst();
}
// top-level AssociationVisitationStrategy hooks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
@ -163,6 +163,8 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
return true;
}
// Entities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void startingEntity(EntityDefinition entityDefinition) {
log.tracef(
@ -552,7 +554,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
@Override
public void associationKeyRegistered(AssociationKey associationKey) {
// todo : use this information to maintain a map of AssociationKey->FetchOwner mappings (associationKey + current fetchOwner stack entry)
// todo : use this information to maintain a map of AssociationKey->FetchSource mappings (associationKey + current FetchSource stack entry)
// that mapping can then be used in #foundCircularAssociationKey to build the proper BiDirectionalEntityFetch
// based on the mapped owner
log.tracef(
@ -564,6 +566,32 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
fetchedAssociationKeySourceMap.put( associationKey, currentSource() );
}
@Override
public FetchSource registeredFetchSource(AssociationKey associationKey) {
return fetchedAssociationKeySourceMap.get( associationKey );
}
@Override
public void foundCircularAssociation(AssociationAttributeDefinition attributeDefinition) {
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) {
return; // nothing to do
}
// go ahead and build the bidirectional fetch
if ( attributeDefinition.getAssociationNature() == AssociationAttributeDefinition.AssociationNature.ENTITY ) {
currentSource().buildEntityFetch(
attributeDefinition,
fetchStrategy,
this
);
}
else {
// Collection
currentSource().buildCollectionFetch( attributeDefinition, fetchStrategy, this );
}
}
// @Override
// public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition) {
// // use this information to create the bi-directional EntityReference (as EntityFetch) instances
@ -580,7 +608,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
// final FetchSource currentFetchSource = currentSource();
// ( (ExpandingFetchSource) currentFetchSource ).addCircularFetch( new CircularFetch( ))
//
// currentFetchOwner().addFetch( new CircularFetch( currentSource(), fetchOwner, attributeDefinition ) );
// currentFetchOwner().addFetch( new CircularFetch( currentSource(), fetchSource, attributeDefinition ) );
// }
//
// public static class CircularFetch implements EntityFetch, EntityReference {
@ -658,7 +686,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
// }
//
// @Override
// public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchOwnerCopy) {
// public Fetch makeCopy(CopyContext copyContext, FetchOwner fetchSourceCopy) {
// // todo : will need this implemented
// return null;
// }
@ -696,7 +724,7 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
// 2) (because the fetch cannot be a JOIN...) do not push it to the stack
final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition );
// final FetchOwner fetchOwner = currentFetchOwner();
// final FetchOwner fetchSource = currentFetchOwner();
// fetchOwner.validateFetchPlan( fetchStrategy, attributeDefinition );
//
// fetchOwner.buildAnyFetch(
@ -745,7 +773,6 @@ public abstract class AbstractLoadPlanBuildingAssociationVisitationStrategy
CollectionFetch fetch = currentSource.buildCollectionFetch( attributeDefinition, fetchStrategy, this );
pushToCollectionStack( fetch );
}
return true;
}

View File

@ -28,7 +28,7 @@ import java.util.Collections;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan.spi.AbstractPlanNode;
import org.hibernate.loader.plan2.spi.AbstractPlanNode;
import org.hibernate.loader.plan2.build.internal.spaces.QuerySpacesImpl;
import org.hibernate.loader.plan2.spi.Join;
import org.hibernate.loader.plan2.spi.QuerySpace;

View File

@ -23,7 +23,7 @@
*/
package org.hibernate.loader.plan2.build.spi;
import org.hibernate.loader.plan2.build.internal.spaces.CollectionQuerySpaceImpl;
import org.hibernate.loader.plan2.spi.CollectionQuerySpace;
import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.persister.collection.CollectionPersister;
@ -37,5 +37,5 @@ public interface ExpandingQuerySpaces extends QuerySpaces {
public EntityQuerySpace makeEntityQuerySpace(String uid, EntityPersister entityPersister);
public CollectionQuerySpaceImpl makeCollectionQuerySpace(String uid, CollectionPersister collectionPersister);
public CollectionQuerySpace makeCollectionQuerySpace(String uid, CollectionPersister collectionPersister);
}

View File

@ -28,7 +28,7 @@ import org.hibernate.persister.walking.spi.AssociationVisitationStrategy;
/**
* Specialized {@link org.hibernate.persister.walking.spi.AssociationVisitationStrategy} implementation for
* building {@link org.hibernate.loader.plan.spi.LoadPlan} instances.
* building {@link org.hibernate.loader.plan2.spi.LoadPlan} instances.
*
* @author Steve Ebersole
*/

View File

@ -24,7 +24,9 @@
package org.hibernate.loader.plan2.build.spi;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.persister.walking.spi.AssociationKey;
/**
* Provides access to context needed in building a LoadPlan.
@ -40,4 +42,6 @@ public interface LoadPlanBuildingContext {
public SessionFactoryImplementor getSessionFactory();
public ExpandingQuerySpaces getQuerySpaces();
public FetchSource registeredFetchSource(AssociationKey associationKey);
}

View File

@ -30,7 +30,7 @@ import java.io.PrintWriter;
import org.jboss.logging.Logger;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan2.exec.internal.AliasResolutionContextImpl;
import org.hibernate.loader.plan2.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.LoadPlan;
@ -57,7 +57,7 @@ public class LoadPlanTreePrinter {
return toString( loadPlan, null );
}
private String toString(LoadPlan loadPlan, AliasResolutionContextImpl aliasResolutionContext) {
private String toString(LoadPlan loadPlan, AliasResolutionContext aliasResolutionContext) {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
final PrintStream printStream = new PrintStream( byteArrayOutputStream );
final PrintWriter printWriter = new PrintWriter( printStream );
@ -70,7 +70,7 @@ public class LoadPlanTreePrinter {
return new String( byteArrayOutputStream.toByteArray() );
}
public void logTree(LoadPlan loadPlan, AliasResolutionContextImpl aliasResolutionContext) {
public void logTree(LoadPlan loadPlan, AliasResolutionContext aliasResolutionContext) {
if ( ! log.isDebugEnabled() ) {
return;
}
@ -88,7 +88,7 @@ public class LoadPlanTreePrinter {
private void logTree(
LoadPlan loadPlan,
AliasResolutionContextImpl aliasResolutionContext,
AliasResolutionContext aliasResolutionContext,
PrintWriter printWriter) {
printWriter.println( "LoadPlan(" + extractDetails( loadPlan ) + ")" );
printWriter.println( TreePrinterHelper.INSTANCE.generateNodePrefix( 1 ) + "Returns" );

View File

@ -29,7 +29,6 @@ import java.io.PrintWriter;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan2.build.internal.spaces.CollectionQuerySpaceImpl;
import org.hibernate.loader.plan2.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan2.exec.spi.CollectionReferenceAliases;
import org.hibernate.loader.plan2.exec.spi.EntityReferenceAliases;

View File

@ -88,7 +88,7 @@ public class AliasResolutionContextImpl implements AliasResolutionContext {
* non-query (HQL, criteria, etc) contexts.
* <p/>
* See the notes on
* {@link org.hibernate.loader.plan.exec.spi.AliasResolutionContext#getSourceAlias} for discussion of
* {@link org.hibernate.loader.plan2.exec.spi.AliasResolutionContext#getSourceAlias} for discussion of
* "source aliases". They are not implemented here yet.
*
* @param sessionFactory The session factory

View File

@ -140,7 +140,7 @@ public class CollectionReferenceInitializerImpl implements CollectionReferenceIn
collectionRowKey,
collectionReference.getCollectionPersister()
);
// todo : try org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.getOwnerProcessingState() ??
// todo : try org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext.getOwnerProcessingState() ??
// -- specifically to return its ResultSetProcessingContext.EntityReferenceProcessingState#getEntityInstance()
if ( collectionOwner == null ) {
//TODO: This is assertion is disabled because there is a bug that means the

View File

@ -79,27 +79,16 @@ public class EntityReferenceInitializerImpl implements EntityReferenceInitialize
isReturn = isRoot;
}
@Override
public EntityReference getEntityReference() {
return entityReference;
}
@Override
public void hydrateIdentifier(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
// if the entity reference we are hydrating is a Return, it is possible that its EntityKey is
// supplied by the QueryParameter optional entity information
if ( context.shouldUseOptionalEntityInformation() ) {
if ( isReturn ) {
final EntityKey entityKey = ResultSetProcessorHelper.getOptionalObjectKey(
context.getQueryParameters(),
context.getSession()
);
if ( entityKey != null ) {
processingState.registerEntityKey( entityKey );
return;
}
}
}
// get any previously registered identifier hydrated-state
Object identifierHydratedForm = processingState.getIdentifierHydratedForm();
if ( identifierHydratedForm == null ) {
@ -144,7 +133,6 @@ public class EntityReferenceInitializerImpl implements EntityReferenceInitialize
@Override
public void resolveEntityKey(ResultSet resultSet, ResultSetProcessingContextImpl context) {
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
// see if we already have an EntityKey associated with this EntityReference in the processing state.

View File

@ -48,7 +48,7 @@ public class EntityReturnReader implements ReturnReader {
this.aliases = aliases;
}
private EntityReferenceProcessingState getIdentifierResolutionContext(ResultSetProcessingContext context) {
public EntityReferenceProcessingState getIdentifierResolutionContext(ResultSetProcessingContext context) {
final EntityReferenceProcessingState entityReferenceProcessingState = context.getProcessingState( entityReturn );
if ( entityReferenceProcessingState == null ) {

View File

@ -35,29 +35,18 @@ import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.PostLoadEvent;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan2.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan2.exec.spi.LockModeResolver;
import org.hibernate.loader.plan2.spi.CollectionFetch;
import org.hibernate.loader.plan2.spi.CollectionReturn;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitationStrategyAdapter;
import org.hibernate.loader.plan.spi.visit.LoadPlanVisitor;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.type.EntityType;
@ -276,27 +265,7 @@ public class ResultSetProcessingContextImpl implements ResultSetProcessingContex
@Override
public EntityReferenceProcessingState getOwnerProcessingState(Fetch fetch) {
return getProcessingState( resolveFetchOwnerEntityReference( fetch ) );
}
private EntityReference resolveFetchOwnerEntityReference(Fetch fetch) {
final FetchSource fetchSource = fetch.getSource();
if ( EntityReference.class.isInstance( fetchSource ) ) {
return (EntityReference) fetchSource;
}
else if ( CompositeFetch.class.isInstance( fetchSource ) ) {
return resolveFetchOwnerEntityReference( (CompositeFetch) fetchSource );
}
throw new IllegalStateException(
String.format(
"Cannot resolve FetchOwner [%s] of Fetch [%s (%s)] to an EntityReference",
fetchSource,
fetch,
fetch.getPropertyPath()
)
);
return getProcessingState( fetch.getSource().resolveEntityReference() );
}
@Override

View File

@ -64,10 +64,10 @@ public class ResultSetProcessorHelper {
else {
entityPersister = session.getFactory().getEntityPersister( optionalEntityName );
}
if ( entityPersister.isInstance( optionalId ) && !entityPersister.getEntityMetamodel()
.getIdentifierProperty()
.isVirtual() && entityPersister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) {
// embedded (non-encapsulated) composite identifier
if ( entityPersister.isInstance( optionalId ) &&
!entityPersister.getEntityMetamodel().getIdentifierProperty().isVirtual() &&
entityPersister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) {
// non-encapsulated composite identifier
final Serializable identifierState = ((CompositeType) entityPersister.getIdentifierType()).getPropertyValues(
optionalId,
session

View File

@ -25,7 +25,9 @@ package org.hibernate.loader.plan2.exec.process.spi;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
@ -37,6 +39,13 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.loader.plan2.exec.process.internal.HydratedEntityRegistration;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan2.spi.BidirectionalEntityFetch;
import org.hibernate.loader.plan2.spi.CompositeFetch;
import org.hibernate.loader.plan2.spi.EntityFetch;
import org.hibernate.loader.plan2.spi.EntityIdentifierDescription;
import org.hibernate.loader.plan2.spi.EntityReference;
import org.hibernate.loader.plan2.spi.Fetch;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.persister.entity.Loadable;
@ -66,8 +75,18 @@ public abstract class AbstractRowReader implements RowReader {
for ( EntityReferenceInitializer entityReferenceInitializer : entityReferenceInitializers ) {
entityReferenceInitializer.hydrateIdentifier( resultSet, context );
}
final Map<EntityReference,EntityReferenceInitializer> initializerByEntityReference =
new HashMap<EntityReference, EntityReferenceInitializer>( entityReferenceInitializers.size() );
for ( EntityReferenceInitializer entityReferenceInitializerFromMap : entityReferenceInitializers ) {
initializerByEntityReference.put( entityReferenceInitializerFromMap.getEntityReference(), entityReferenceInitializerFromMap );
}
for ( EntityReferenceInitializer entityReferenceInitializer : entityReferenceInitializers ) {
entityReferenceInitializer.resolveEntityKey( resultSet, context );
resolveEntityKey(
resultSet,
context,
entityReferenceInitializer,
initializerByEntityReference
);
}
// 2) allow entity references to resolve their non-identifier hydrated state and entity instance
@ -102,6 +121,58 @@ public abstract class AbstractRowReader implements RowReader {
return logicalRow;
}
private void resolveEntityKey(
ResultSet resultSet,
ResultSetProcessingContextImpl context,
EntityReferenceInitializer entityReferenceInitializer,
Map<EntityReference,EntityReferenceInitializer> initializerByEntityReference) throws SQLException {
final EntityReference entityReference = entityReferenceInitializer.getEntityReference();
final EntityIdentifierDescription identifierDescription = entityReference.getIdentifierDescription();
if ( identifierDescription.hasFetches() ) {
resolveEntityKey( resultSet, context, (FetchSource) identifierDescription, initializerByEntityReference );
}
entityReferenceInitializer.resolveEntityKey( resultSet, context );
}
private void resolveEntityKey(
ResultSet resultSet,
ResultSetProcessingContextImpl context,
FetchSource fetchSource,
Map<EntityReference,EntityReferenceInitializer> initializerByEntityReference) throws SQLException {
for ( Fetch fetch : fetchSource.getFetches() ) {
if ( EntityFetch.class.isInstance( fetch ) ) {
final EntityFetch entityFetch = (EntityFetch) fetch;
final EntityReferenceInitializer targetEntityReferenceInitializer;
if ( BidirectionalEntityFetch.class.isInstance( fetch ) ) {
final BidirectionalEntityFetch bidirectionalEntityFetch = (BidirectionalEntityFetch) fetch;
targetEntityReferenceInitializer = initializerByEntityReference.get(
bidirectionalEntityFetch.getTargetEntityReference()
);
}
else {
targetEntityReferenceInitializer = initializerByEntityReference.get( entityFetch );
}
if ( targetEntityReferenceInitializer != null ) {
resolveEntityKey(
resultSet,
context,
targetEntityReferenceInitializer,
initializerByEntityReference
);
targetEntityReferenceInitializer.hydrateEntityState( resultSet, context );
}
}
else if ( CompositeFetch.class.isInstance( fetch ) ) {
resolveEntityKey(
resultSet,
context,
(CompositeFetch) fetch,
initializerByEntityReference );
}
}
}
@Override
public void finishUp(ResultSetProcessingContextImpl context, List<AfterLoadAction> afterLoadActionList) {
final List<HydratedEntityRegistration> hydratedEntityRegistrations = context.getHydratedEntityRegistrationList();

View File

@ -27,11 +27,14 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan2.spi.EntityReference;
/**
* @author Steve Ebersole
*/
public interface EntityReferenceInitializer {
EntityReference getEntityReference();
void hydrateIdentifier(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException;
void resolveEntityKey(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException;

View File

@ -31,6 +31,9 @@ import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan2.build.spi.LoadPlanTreePrinter;
@ -41,11 +44,13 @@ import org.hibernate.loader.plan2.exec.internal.LoadQueryJoinAndFetchProcessor;
import org.hibernate.loader.plan2.exec.process.internal.EntityReferenceInitializerImpl;
import org.hibernate.loader.plan2.exec.process.internal.EntityReturnReader;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessingContextImpl;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessorHelper;
import org.hibernate.loader.plan2.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan2.exec.process.spi.AbstractRowReader;
import org.hibernate.loader.plan2.exec.process.spi.CollectionReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan2.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan2.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan2.exec.process.spi.RowReader;
import org.hibernate.loader.plan2.exec.query.internal.SelectStatementBuilder;
@ -54,11 +59,14 @@ import org.hibernate.loader.plan2.spi.EntityQuerySpace;
import org.hibernate.loader.plan2.spi.EntityReturn;
import org.hibernate.loader.plan2.spi.LoadPlan;
import org.hibernate.loader.plan2.spi.QuerySpaces;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type;
/**
* Handles interpreting a LoadPlan (for loading of an entity) by:<ul>
@ -410,6 +418,47 @@ public class EntityLoadQueryDetails implements LoadQueryDetails {
: Collections.<CollectionReferenceInitializer>emptyList();
}
@Override
public Object readRow(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final ResultSetProcessingContext.EntityReferenceProcessingState processingState =
rootReturnReader.getIdentifierResolutionContext( context );
// if the entity reference we are hydrating is a Return, it is possible that its EntityKey is
// supplied by the QueryParameter optional entity information
if ( context.shouldUseOptionalEntityInformation() && context.getQueryParameters().getOptionalId() != null ) {
EntityKey entityKey = ResultSetProcessorHelper.getOptionalObjectKey(
context.getQueryParameters(),
context.getSession()
);
processingState.registerIdentifierHydratedForm( entityKey.getIdentifier() );
processingState.registerEntityKey( entityKey );
final EntityPersister entityPersister = processingState.getEntityReference().getEntityPersister();
if ( entityPersister.getIdentifierType().isComponentType() ) {
final ComponentType identifierType = (ComponentType) entityPersister.getIdentifierType();
if ( !identifierType.isEmbedded() ) {
addKeyManyToOnesToSession(
context,
identifierType,
entityKey.getIdentifier()
);
}
}
}
return super.readRow( resultSet, context );
}
private void addKeyManyToOnesToSession(ResultSetProcessingContextImpl context, ComponentType componentType, Object component ) {
for ( int i = 0 ; i < componentType.getSubtypes().length ; i++ ) {
final Type subType = componentType.getSubtypes()[ i ];
final Object subValue = componentType.getPropertyValue( component, i, context.getSession() );
if ( subType.isEntityType() ) {
( (Session) context.getSession() ).buildLockRequest( LockOptions.NONE ).lock( subValue );
}
else if ( subType.isComponentType() ) {
addKeyManyToOnesToSession( context, (ComponentType) subType, subValue );
}
}
}
@Override
protected List<EntityReferenceInitializer> getEntityReferenceInitializers() {
return entityReferenceInitializers;

View File

@ -26,7 +26,7 @@ package org.hibernate.loader.plan2.exec.spi;
import org.hibernate.loader.EntityAliases;
/**
* Aggregates the alias/suffix information in relation to an {@link org.hibernate.loader.plan.spi.EntityReference}
* Aggregates the alias/suffix information in relation to an {@link org.hibernate.loader.plan2.spi.EntityReference}
*
* todo : add a contract (interface) that can be shared by entity and collection alias info objects as lhs/rhs of a join ?
*

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.loader.plan2.spi;
/**
* Represents the circular side of a bi-directional entity fetch. Wraps a reference to an EntityReference
* as an EntityFetch. We can use the special type as a trigger in AliasResolutionContext, etc to lookup information
* based on the wrapped reference.
* <p/>
* This relies on reference lookups against the EntityReference instances, therefore this allows representation of the
* circularity but with a little protection against potential stack overflows. This is unfortunately still a cyclic
* graph. An alternative approach is to make the graph acyclic (DAG) would be to follow the process I adopted in the
* original HQL Antlr v3 work with regard to always applying an alias to the "persister reference", even where that
* meant creating a generated, unique identifier as the alias. That allows other parts of the tree to refer to the
* "persister reference" by that alias without the need for potentially cyclic graphs (think ALIAS_REF in the current
* ORM parser). Those aliases can then be mapped/catalogued against the "persister reference" for retrieval as needed.
*
* @author Steve Ebersole
*/
public interface BidirectionalEntityFetch {
/**
* Get the targeted EntityReference
*
* @return The targeted EntityReference
*/
public EntityReference getTargetEntityReference();
}

View File

@ -53,7 +53,22 @@ public interface FetchSource {
*/
public Fetch[] getFetches();
/**
* Resolve the "current" {@link EntityReference}, or null if none.
*
* If this object is an {@link EntityReference}, then this object is returned.
*
* If this object is a {@link CompositeFetch}, then the nearest {@link EntityReference}
* will be resolved from its source, if possible.
*
* If no EntityReference can be resolved, null is return.
*
* @return the "current" EntityReference or null if none.
* otherwise, if this object is also a {@link Fetch}, then
* .
* @see org.hibernate.loader.plan2.spi.Fetch#getSource().
* */
public EntityReference resolveEntityReference();
// Stuff I can hopefully remove ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -31,17 +31,17 @@ import java.util.List;
* Generally speaking there are 3 forms of load plans:<ul>
* <li>
* {@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#ENTITY_LOADER} - An entity load plan for
* handling get/load handling. This form will typically have a single return (of type {@link org.hibernate.loader.plan.spi.EntityReturn})
* handling get/load handling. This form will typically have a single return (of type {@link org.hibernate.loader.plan2.spi.EntityReturn})
* defined by {@link #getReturns()}, possibly defining fetches.
* </li>
* <li>
* {@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#COLLECTION_INITIALIZER} - A collection initializer,
* used to load the contents of a collection. This form will typically have a single return (of
* type {@link org.hibernate.loader.plan.spi.CollectionReturn}) defined by {@link #getReturns()}, possibly defining fetches
* type {@link org.hibernate.loader.plan2.spi.CollectionReturn}) defined by {@link #getReturns()}, possibly defining fetches
* </li>
* <li>
* {@link org.hibernate.loader.plan2.spi.LoadPlan.Disposition#MIXED} - A query load plan which can contain
* multiple returns of mixed type (though all implementing {@link org.hibernate.loader.plan.spi.Return}). Again, may possibly define fetches.
* multiple returns of mixed type (though all implementing {@link org.hibernate.loader.plan2.spi.Return}). Again, may possibly define fetches.
* </li>
* </ul>
* <p/>
@ -64,15 +64,15 @@ public interface LoadPlan {
/**
* Get the returns indicated by this LoadPlan.<ul>
* <li>
* A {@link Disposition#ENTITY_LOADER} LoadPlan would have just a single Return of type {@link org.hibernate.loader.plan.spi.EntityReturn}.
* A {@link Disposition#ENTITY_LOADER} LoadPlan would have just a single Return of type {@link org.hibernate.loader.plan2.spi.EntityReturn}.
* </li>
* <li>
* A {@link Disposition#COLLECTION_INITIALIZER} LoadPlan would have just a single Return of type
* {@link org.hibernate.loader.plan.spi.CollectionReturn}.
* {@link org.hibernate.loader.plan2.spi.CollectionReturn}.
* </li>
* <li>
* A {@link Disposition#MIXED} LoadPlan would contain a mix of {@link org.hibernate.loader.plan.spi.EntityReturn} and
* {@link org.hibernate.loader.plan.spi.ScalarReturn} elements, but no {@link org.hibernate.loader.plan.spi.CollectionReturn}.
* A {@link Disposition#MIXED} LoadPlan would contain a mix of {@link org.hibernate.loader.plan2.spi.EntityReturn} and
* {@link org.hibernate.loader.plan2.spi.ScalarReturn} elements, but no {@link org.hibernate.loader.plan2.spi.CollectionReturn}.
* </li>
* </ul>
*
@ -122,17 +122,17 @@ public interface LoadPlan {
public static enum Disposition {
/**
* This is an "entity loader" load plan, which describes a plan for loading one or more entity instances of
* the same entity type. There is a single return, which will be of type {@link org.hibernate.loader.plan.spi.EntityReturn}
* the same entity type. There is a single return, which will be of type {@link org.hibernate.loader.plan2.spi.EntityReturn}
*/
ENTITY_LOADER,
/**
* This is a "collection initializer" load plan, which describes a plan for loading one or more entity instances of
* the same collection type. There is a single return, which will be of type {@link org.hibernate.loader.plan.spi.CollectionReturn}
* the same collection type. There is a single return, which will be of type {@link org.hibernate.loader.plan2.spi.CollectionReturn}
*/
COLLECTION_INITIALIZER,
/**
* We have a mixed load plan, which will have one or more returns of {@link org.hibernate.loader.plan.spi.EntityReturn} and {@link org.hibernate.loader.plan.spi.ScalarReturn}
* (NOT {@link org.hibernate.loader.plan.spi.CollectionReturn}).
* We have a mixed load plan, which will have one or more returns of {@link org.hibernate.loader.plan2.spi.EntityReturn} and {@link org.hibernate.loader.plan2.spi.ScalarReturn}
* (NOT {@link org.hibernate.loader.plan2.spi.CollectionReturn}).
*/
MIXED
}

View File

@ -26,7 +26,7 @@ package org.hibernate.loader.plan2.spi;
/**
* Represents a return value in the query results. Not the same as a result (column) in the JDBC ResultSet!
* <p/>
* Return is distinctly different from a {@link org.hibernate.loader.plan.spi.Fetch} and so modeled as completely separate hierarchy.
* Return is distinctly different from a {@link org.hibernate.loader.plan2.spi.Fetch} and so modeled as completely separate hierarchy.
*
* @see ScalarReturn
* @see EntityReturn

View File

@ -23,6 +23,8 @@
*/
package org.hibernate.persister.walking.spi;
import org.hibernate.loader.plan2.spi.FetchSource;
/**
* Strategy for walking associations as defined by the Hibernate metamodel. Is essentially a callback listener for
* interesting events while walking a metamodel graph
@ -166,5 +168,6 @@ public interface AssociationVisitationStrategy {
public void foundAny(AssociationAttributeDefinition attributeDefinition, AnyMappingDefinition anyDefinition);
public void associationKeyRegistered(AssociationKey associationKey);
public void foundCircularAssociationKey(AssociationKey associationKey, AttributeDefinition attributeDefinition);
public FetchSource registeredFetchSource(AssociationKey associationKey);
public void foundCircularAssociation(AssociationAttributeDefinition attributeDefinition);
}

View File

@ -50,7 +50,7 @@ import org.hibernate.type.Type;
* calls out to the strategy the strategy then decides the semantics (literally, the meaning).
* <p/>
* The visitor will, however, stop if it sees a "duplicate" AssociationKey. In such a case, the walker would call
* {@link AssociationVisitationStrategy#foundCircularAssociationKey} and stop walking any further down that graph any
* {@link AssociationVisitationStrategy#foundCircularAssociation} and stop walking any further down that graph any
* further.
*
* @author Steve Ebersole
@ -152,17 +152,21 @@ public class MetamodelGraphWalker {
final PropertyPath subPath = currentPropertyPath.append( attributeDefinition.getName() );
log.debug( "Visiting attribute path : " + subPath.getFullPath() );
if ( attributeDefinition.getType().isAssociationType() ) {
final AssociationKey associationKey = ( (AssociationAttributeDefinition) attributeDefinition ).getAssociationKey();
final AssociationAttributeDefinition associationAttributeDefinition =
(AssociationAttributeDefinition) attributeDefinition;
final AssociationKey associationKey = associationAttributeDefinition.getAssociationKey();
if ( isDuplicateAssociationKey( associationKey ) ) {
log.debug( "Property path deemed to be circular : " + subPath.getFullPath() );
strategy.foundCircularAssociationKey( associationKey, attributeDefinition );
strategy.foundCircularAssociation( associationAttributeDefinition );
// EARLY EXIT!!!
return;
}
}
final boolean continueWalk = strategy.startingAttribute( attributeDefinition );
boolean continueWalk = strategy.startingAttribute( attributeDefinition );
if ( continueWalk ) {
final PropertyPath old = currentPropertyPath;
currentPropertyPath = subPath;

View File

@ -26,6 +26,7 @@ package org.hibernate.test.loadplans.plans;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.plan2.spi.BidirectionalEntityFetch;
import org.hibernate.test.annotations.Country;
import org.hibernate.test.annotations.collectionelement.Boy;
import org.hibernate.test.annotations.collectionelement.Matrix;
@ -225,18 +226,18 @@ public class LoadPlanStructureAssertionTest extends BaseUnitTestCase {
FetchSource.class,
cardFieldElementGraph.getIdentifierDescription()
);
assertEquals( 1, cardFieldElementGraphIdAsFetchSource.getFetches().length );
assertEquals( 2, cardFieldElementGraphIdAsFetchSource.getFetches().length );
// BidirectionalEntityFetch circularCardFetch = assertTyping(
// BidirectionalEntityFetch.class,
// fieldsElementCompositeIdFetch.getFetches()[0]
// );
// assertSame( circularCardFetch.getTargetEntityReference(), cardReturn );
BidirectionalEntityFetch circularCardFetch = assertTyping(
BidirectionalEntityFetch.class,
cardFieldElementGraphIdAsFetchSource.getFetches()[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]
cardFieldElementGraphIdAsFetchSource.getFetches()[1]
);
assertEquals( Key.class.getName(), keyFetch.getEntityPersister().getEntityName() );
}

View File

@ -24,6 +24,7 @@
package org.hibernate.test.loadplans.walking;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.loader.plan2.spi.FetchSource;
import org.hibernate.persister.walking.spi.AnyMappingDefinition;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AssociationKey;
@ -32,7 +33,6 @@ import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
import org.hibernate.persister.walking.spi.CompositeCollectionElementDefinition;
import org.hibernate.persister.walking.spi.CompositionDefinition;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.persister.walking.spi.EntityIdentifierDefinition;
@ -246,15 +246,19 @@ public class LoggingAssociationVisitationStrategy implements AssociationVisitati
}
@Override
public void foundCircularAssociationKey(
AssociationKey associationKey,
AttributeDefinition attributeDefinition) {
public FetchSource registeredFetchSource(AssociationKey associationKey) {
return null;
}
@Override
public void foundCircularAssociation(
AssociationAttributeDefinition attributeDefinition) {
System.out.println(
String.format(
"%s Handling circular association attribute (%s) : %s",
StringHelper.repeat( ">>", depth + 1 ),
attributeDefinition.toString(),
associationKey.toString()
attributeDefinition.getAssociationKey().toString()
)
);
}