HHH-7841 - Redesign Loader
This commit is contained in:
parent
cbfa233ea1
commit
fafce001e7
|
@ -26,6 +26,7 @@ package org.hibernate.loader.plan.internal;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
import org.hibernate.loader.plan.spi.Return;
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
|
|
||||||
|
@ -47,6 +48,10 @@ public class LoadPlanImpl implements LoadPlan {
|
||||||
this( hasScalars, Collections.singletonList( rootReturn ) );
|
this( hasScalars, Collections.singletonList( rootReturn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoadPlanImpl(EntityReturn entityReturn) {
|
||||||
|
this( false, entityReturn );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAnyScalarReturns() {
|
public boolean hasAnyScalarReturns() {
|
||||||
return hasScalars;
|
return hasScalars;
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.jpa.HibernateEntityManagerFactory;
|
import org.hibernate.jpa.HibernateEntityManagerFactory;
|
||||||
|
import org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
|
||||||
import org.hibernate.jpa.graph.spi.GraphNodeImplementor;
|
import org.hibernate.jpa.graph.spi.GraphNodeImplementor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,7 +50,7 @@ public abstract class AbstractGraphNode<T> implements GraphNodeImplementor {
|
||||||
private final HibernateEntityManagerFactory entityManagerFactory;
|
private final HibernateEntityManagerFactory entityManagerFactory;
|
||||||
private final boolean mutable;
|
private final boolean mutable;
|
||||||
|
|
||||||
private Map<String, AttributeNode<?>> attributeNodeMap;
|
private Map<String, AttributeNodeImplementor<?>> attributeNodeMap;
|
||||||
|
|
||||||
protected AbstractGraphNode(HibernateEntityManagerFactory entityManagerFactory, boolean mutable) {
|
protected AbstractGraphNode(HibernateEntityManagerFactory entityManagerFactory, boolean mutable) {
|
||||||
this.entityManagerFactory = entityManagerFactory;
|
this.entityManagerFactory = entityManagerFactory;
|
||||||
|
@ -62,14 +63,14 @@ public abstract class AbstractGraphNode<T> implements GraphNodeImplementor {
|
||||||
this.attributeNodeMap = makeSafeMapCopy( original.attributeNodeMap );
|
this.attributeNodeMap = makeSafeMapCopy( original.attributeNodeMap );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, AttributeNode<?>> makeSafeMapCopy(Map<String, AttributeNode<?>> attributeNodeMap) {
|
private static Map<String, AttributeNodeImplementor<?>> makeSafeMapCopy(Map<String, AttributeNodeImplementor<?>> attributeNodeMap) {
|
||||||
if ( attributeNodeMap == null ) {
|
if ( attributeNodeMap == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int properSize = CollectionHelper.determineProperSizing( attributeNodeMap );
|
final int properSize = CollectionHelper.determineProperSizing( attributeNodeMap );
|
||||||
final HashMap<String,AttributeNode<?>> copy = new HashMap<String,AttributeNode<?>>( properSize );
|
final HashMap<String,AttributeNodeImplementor<?>> copy = new HashMap<String,AttributeNodeImplementor<?>>( properSize );
|
||||||
for ( Map.Entry<String,AttributeNode<?>> attributeNodeEntry : attributeNodeMap.entrySet() ) {
|
for ( Map.Entry<String,AttributeNodeImplementor<?>> attributeNodeEntry : attributeNodeMap.entrySet() ) {
|
||||||
copy.put(
|
copy.put(
|
||||||
attributeNodeEntry.getKey(),
|
attributeNodeEntry.getKey(),
|
||||||
( ( AttributeNodeImpl ) attributeNodeEntry.getValue() ).makeImmutableCopy()
|
( ( AttributeNodeImpl ) attributeNodeEntry.getValue() ).makeImmutableCopy()
|
||||||
|
@ -83,6 +84,16 @@ public abstract class AbstractGraphNode<T> implements GraphNodeImplementor {
|
||||||
return entityManagerFactory;
|
return entityManagerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AttributeNodeImplementor<?>> attributeImplementorNodes() {
|
||||||
|
if ( attributeNodeMap == null ) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new ArrayList<AttributeNodeImplementor<?>>( attributeNodeMap.values() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AttributeNode<?>> attributeNodes() {
|
public List<AttributeNode<?>> attributeNodes() {
|
||||||
if ( attributeNodeMap == null ) {
|
if ( attributeNodeMap == null ) {
|
||||||
|
@ -120,7 +131,7 @@ public abstract class AbstractGraphNode<T> implements GraphNodeImplementor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( attributeNodeMap == null ) {
|
if ( attributeNodeMap == null ) {
|
||||||
attributeNodeMap = new HashMap<String, AttributeNode<?>>();
|
attributeNodeMap = new HashMap<String, AttributeNodeImplementor<?>>();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final AttributeNode old = attributeNodeMap.get( attributeNode.getRegistrationName() );
|
final AttributeNode old = attributeNodeMap.get( attributeNode.getRegistrationName() );
|
||||||
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.graph.internal.advisor;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import org.hibernate.jpa.graph.internal.EntityGraphImpl;
|
||||||
|
import org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
|
||||||
|
import org.hibernate.loader.plan.internal.LoadPlanImpl;
|
||||||
|
import org.hibernate.loader.plan.spi.CollectionFetch;
|
||||||
|
import org.hibernate.loader.plan.spi.CompositeFetch;
|
||||||
|
import org.hibernate.loader.plan.spi.CopyContext;
|
||||||
|
import org.hibernate.loader.plan.spi.EntityFetch;
|
||||||
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
|
import org.hibernate.loader.plan.spi.FetchOwner;
|
||||||
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
|
import org.hibernate.loader.plan.spi.Return;
|
||||||
|
import org.hibernate.loader.plan.spi.visit.ReturnGraphVisitationStrategy;
|
||||||
|
import org.hibernate.loader.plan.spi.visit.ReturnGraphVisitationStrategyAdapter;
|
||||||
|
import org.hibernate.loader.spi.LoadPlanAdvisor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
|
||||||
|
private static final Logger log = Logger.getLogger( EntityGraphBasedLoadPlanAdvisor.class );
|
||||||
|
|
||||||
|
private final EntityGraphImpl root;
|
||||||
|
private final AdviceStyle adviceStyle;
|
||||||
|
|
||||||
|
public EntityGraphBasedLoadPlanAdvisor(EntityGraphImpl root, AdviceStyle adviceStyle) {
|
||||||
|
if ( root == null ) {
|
||||||
|
throw new IllegalArgumentException( "EntityGraph cannot be null" );
|
||||||
|
}
|
||||||
|
this.root = root;
|
||||||
|
this.adviceStyle = adviceStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadPlan advise(LoadPlan loadPlan) {
|
||||||
|
if ( root == null ) {
|
||||||
|
log.debug( "Skipping load plan advising: no entity graph was specified" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// for now, lets assume that the graph and the load-plan returns have to match up
|
||||||
|
EntityReturn entityReturn = findRootEntityReturn( loadPlan );
|
||||||
|
if ( entityReturn == null ) {
|
||||||
|
log.debug( "Skipping load plan advising: not able to find appropriate root entity return in load plan" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String entityName = entityReturn.getEntityPersister().getEntityName();
|
||||||
|
if ( ! root.appliesTo( entityName ) ) {
|
||||||
|
log.debugf(
|
||||||
|
"Skipping load plan advising: entity types did not match : [%s] and [%s]",
|
||||||
|
root.getEntityType().getName(),
|
||||||
|
entityName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ok to apply the advice
|
||||||
|
return applyAdvice( entityReturn );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the original load-plan
|
||||||
|
return loadPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadPlan applyAdvice(final EntityReturn entityReturn) {
|
||||||
|
final EntityReturn copy = entityReturn.makeCopy( new CopyContextImpl( entityReturn ) );
|
||||||
|
return new LoadPlanImpl( copy );
|
||||||
|
}
|
||||||
|
|
||||||
|
private EntityReturn findRootEntityReturn(LoadPlan loadPlan) {
|
||||||
|
EntityReturn rootEntityReturn = null;
|
||||||
|
for ( Return rtn : loadPlan.getReturns() ) {
|
||||||
|
if ( ! EntityReturn.class.isInstance( rtn ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( rootEntityReturn != null ) {
|
||||||
|
log.debug( "Multiple EntityReturns were found" );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootEntityReturn = (EntityReturn) rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( rootEntityReturn == null ) {
|
||||||
|
log.debug( "Unable to find root entity return in load plan" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootEntityReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum AdviceStyle {
|
||||||
|
FETCH,
|
||||||
|
LOAD
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CopyContextImpl implements CopyContext {
|
||||||
|
private final ReturnGraphVisitationStrategyImpl strategy;
|
||||||
|
|
||||||
|
public CopyContextImpl(EntityReturn entityReturn) {
|
||||||
|
strategy = new ReturnGraphVisitationStrategyImpl( entityReturn );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReturnGraphVisitationStrategy getReturnGraphVisitationStrategy() {
|
||||||
|
return strategy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReturnGraphVisitationStrategyImpl extends ReturnGraphVisitationStrategyAdapter {
|
||||||
|
private ArrayDeque<NodeDescriptor> nodeStack = new ArrayDeque<NodeDescriptor>();
|
||||||
|
|
||||||
|
public ReturnGraphVisitationStrategyImpl(EntityReturn entityReturn) {
|
||||||
|
nodeStack.addFirst( new EntityReferenceDescriptor( entityReturn, new RootEntityGraphNode( root ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingRootReturn(Return rootReturn) {
|
||||||
|
nodeStack.removeFirst();
|
||||||
|
super.finishingRootReturn( rootReturn );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingFetches(FetchOwner fetchOwner) {
|
||||||
|
nodeStack.peekFirst().applyMissingFetches();
|
||||||
|
super.finishingFetches( fetchOwner );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startingEntityFetch(EntityFetch entityFetch) {
|
||||||
|
super.startingEntityFetch( entityFetch );
|
||||||
|
|
||||||
|
final NodeDescriptor currentNode = nodeStack.peekFirst();
|
||||||
|
final String attributeName = entityFetch.getOwnerPropertyName();
|
||||||
|
final JpaGraphReference fetchedGraphReference = currentNode.attributeProcessed( attributeName );
|
||||||
|
nodeStack.addFirst( new EntityReferenceDescriptor( entityFetch, fetchedGraphReference ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingEntityFetch(EntityFetch entityFetch) {
|
||||||
|
nodeStack.removeFirst();
|
||||||
|
super.finishingEntityFetch( entityFetch );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startingCollectionFetch(CollectionFetch collectionFetch) {
|
||||||
|
super.startingCollectionFetch( collectionFetch ); //To change body of overridden methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingCollectionFetch(CollectionFetch collectionFetch) {
|
||||||
|
super.finishingCollectionFetch( collectionFetch ); //To change body of overridden methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startingCompositeFetch(CompositeFetch fetch) {
|
||||||
|
super.startingCompositeFetch( fetch ); //To change body of overridden methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishingCompositeFetch(CompositeFetch fetch) {
|
||||||
|
super.finishingCompositeFetch( fetch ); //To change body of overridden methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static interface NodeDescriptor {
|
||||||
|
public JpaGraphReference attributeProcessed(String attributeName);
|
||||||
|
|
||||||
|
public void applyMissingFetches();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static abstract class AbstractNodeDescriptor implements NodeDescriptor {
|
||||||
|
private final FetchOwner fetchOwner;
|
||||||
|
private final JpaGraphReference jpaGraphReference;
|
||||||
|
|
||||||
|
protected AbstractNodeDescriptor(FetchOwner fetchOwner, JpaGraphReference jpaGraphReference) {
|
||||||
|
this.fetchOwner = fetchOwner;
|
||||||
|
this.jpaGraphReference = jpaGraphReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JpaGraphReference attributeProcessed(String attributeName) {
|
||||||
|
if ( jpaGraphReference != null ) {
|
||||||
|
return jpaGraphReference.attributeProcessed( attributeName );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyMissingFetches() {
|
||||||
|
if ( jpaGraphReference != null ) {
|
||||||
|
jpaGraphReference.applyMissingFetches( fetchOwner );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EntityReferenceDescriptor extends AbstractNodeDescriptor {
|
||||||
|
private EntityReferenceDescriptor(EntityReturn entityReturn, JpaGraphReference correspondingJpaGraphNode) {
|
||||||
|
super( entityReturn, correspondingJpaGraphNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public EntityReferenceDescriptor(EntityFetch entityFetch, JpaGraphReference jpaGraphReference) {
|
||||||
|
super( entityFetch, jpaGraphReference );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static interface JpaGraphReference {
|
||||||
|
public JpaGraphReference attributeProcessed(String attributeName);
|
||||||
|
public void applyMissingFetches(FetchOwner fetchOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RootEntityGraphNode implements JpaGraphReference {
|
||||||
|
private final Map<String,AttributeNodeImplementor> graphAttributeMap;
|
||||||
|
|
||||||
|
private RootEntityGraphNode(EntityGraphImpl entityGraph) {
|
||||||
|
graphAttributeMap = new HashMap<String, AttributeNodeImplementor>();
|
||||||
|
|
||||||
|
final List<AttributeNodeImplementor<?>> explicitAttributeNodes = entityGraph.attributeImplementorNodes();
|
||||||
|
if ( explicitAttributeNodes != null ) {
|
||||||
|
for ( AttributeNodeImplementor node : explicitAttributeNodes ) {
|
||||||
|
graphAttributeMap.put( node.getAttributeName(), node );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JpaGraphReference attributeProcessed(String attributeName) {
|
||||||
|
final AttributeNodeImplementor attributeNode = graphAttributeMap.remove( attributeName );
|
||||||
|
|
||||||
|
if ( attributeNode == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SubGraphNode( attributeNode );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyMissingFetches(FetchOwner fetchOwner) {
|
||||||
|
for ( AttributeNodeImplementor attributeNode : graphAttributeMap.values() ) {
|
||||||
|
System.out.println( "Found unprocessed attribute node : " + attributeNode.getAttributeName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SubGraphNode implements JpaGraphReference {
|
||||||
|
protected SubGraphNode(AttributeNodeImplementor attributeNode) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JpaGraphReference attributeProcessed(String attributeName) {
|
||||||
|
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyMissingFetches(FetchOwner fetchOwner) {
|
||||||
|
//To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.jpa.graph.spi;
|
package org.hibernate.jpa.graph.spi;
|
||||||
|
|
||||||
|
import javax.persistence.AttributeNode;
|
||||||
import javax.persistence.metamodel.Attribute;
|
import javax.persistence.metamodel.Attribute;
|
||||||
|
|
||||||
import org.hibernate.jpa.HibernateEntityManagerFactory;
|
import org.hibernate.jpa.HibernateEntityManagerFactory;
|
||||||
|
@ -30,7 +31,7 @@ import org.hibernate.jpa.HibernateEntityManagerFactory;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public interface AttributeNodeImplementor<T> {
|
public interface AttributeNodeImplementor<T> extends AttributeNode<T> {
|
||||||
public HibernateEntityManagerFactory entityManagerFactory();
|
public HibernateEntityManagerFactory entityManagerFactory();
|
||||||
|
|
||||||
public Attribute<?,T> getAttribute();
|
public Attribute<?,T> getAttribute();
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.jpa.graph.spi;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import org.hibernate.jpa.graph.internal.EntityGraphImpl;
|
|
||||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
|
||||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
|
||||||
import org.hibernate.loader.plan.spi.Return;
|
|
||||||
import org.hibernate.loader.spi.LoadPlanAdvisor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
|
|
||||||
private static final Logger log = Logger.getLogger( EntityGraphBasedLoadPlanAdvisor.class );
|
|
||||||
|
|
||||||
private final EntityGraphImpl root;
|
|
||||||
|
|
||||||
public EntityGraphBasedLoadPlanAdvisor(EntityGraphImpl root) {
|
|
||||||
if ( root == null ) {
|
|
||||||
throw new IllegalArgumentException( "EntityGraph cannot be null" );
|
|
||||||
}
|
|
||||||
this.root = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoadPlan advise(LoadPlan loadPlan) {
|
|
||||||
if ( root == null ) {
|
|
||||||
log.debug( "Skipping load plan advising: no entity graph was specified" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// for now, lets assume that the graph and the load-plan returns have to match up
|
|
||||||
EntityReturn entityReturn = findRootEntityReturn( loadPlan );
|
|
||||||
if ( entityReturn == null ) {
|
|
||||||
log.debug( "Skipping load plan advising: not able to find appropriate root entity return in load plan" );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final String entityName = entityReturn.getEntityPersister().getEntityName();
|
|
||||||
if ( ! root.appliesTo( entityName ) ) {
|
|
||||||
log.debugf(
|
|
||||||
"Skipping load plan advising: entity types did not match : [%s] and [%s]",
|
|
||||||
root.getEntityType().getName(),
|
|
||||||
entityName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// ok to apply the advice
|
|
||||||
return applyAdvice( entityReturn );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the original load-plan
|
|
||||||
return loadPlan;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LoadPlan applyAdvice(final EntityReturn entityReturn) {
|
|
||||||
// final EntityReturn copy = entityReturn.makeCopy( )
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityReturn findRootEntityReturn(LoadPlan loadPlan) {
|
|
||||||
EntityReturn rootEntityReturn = null;
|
|
||||||
for ( Return rtn : loadPlan.getReturns() ) {
|
|
||||||
if ( ! EntityReturn.class.isInstance( rtn ) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( rootEntityReturn != null ) {
|
|
||||||
log.debug( "Multiple EntityReturns were found" );
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
rootEntityReturn = (EntityReturn) rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( rootEntityReturn == null ) {
|
|
||||||
log.debug( "Unable to find root entity return in load plan" );
|
|
||||||
}
|
|
||||||
|
|
||||||
return rootEntityReturn;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -35,5 +35,6 @@ import org.hibernate.jpa.HibernateEntityManagerFactory;
|
||||||
*/
|
*/
|
||||||
public interface GraphNodeImplementor {
|
public interface GraphNodeImplementor {
|
||||||
public HibernateEntityManagerFactory entityManagerFactory();
|
public HibernateEntityManagerFactory entityManagerFactory();
|
||||||
|
public List<AttributeNodeImplementor<?>> attributeImplementorNodes();
|
||||||
public List<AttributeNode<?>> attributeNodes();
|
public List<AttributeNode<?>> attributeNodes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.graphs;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EntityGraph;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Subgraph;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.jpa.graph.internal.EntityGraphImpl;
|
||||||
|
import org.hibernate.jpa.graph.internal.advisor.EntityGraphBasedLoadPlanAdvisor;
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
import org.hibernate.loader.DefaultEntityAliases;
|
||||||
|
import org.hibernate.loader.plan.internal.LoadPlanImpl;
|
||||||
|
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||||
|
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||||
|
import org.hibernate.loader.spi.LoadPlanAdvisor;
|
||||||
|
import org.hibernate.persister.entity.Loadable;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.jpa.graph.internal.advisor.EntityGraphBasedLoadPlanAdvisor.AdviceStyle;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotSame;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class BasicGraphLoadPlanAdviceTests extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
private static final String ENTIYT_NAME = "org.hibernate.jpa.test.graphs.BasicGraphLoadPlanAdviceTests$Entity1";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] { Entity1.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoAdvice() {
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadPlan buildBasicLoadPlan() {
|
||||||
|
return new LoadPlanImpl(
|
||||||
|
new EntityReturn(
|
||||||
|
sfi(),
|
||||||
|
"abc",
|
||||||
|
LockMode.NONE,
|
||||||
|
ENTIYT_NAME,
|
||||||
|
"a1",
|
||||||
|
new DefaultEntityAliases( (Loadable) sfi().getEntityPersister( ENTIYT_NAME ), "1" )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SessionFactoryImplementor sfi() {
|
||||||
|
return entityManagerFactory().unwrap( SessionFactoryImplementor.class );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicGraphBuilding() {
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
EntityGraph<Entity1> graphRoot = em.createEntityGraph( Entity1.class );
|
||||||
|
assertNull( graphRoot.getName() );
|
||||||
|
assertEquals( 0, graphRoot.getAttributeNodes().size() );
|
||||||
|
|
||||||
|
LoadPlan loadPlan = buildBasicLoadPlan();
|
||||||
|
|
||||||
|
LoadPlan advised = buildAdvisor( graphRoot, AdviceStyle.FETCH ).advise( loadPlan );
|
||||||
|
assertNotSame( advised, loadPlan );
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadPlanAdvisor buildAdvisor(EntityGraph<Entity1> graphRoot, AdviceStyle adviceStyle) {
|
||||||
|
return new EntityGraphBasedLoadPlanAdvisor( (EntityGraphImpl) graphRoot, adviceStyle );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicSubgraphBuilding() {
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
EntityGraph<Entity1> graphRoot = em.createEntityGraph( Entity1.class );
|
||||||
|
Subgraph<Entity1> parentGraph = graphRoot.addSubgraph( "parent" );
|
||||||
|
Subgraph<Entity1> childGraph = graphRoot.addSubgraph( "children" );
|
||||||
|
|
||||||
|
assertNull( graphRoot.getName() );
|
||||||
|
assertEquals( 2, graphRoot.getAttributeNodes().size() );
|
||||||
|
assertTrue(
|
||||||
|
graphRoot.getAttributeNodes().get( 0 ).getSubgraphs().containsValue( parentGraph )
|
||||||
|
|| graphRoot.getAttributeNodes().get( 0 ).getSubgraphs().containsValue( childGraph )
|
||||||
|
);
|
||||||
|
assertTrue(
|
||||||
|
graphRoot.getAttributeNodes().get( 1 ).getSubgraphs().containsValue( parentGraph )
|
||||||
|
|| graphRoot.getAttributeNodes().get( 1 ).getSubgraphs().containsValue( childGraph )
|
||||||
|
);
|
||||||
|
|
||||||
|
LoadPlan loadPlan = buildBasicLoadPlan();
|
||||||
|
|
||||||
|
LoadPlan advised = buildAdvisor( graphRoot, AdviceStyle.FETCH ).advise( loadPlan );
|
||||||
|
assertNotSame( advised, loadPlan );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "Entity1" )
|
||||||
|
public static class Entity1 {
|
||||||
|
@Id
|
||||||
|
public Integer id;
|
||||||
|
public String name;
|
||||||
|
@ManyToOne
|
||||||
|
public Entity1 parent;
|
||||||
|
@OneToMany( mappedBy = "parent" )
|
||||||
|
public Set<Entity1> children;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue