All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.loader.plan.build.internal.AbstractEntityGraphVisitationStrategy Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.loader.plan.build.internal;

import java.util.Map;
import javax.persistence.Subgraph;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.graph.spi.AttributeNodeImplementor;
import org.hibernate.graph.spi.GraphImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.loader.plan.spi.EntityReturn;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.persister.walking.spi.AssociationAttributeDefinition;
import org.hibernate.persister.walking.spi.AttributeDefinition;
import org.hibernate.persister.walking.spi.CollectionElementDefinition;
import org.hibernate.persister.walking.spi.CollectionIndexDefinition;
import org.hibernate.persister.walking.spi.WalkingException;

import org.jboss.logging.Logger;

/**
 * Abstract strategy of building loadplan based on entity graph.
 *
 * The problem we're resolving here is, we have TWO trees to walk (only entity loading here):
 * 
    *
      entity metadata and its associations
    *
      entity graph and attribute nodes
    *
* * And most time, the entity graph tree is partial of entity metadata tree. * * So, the idea here is, we walk the entity metadata tree, just as how we build the static loadplan from mappings, * and we try to match the node to entity graph ( and subgraph ), if there is a match, then the attribute is fetched, * it is not, then depends on which property is used to apply this entity graph. * * @author Strong Liu * @author Brett Meyer */ public abstract class AbstractEntityGraphVisitationStrategy extends AbstractLoadPlanBuildingAssociationVisitationStrategy { private static final Logger LOG = CoreLogging.logger( AbstractEntityGraphVisitationStrategy.class ); /** * The JPA 2.1 SPEC's Entity Graph only defines _WHEN_ to load an attribute, it doesn't define _HOW_ to load it * So I'm here just making an assumption that when it is EAGER, then we use JOIN, and when it is LAZY, then we use SELECT. * * NOTE: this may be changed in the near further, though ATM I have no idea how this will be changed to :) * -- stliu */ protected static final FetchStrategy DEFAULT_EAGER = new FetchStrategy( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); protected static final FetchStrategy DEFAULT_LAZY = new FetchStrategy( FetchTiming.DELAYED, FetchStyle.SELECT ); protected final LoadQueryInfluencers loadQueryInfluencers; // Queue containing entity/sub graphs to be visited. private final Stack graphStack = new StandardStack<>(); // Queue containing attributes being visited, used eventually to determine the fetch strategy. private final Stack attributeStack = new StandardStack<>(); private EntityReturn rootEntityReturn; private final LockMode lockMode; protected AbstractEntityGraphVisitationStrategy( final SessionFactoryImplementor sessionFactory, final LoadQueryInfluencers loadQueryInfluencers, final LockMode lockMode) { super( sessionFactory ); this.loadQueryInfluencers = loadQueryInfluencers; this.lockMode = lockMode; } @Override public void start() { super.start(); graphStack.push( getRootEntityGraph() ); } @Override public void finish() { super.finish(); graphStack.pop(); //applying a little internal stack checking if ( !graphStack.isEmpty() || !attributeStack.isEmpty() ) { throw new WalkingException( "Internal stack error [" + graphStack.depth() + ", " + attributeStack.depth() + "]" ); } } /** * I'm using the NULL-OBJECT pattern here. * For attributes that don't exist in the EntityGraph, * a predefined NULL-ATTRIBUTE-NODE is pushed to the stack. * * And for a nonexistent subgraph, a predefined NULL-SUBGRAPH is pushed to the stack. * * So, whenever we start visiting an attribute, there will be an attribute node pushed to the attribute stack, * and a subgraph node pushed to the graph stack. * * when we finish visiting an attribute, these two will be popped from each stack. */ @Override public boolean startingAttribute(AttributeDefinition attributeDefinition) { AttributeNodeImplementor attributeNode = null; GraphImplementor subGraphNode = null; final GraphImplementor currentGraph = graphStack.getCurrent(); if ( currentGraph != null ) { final String attrName = attributeDefinition.getName(); attributeNode = currentGraph.findAttributeNode( attrName ); if ( attributeNode != null ) { //here we need to check if there is a subgraph (or sub key graph if it is an indexed attribute ) Map subGraphs = attributeNode.getSubgraphs(); Class javaType = attributeDefinition.getType().getReturnedClass(); if ( !subGraphs.isEmpty() && subGraphs.containsKey( javaType ) ) { subGraphNode = (GraphImplementor) subGraphs.get( javaType ); } } } attributeStack.push( attributeNode ); graphStack.push( subGraphNode ); return super.startingAttribute( attributeDefinition ); } @Override public void finishingAttribute(final AttributeDefinition attributeDefinition) { attributeStack.pop(); graphStack.pop(); super.finishingAttribute( attributeDefinition ); } @Override public void startingCollectionElements( final CollectionElementDefinition elementDefinition) { AttributeNodeImplementor attributeNode = attributeStack.getCurrent(); GraphImplementor subGraphNode = null; if ( attributeNode != null ) { Class javaType = elementDefinition.getType().getReturnedClass(); Map subGraphs = attributeNode.getSubgraphs(); if ( !subGraphs.isEmpty() && subGraphs.containsKey( javaType ) ) { subGraphNode = (GraphImplementor) subGraphs.get( javaType ); } } graphStack.push( subGraphNode ); super.startingCollectionElements( elementDefinition ); } @Override public void finishingCollectionElements( final CollectionElementDefinition elementDefinition) { super.finishingCollectionElements( elementDefinition ); graphStack.pop(); } @Override public void startingCollectionIndex(final CollectionIndexDefinition indexDefinition) { AttributeNodeImplementor attributeNode = attributeStack.getCurrent(); GraphImplementor subGraphNode = null; if ( attributeNode != null ) { Map subGraphs = attributeNode.getKeySubgraphs(); Class javaType = indexDefinition.getType().getReturnedClass(); if (!subGraphs.isEmpty() && subGraphs.containsKey(javaType)) { subGraphNode = (GraphImplementor) subGraphs.get(javaType); } } graphStack.push( subGraphNode ); super.startingCollectionIndex( indexDefinition ); } @Override public void finishingCollectionIndex(final CollectionIndexDefinition indexDefinition) { super.finishingCollectionIndex( indexDefinition ); graphStack.pop(); } @Override protected boolean supportsRootCollectionReturns() { //entity graph doesn't support root collection. return false; } @Override protected void addRootReturn(final Return rootReturn) { if ( this.rootEntityReturn != null ) { throw new HibernateException( "Root return already identified" ); } if ( !( rootReturn instanceof EntityReturn ) ) { throw new HibernateException( "Load entity graph only supports EntityReturn" ); } this.rootEntityReturn = (EntityReturn) rootReturn; } @Override protected FetchStrategy determineFetchStrategy( final AssociationAttributeDefinition attributeDefinition) { final AttributeNodeImplementor currentAttrNode = attributeStack.getCurrent(); return currentAttrNode != null ? DEFAULT_EAGER : resolveImplicitFetchStrategyFromEntityGraph( attributeDefinition ); } protected abstract FetchStrategy resolveImplicitFetchStrategyFromEntityGraph( final AssociationAttributeDefinition attributeDefinition); protected FetchStrategy adjustJoinFetchIfNeeded( AssociationAttributeDefinition attributeDefinition, FetchStrategy fetchStrategy) { if ( lockMode.greaterThan( LockMode.READ ) ) { return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT ); } final Integer maxFetchDepth = sessionFactory().getSettings().getMaximumFetchDepth(); if ( maxFetchDepth != null && currentDepth() > maxFetchDepth ) { return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT ); } if ( attributeDefinition.getType().isCollectionType() && isTooManyCollections() ) { // todo : have this revert to batch or subselect fetching once "sql gen redesign" is in place return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT ); } return fetchStrategy; } @Override public LoadPlan buildLoadPlan() { LOG.debug( "Building LoadPlan..." ); return new LoadPlanImpl( rootEntityReturn, getQuerySpaces() ); } abstract protected GraphImplementor getRootEntityGraph(); @Override public void foundCircularAssociation(AssociationAttributeDefinition attributeDefinition) { final FetchStrategy fetchStrategy = determineFetchStrategy( attributeDefinition ); if ( fetchStrategy.getStyle() != FetchStyle.JOIN ) { return; // nothing to do } // Bi-directional association & the owning side was already visited. If the current attribute node refers // to it, fetch. // ENTITY nature handled by super. final GraphImplementor currentGraph = graphStack.getCurrent(); if ( attributeDefinition.getAssociationNature() == AssociationAttributeDefinition.AssociationNature.COLLECTION && currentGraph != null && currentGraph.findAttributeNode( attributeDefinition.getName() ) != null ) { currentSource().buildCollectionAttributeFetch( attributeDefinition, fetchStrategy ); } super.foundCircularAssociation( attributeDefinition ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy