HHH-8276 - Integrate LoadPlans into UniqueEntityLoader (PoC)
This commit is contained in:
parent
438dd9c180
commit
4defc8a5d6
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -116,6 +116,11 @@ public abstract class AbstractEntityReference implements EntityReference, Expand
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityReference resolveEntityReference() {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected EntityQuerySpace getEntityQuerySpace() {
|
||||
return entityQuerySpace;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
),
|
||||
|
|
|
@ -54,4 +54,5 @@ public class EncapsulatedEntityIdentifierDescription
|
|||
PropertyPath propertyPath) {
|
||||
super( entityReference, compositeQuerySpace, compositeType, propertyPath );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ?
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue