HHH-7841 - Redesign Loader

This commit is contained in:
Steve Ebersole 2013-04-18 12:47:18 -05:00
parent b8b9735cf5
commit 3600ffb7f2
19 changed files with 858 additions and 199 deletions

View File

@ -90,6 +90,9 @@ public class ResultSetProcessorImpl implements ResultSetProcessor {
List<AfterLoadAction> afterLoadActionList) throws SQLException {
final LoadPlan loadPlan = loadPlanAdvisor.advise( this.baseLoadPlan );
if ( loadPlan == null ) {
throw new IllegalStateException( "LoadPlanAdvisor returned null" );
}
handlePotentiallyEmptyCollectionRootReturns( loadPlan, queryParameters.getCollectionKeys(), resultSet, session );

View File

@ -46,7 +46,7 @@ public class CompositeFetch extends AbstractSingularAttributeFetch {
public CompositeFetch(
SessionFactoryImplementor sessionFactory,
String alias,
AbstractFetchOwner owner,
FetchOwner owner,
String ownerProperty) {
super( sessionFactory, alias, LockMode.NONE, owner, ownerProperty, FETCH_PLAN );
}

View File

@ -26,8 +26,24 @@ package org.hibernate.loader.spi;
import org.hibernate.loader.plan.spi.LoadPlan;
/**
* An advisor that can be made available to the {@link ResultSetProcessor} and {@link ScrollableResultSetProcessor}.
*
* The processors consult with the advisor, if one is provided, as a means to influence the load plan, meaning that
* the advisor might add fetches. A caveat is that any added fetches cannot be join fetches (they cannot alter the
* SQL); if a fetch is added as {@link org.hibernate.engine.FetchTiming#IMMEDIATE}, it must be a "subsequent form":
* {@link org.hibernate.engine.FetchStyle#SELECT}, {@link org.hibernate.engine.FetchStyle#SUBSELECT},
* {@link org.hibernate.engine.FetchStyle#BATCH}.
*
* @author Steve Ebersole
*/
public interface LoadPlanAdvisor {
/**
* Advise on the given LoadPlan, returning a new LoadPlan if any additions are needed. It is the responsibility
* of the advisor to return the original load plan if no additions were needed
*
* @param loadPlan The load plan to advise on.
*
* @return The original or advised load plan.
*/
public LoadPlan advise(LoadPlan loadPlan);
}

View File

@ -0,0 +1,81 @@
/*
* 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 org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.FetchOwner;
/**
* @author Steve Ebersole
*/
public class AdviceHelper {
private AdviceHelper() {
}
static Fetch buildFetch(FetchOwner fetchOwner, AttributeNodeImplementor attributeNode) {
if ( attributeNode.getAttribute().isAssociation() ) {
if ( attributeNode.getAttribute().isCollection() ) {
return new CollectionFetch(
(SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(),
"abc-xyz", // alias
LockMode.NONE,
fetchOwner,
new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT ),
attributeNode.getAttributeName(),
null, // sql table alias
null // entityaliases
);
}
else {
return new EntityFetch(
(SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(),
"abc-xyz", // alias
LockMode.NONE,
fetchOwner,
attributeNode.getAttributeName(),
new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.SELECT ),
null, // sql table alias
null // entityaliases
);
}
}
else {
return new CompositeFetch(
(SessionFactoryImplementor) attributeNode.entityManagerFactory().getSessionFactory(),
"abc-xyz", // alias
fetchOwner,
attributeNode.getAttributeName()
);
}
}
}

View File

@ -0,0 +1,35 @@
/*
* 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;
/**
* Links together the LoadPlan graph and JPA graph notions of the same node.
*
* @author Steve Ebersole
*/
interface AdviceNodeDescriptor {
public JpaGraphReference attributeProcessed(String attributeName);
public void applyMissingFetches();
}

View File

@ -0,0 +1,59 @@
/*
* 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 org.hibernate.loader.plan.spi.CollectionFetch;
/**
* @author Steve Ebersole
*/
public class AdviceNodeDescriptorCollectionReference implements AdviceNodeDescriptor {
private final CollectionFetch collectionFetch;
private final JpaGraphReference jpaGraphReference;
public AdviceNodeDescriptorCollectionReference(
CollectionFetch collectionFetch,
JpaGraphReference jpaGraphReference) {
//To change body of created methods use File | Settings | File Templates.
this.collectionFetch = collectionFetch;
this.jpaGraphReference = jpaGraphReference;
}
@Override
public JpaGraphReference attributeProcessed(String attributeName) {
return jpaGraphReference != null
? jpaGraphReference.attributeProcessed( attributeName )
: null;
}
@Override
public void applyMissingFetches() {
if ( jpaGraphReference == null ) {
return;
}
jpaGraphReference.applyMissingFetches( collectionFetch.getElementGraph() );
( (JpaGraphCollectionReference) jpaGraphReference ).applyMissingKeyFetches( collectionFetch.getIndexGraph() );
}
}

View File

@ -0,0 +1,54 @@
/*
* 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 org.hibernate.loader.plan.spi.CompositeFetch;
import org.hibernate.loader.plan.spi.FetchOwner;
/**
* @author Steve Ebersole
*/
public class AdviceNodeDescriptorCompositeReference implements AdviceNodeDescriptor {
private final FetchOwner fetchOwner;
private final JpaGraphReference jpaGraphReference;
AdviceNodeDescriptorCompositeReference(CompositeFetch fetchOwner, JpaGraphReference jpaGraphReference) {
this.fetchOwner = fetchOwner;
this.jpaGraphReference = jpaGraphReference;
}
@Override
public JpaGraphReference attributeProcessed(String attributeName) {
return jpaGraphReference != null
? jpaGraphReference.attributeProcessed( attributeName )
: null;
}
@Override
public void applyMissingFetches() {
if ( jpaGraphReference != null ) {
jpaGraphReference.applyMissingFetches( fetchOwner );
}
}
}

View File

@ -0,0 +1,62 @@
/*
* 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 org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.FetchOwner;
/**
* An AdviceNodeDescriptor that represents an entity reference
*
* @author Steve Ebersole
*/
class AdviceNodeDescriptorEntityReference implements AdviceNodeDescriptor {
private final FetchOwner fetchOwner;
private final JpaGraphReference jpaGraphReference;
AdviceNodeDescriptorEntityReference(EntityReturn fetchOwner, JpaGraphReference jpaGraphReference) {
this.fetchOwner = fetchOwner;
this.jpaGraphReference = jpaGraphReference;
}
AdviceNodeDescriptorEntityReference(EntityFetch fetchOwner, JpaGraphReference jpaGraphReference) {
this.fetchOwner = fetchOwner;
this.jpaGraphReference = jpaGraphReference;
}
@Override
public JpaGraphReference attributeProcessed(String attributeName) {
return jpaGraphReference != null
? jpaGraphReference.attributeProcessed( attributeName )
: null;
}
@Override
public void applyMissingFetches() {
if ( jpaGraphReference != null ) {
jpaGraphReference.applyMissingFetches( fetchOwner );
}
}
}

View File

@ -0,0 +1,39 @@
/*
* 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;
/** The style of advice. This is defined by the JPA spec. See tha values for details.
*
* @author Steve Ebersole
*/
public enum AdviceStyle {
/**
* Indicates a graph specified by the {@code javax.persistence.fetchgraph} setting.
*/
FETCH,
/**
* Indicates a graph specified by the {@code javax.persistence.loadgraph} setting.
*/
LOAD
}

View File

@ -23,29 +23,20 @@
*/
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;
/**
* A LoadPlanAdvisor implementation for applying JPA "entity graph" fetches
*
* @author Steve Ebersole
*/
public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
@ -54,6 +45,13 @@ public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
private final EntityGraphImpl root;
private final AdviceStyle adviceStyle;
/**
* Constricts a LoadPlanAdvisor for applying any additional fetches needed as indicated by the
* given entity graph.
*
* @param root The entity graph indicating the fetches.
* @param adviceStyle The style of advise (this is defikned
*/
public EntityGraphBasedLoadPlanAdvisor(EntityGraphImpl root, AdviceStyle adviceStyle) {
if ( root == null ) {
throw new IllegalArgumentException( "EntityGraph cannot be null" );
@ -62,6 +60,7 @@ public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
this.adviceStyle = adviceStyle;
}
@Override
public LoadPlan advise(LoadPlan loadPlan) {
if ( root == null ) {
log.debug( "Skipping load plan advising: no entity graph was specified" );
@ -119,16 +118,11 @@ public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
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 );
strategy = new ReturnGraphVisitationStrategyImpl( entityReturn, root );
}
@Override
@ -137,157 +131,4 @@ public class EntityGraphBasedLoadPlanAdvisor implements LoadPlanAdvisor {
}
}
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.
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
import org.hibernate.loader.plan.spi.FetchOwner;
/**
* @author Steve Ebersole
*/
class JpaGraphCollectionReference extends JpaGraphReferenceSubGraphSupport {
private final AttributeNodeImplementor<?> attributeNode;
JpaGraphCollectionReference(AttributeNodeImplementor<?> attributeNode) {
super( attributeNode );
this.attributeNode = attributeNode;
}
@Override
public void applyMissingFetches(FetchOwner fetchOwner) {
super.applyMissingFetches( fetchOwner );
// todo : additionally we need to process key graph(s)
}
void applyMissingKeyFetches(FetchOwner fetchOwner) {
// todo : additionally we need to process key graph(s)
}
}

View File

@ -0,0 +1,53 @@
/*
* 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 org.hibernate.loader.plan.spi.FetchOwner;
/**
* Describes a reference to a JPA graph. This encompasses both {@link javax.persistence.EntityGraph}
* and {@link javax.persistence.Subgraph}. Exposes the functionality needed for advising in a common way.
*
* @author Steve Ebersole
*/
interface JpaGraphReference {
/**
* Callback to let the JPA graph reference node know that the particular attribute (by name) was processed, which
* means it already was accounted for in the LoadPlan graph. For association attributes and composites, also
* returns a representation of the corresponding JPA graph node.
*
* @param attributeName The name of the attribute processed.
*
* @return The JPA graph reference corresponding to that attribute, if one.
*/
public JpaGraphReference attributeProcessed(String attributeName);
/**
* For any attributes that are defined in the JPA graph, that were not processed (as would have been indicated
* by a previous call to {@link #attributeProcessed}), apply needed fetches to the fetch owner.
*
* @param fetchOwner The owner of any generated fetches.
*/
public void applyMissingFetches(FetchOwner fetchOwner);
}

View File

@ -0,0 +1,101 @@
/*
* 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 javax.persistence.AttributeNode;
import javax.persistence.Subgraph;
import java.util.HashMap;
import java.util.Map;
import org.jboss.logging.Logger;
import org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
import org.hibernate.loader.plan.spi.FetchOwner;
/**
* @author Steve Ebersole
*/
abstract class JpaGraphReferenceSubGraphSupport implements JpaGraphReference {
private static final Logger log = Logger.getLogger( JpaGraphReferenceSubGraphSupport.class );
private final Map<String,AttributeNodeImplementor> elementGraphAttributeMap;
protected JpaGraphReferenceSubGraphSupport(AttributeNodeImplementor<?> attributeNode) {
this.elementGraphAttributeMap = new HashMap<String, AttributeNodeImplementor>();
for ( Subgraph<?> subgraph : attributeNode.getSubgraphs().values() ) {
for ( AttributeNode<?> subGraphAttributeNode : subgraph.getAttributeNodes() ) {
final AttributeNodeImplementor<?> nodeImplementor = (AttributeNodeImplementor<?>) subGraphAttributeNode;
final AttributeNodeImplementor<?> old = this.elementGraphAttributeMap.put(
nodeImplementor.getAttributeName(),
nodeImplementor
);
if ( old != null && old != nodeImplementor ) {
throw new IllegalStateException(
"Found multiple representations of the same attribute : " + nodeImplementor.getAttributeName()
);
}
}
}
}
@Override
public JpaGraphReference attributeProcessed(String attributeName) {
final AttributeNodeImplementor attributeNode = this.elementGraphAttributeMap.remove( attributeName );
if ( attributeNode == null ) {
return NoOpJpaGraphReference.INSTANCE;
}
return attributeNode.getAttribute().isCollection()
? new JpaGraphCollectionReference( attributeNode )
: new JpaGraphSingularAttributeReference( attributeNode );
}
@Override
public void applyMissingFetches(FetchOwner fetchOwner) {
for ( AttributeNodeImplementor attributeNode : elementGraphAttributeMap.values() ) {
System.out.println(
String.format(
"Found unprocessed attribute node [%s], applying to fetch-owner [%s]",
attributeNode.getAttributeName(),
fetchOwner.getPropertyPath().getFullPath()
)
);
log.tracef(
"Found unprocessed attribute node [%s], applying to fetch-owner [%s]",
attributeNode.getAttributeName(),
fetchOwner.getPropertyPath()
);
AdviceHelper.buildFetch( fetchOwner, attributeNode );
// todo : additionally we need to process any further graphs in the attribute node path
// since we are effectively at a leaf in the LoadPlan graph
}
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.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.spi.FetchOwner;
/**
* Models the root {@link javax.persistence.EntityGraph} as a JpaGraphReference
*
* @author Steve Ebersole
*/
class JpaGraphRootEntityReference implements JpaGraphReference {
private static final Logger log = Logger.getLogger( JpaGraphRootEntityReference.class );
private final Map<String,AttributeNodeImplementor> graphAttributeMap;
JpaGraphRootEntityReference(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 NoOpJpaGraphReference.INSTANCE;
}
return attributeNode.getAttribute().isCollection()
? new JpaGraphCollectionReference( attributeNode )
: new JpaGraphSingularAttributeReference( attributeNode );
}
@Override
public void applyMissingFetches(FetchOwner fetchOwner) {
for ( AttributeNodeImplementor attributeNode : graphAttributeMap.values() ) {
System.out.println(
String.format(
"Found unprocessed attribute node [%s], applying to fetch-owner [%s]",
attributeNode.getAttributeName(),
fetchOwner.getPropertyPath().getFullPath()
)
);
log.tracef(
"Found unprocessed attribute node [%s], applying to fetch-owner [%s]",
attributeNode.getAttributeName(),
fetchOwner.getPropertyPath()
);
AdviceHelper.buildFetch( fetchOwner, attributeNode );
// todo : additionally we need to process any further graphs in the attribute node path
// since we are effectively at a leaf in the LoadPlan graph
}
}
}

View File

@ -0,0 +1,35 @@
/*
* 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 org.hibernate.jpa.graph.spi.AttributeNodeImplementor;
/**
* @author Steve Ebersole
*/
class JpaGraphSingularAttributeReference extends JpaGraphReferenceSubGraphSupport {
JpaGraphSingularAttributeReference(AttributeNodeImplementor attributeNode) {
super( attributeNode );
}
}

View File

@ -0,0 +1,47 @@
/*
* 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 org.hibernate.loader.plan.spi.FetchOwner;
/**
* A no-op implementation of JpaGraphReference. Used when the LoadPlan graph already defines
* nodes beyond the scope of the JPA graph.
*
* @author Steve Ebersole
*/
class NoOpJpaGraphReference implements JpaGraphReference {
public static final NoOpJpaGraphReference INSTANCE = new NoOpJpaGraphReference();
@Override
public JpaGraphReference attributeProcessed(String attributeName) {
// its no-op, nothing to do
return INSTANCE;
}
@Override
public void applyMissingFetches(FetchOwner fetchOwner) {
// its no-op, nothing to do
}
}

View File

@ -0,0 +1,101 @@
/*
* 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 org.hibernate.jpa.graph.internal.EntityGraphImpl;
import org.hibernate.loader.plan.spi.CollectionFetch;
import org.hibernate.loader.plan.spi.CompositeFetch;
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.Return;
import org.hibernate.loader.plan.spi.visit.ReturnGraphVisitationStrategyAdapter;
/**
* The visitor strategy for visiting the return graph of the load plan being advised.
*
* @author Steve Ebersole
*/
public class ReturnGraphVisitationStrategyImpl extends ReturnGraphVisitationStrategyAdapter {
private ArrayDeque<AdviceNodeDescriptor> nodeStack = new ArrayDeque<AdviceNodeDescriptor>();
public ReturnGraphVisitationStrategyImpl(EntityReturn entityReturn, EntityGraphImpl jpaRoot) {
nodeStack.addFirst( new AdviceNodeDescriptorEntityReference( entityReturn, new JpaGraphRootEntityReference( jpaRoot ) ) );
}
@Override
public void finishingRootReturn(Return rootReturn) {
nodeStack.removeFirst();
}
@Override
public void finishingFetches(FetchOwner fetchOwner) {
nodeStack.peekFirst().applyMissingFetches();
}
@Override
public void startingEntityFetch(EntityFetch entityFetch) {
final AdviceNodeDescriptor currentNode = nodeStack.peekFirst();
final String attributeName = entityFetch.getOwnerPropertyName();
final JpaGraphReference fetchedGraphReference = currentNode.attributeProcessed( attributeName );
nodeStack.addFirst( new AdviceNodeDescriptorEntityReference( entityFetch, fetchedGraphReference ) );
}
@Override
public void finishingEntityFetch(EntityFetch entityFetch) {
nodeStack.removeFirst();
}
@Override
public void startingCollectionFetch(CollectionFetch collectionFetch) {
final AdviceNodeDescriptor currentNode = nodeStack.peekFirst();
final String attributeName = collectionFetch.getOwnerPropertyName();
final JpaGraphReference fetchedGraphReference = currentNode.attributeProcessed( attributeName );
nodeStack.addFirst( new AdviceNodeDescriptorCollectionReference( collectionFetch, fetchedGraphReference ) );
}
@Override
public void finishingCollectionFetch(CollectionFetch collectionFetch) {
nodeStack.removeFirst();
}
@Override
public void startingCompositeFetch(CompositeFetch fetch) {
final AdviceNodeDescriptor currentNode = nodeStack.peekFirst();
final String attributeName = fetch.getOwnerPropertyName();
final JpaGraphReference fetchedGraphReference = currentNode.attributeProcessed( attributeName );
nodeStack.addFirst( new AdviceNodeDescriptorCompositeReference( fetch, fetchedGraphReference ) );
}
@Override
public void finishingCompositeFetch(CompositeFetch fetch) {
nodeStack.removeFirst();
}
}

View File

@ -1,8 +1,10 @@
/*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, 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 Middleware LLC.
* 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
@ -20,6 +22,7 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.jpa.internal.metamodel;
import java.io.Serializable;
import java.lang.reflect.Member;
import javax.persistence.metamodel.SingularAttribute;
@ -86,58 +89,43 @@ public class SingularAttributeImpl<X, Y>
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isId() {
return isIdentifier;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isVersion() {
return isVersion;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isOptional() {
return isOptional;
}
/**
* {@inheritDoc}
*/
@Override
public Type<Y> getType() {
return attributeType;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAssociation() {
return false;
return getPersistentAttributeType() == PersistentAttributeType.MANY_TO_ONE
|| getPersistentAttributeType() == PersistentAttributeType.ONE_TO_ONE;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCollection() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public BindableType getBindableType() {
return BindableType.SINGULAR_ATTRIBUTE;
}
/**
* {@inheritDoc}
*/
@Override
public Class<Y> getBindableJavaType() {
return attributeType.getJavaType();
}

View File

@ -46,7 +46,7 @@ import org.hibernate.persister.entity.Loadable;
import org.junit.Test;
import static org.hibernate.jpa.graph.internal.advisor.EntityGraphBasedLoadPlanAdvisor.AdviceStyle;
import org.hibernate.jpa.graph.internal.advisor.AdviceStyle;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;