renaming and some legacy code fixing

This commit is contained in:
Nathan Xu 2020-03-19 23:06:44 -04:00 committed by Andrea Boriero
parent 5d1aea1897
commit 11c5a1019f
7 changed files with 60 additions and 66 deletions

View File

@ -45,6 +45,12 @@ _dynamic_ (sometimes referred to as runtime)::
HQL / JPQL::: both Hibernate and JPA Criteria queries have the ability to specify fetching, specific to said query.
entity graphs::: starting in Hibernate 4.2 (JPA 2.1), this is also an option.
[NOTE]
====
`fetch profile` and `entity graph` are mutually exclusive. When both are present, `entity graph` would take effect
and `fetch profile` would be ignored.
====
[[fetching-direct-vs-query]]
=== Direct fetching vs. entity queries
@ -77,7 +83,7 @@ include::{extrasdir}/fetching-direct-vs-query-direct-fetching-example.sql[]
----
====
The `LEFT JOIN` clause is added to the generated SQL query because this association is required to be fetched eagerly.
The `LEFT OUTER JOIN` clause is added to the generated SQL query because this association is required to be fetched eagerly.
On the other hand, if you are using an entity query that does not contain a `JOIN FETCH` directive to the `Department` association:
@ -101,7 +107,7 @@ so Hibernate requires a secondary select to ensure that the EAGER association is
[IMPORTANT]
====
If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those
which, in turn, can lead to N+1 query issues.
which, in turn, can lead to N + 1 query issue.
For this reason, you should prefer LAZY associations.
====
@ -109,7 +115,7 @@ For this reason, you should prefer LAZY associations.
[[fetching-strategies]]
=== Applying fetch strategies
Let's consider these topics as it relates to a simple domain model and a few use cases.
Let's consider these topics as it relates to a sample domain model and a few use cases.
[[fetching-strategies-domain-model-example]]
.Sample domain model
@ -188,8 +194,15 @@ In both cases, this resolves to exactly one database query to get all that infor
[[fetching-strategies-dynamic-fetching-entity-graph]]
=== Dynamic fetching via JPA entity graph
JPA 2.1 introduced entity graphs so the application developer has more control over fetch plans.
JPA 2.1 introduced ``entity graph`` so the application developer has more control over fetch plans. It has two modes to choose from:
fetch graph:::
In this case, all attributes specified in the entity graph will be treated as FetchType.EAGER, and all attributes not specified will *ALWAYS* be treated as FetchType.LAZY.
load graph:::
In this case, all attributes specified in the entity graph will be treated as FetchType.EAGER, but attributes not specified use their static mapping specification.
Below is an `fetch graph` dynamic fetching example:
[[fetching-strategies-dynamic-fetching-entity-graph-example]]
.Fetch graph example
====
@ -204,14 +217,6 @@ include::{sourcedir}/GraphFetchingTest.java[tags=fetching-strategies-dynamic-fet
----
====
[NOTE]
====
When executing a JPQL query, if an EAGER association is omitted, Hibernate will issue a secondary select for every association needed to be fetched eagerly,
which can lead to N+1 query issues.
For this reason, it's better to use LAZY associations, and only fetch them eagerly on a per-query basis.
====
An EntityGraph is the root of a "load plan" and must correspond to an EntityType.
@ -432,10 +437,10 @@ As you can see in the example above, there are only two SQL statements used to f
[TIP]
====
Without `@BatchSize`, you'd run into a N+1 query issue, so,
Without `@BatchSize`, you'd run into a N + 1 query issue, so,
instead of 2 SQL statements, there would be 10 queries needed for fetching the `Employee` child entities.
However, although `@BatchSize` is better than running into an N+1 query issue,
However, although `@BatchSize` is better than running into an N + 1 query issue,
most of the time, a DTO projection or a `JOIN FETCH` is a much better alternative since
it allows you to fetch all the required data with a single query.
====
@ -447,12 +452,11 @@ Besides the `FetchType.LAZY` or `FetchType.EAGER` JPA annotations,
you can also use the Hibernate-specific `@Fetch` annotation that accepts one of the following ``FetchMode``s:
SELECT::
The association is going to be fetched lazily using a secondary select for each individual entity,
collection, or join load.
It's equivalent to JPA `FetchType.LAZY` fetching strategy.
The association is going to be fetched using a secondary select for each individual entity,
collection, or join load. This mode can be used for either `FetchType.EAGER` or `FetchType.LAZY`.
JOIN::
Use an outer join to load the related entities, collections or joins when using direct fetching.
It's equivalent to JPA `FetchType.EAGER` fetching strategy.
Use an outer join to load the related entities, collections or joins when using direct fetching. This mode
can only be used for `FetchType.EAGER`.
SUBSELECT::
Available for collections only.When accessing a non-initialized collection,
this fetch mode will trigger loading all elements of all collections of the same role for all owners associated
@ -491,7 +495,7 @@ include::{extrasdir}/fetching-strategies-fetch-mode-select-example.sql[]
====
The more `Department` entities are fetched by the first query, the more secondary `SELECT` statements are executed to initialize the `employees` collections.
Therefore, `FetchMode.SELECT` can lead to N+1 query issues.
Therefore, `FetchMode.SELECT` can lead to N + 1 query issue.
[[fetching-fetchmode-subselect]]
=== `FetchMode.SUBSELECT`
@ -511,7 +515,7 @@ include::{sourcedir}/FetchModeSubselectTest.java[tags=fetching-strategies-fetch-
Now, we are going to fetch all `Department` entities that match a given filtering predicate
and then navigate their `employees` collections.
Hibernate is going to avoid the N+1 query issue by generating a single SQL statement to initialize all `employees` collections
Hibernate is going to avoid the N + 1 query issue by generating a single SQL statement to initialize all `employees` collections
for all `Department` entities that were previously fetched.
Instead of using passing all entity identifiers, Hibernate simply reruns the previous query that fetched the `Department` entities.
@ -643,5 +647,5 @@ include::{extrasdir}/fetching-LazyCollection-select-example.sql[]
====
Therefore, the child entities were fetched one after the other without triggering a full collection initialization.
For this reason, caution is advised since accessing all elements using `LazyCollectionOption.EXTRA` can lead to N+1 query issues.
For this reason, caution is advised since accessing all elements using `LazyCollectionOption.EXTRA` can lead to N + 1 query issue.
====

View File

@ -52,7 +52,7 @@
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.EntityGraphSemanticTraverser;
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable;
@ -60,7 +60,7 @@
import org.hibernate.sql.results.graph.collection.internal.CollectionDomainResult;
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.internal.StandardEntityGraphSemanticTraverserImpl;
import org.hibernate.sql.results.internal.StandardEntityGraphTraversalStateImpl;
import org.jboss.logging.Logger;
@ -158,7 +158,7 @@ public static SelectStatement createSubSelectFetchSelect(
private final LoadQueryInfluencers loadQueryInfluencers;
private final LockOptions lockOptions;
private final Consumer<JdbcParameter> jdbcParameterConsumer;
private final EntityGraphSemanticTraverser entityGraphSemanticTraverser;
private final EntityGraphTraversalState entityGraphTraversalState;
private int fetchDepth;
private Map<OrderByFragment, TableGroup> orderByFragments;
@ -183,10 +183,10 @@ private LoaderSelectBuilder(
if ( loadQueryInfluencers != null
&& loadQueryInfluencers.getEffectiveEntityGraph() != null
&& loadQueryInfluencers.getEffectiveEntityGraph().getSemantic() != null ) {
this.entityGraphSemanticTraverser = new StandardEntityGraphSemanticTraverserImpl( loadQueryInfluencers.getEffectiveEntityGraph() );
this.entityGraphTraversalState = new StandardEntityGraphTraversalStateImpl( loadQueryInfluencers.getEffectiveEntityGraph() );
}
else {
this.entityGraphSemanticTraverser = null;
this.entityGraphTraversalState = null;
}
this.lockOptions = lockOptions != null ? lockOptions : LockOptions.NONE;
this.jdbcParameterConsumer = jdbcParameterConsumer;
@ -427,13 +427,13 @@ private BiConsumer<Fetchable, Boolean> createFetchableBiConsumer(
FetchTiming fetchTiming = fetchable.getMappedFetchStrategy().getTiming();
boolean joined = fetchable.getMappedFetchStrategy().getStyle() == FetchStyle.JOIN;
EntityGraphSemanticTraverser.Result result = null;
EntityGraphTraversalState.TraversalResult traversalResult = null;
// 'entity graph' takes precedence over 'fetch profile'
if ( entityGraphSemanticTraverser != null) {
result = entityGraphSemanticTraverser.traverse( fetchParent, fetchable, isKeyFetchable );
fetchTiming = result.getFetchStrategy();
joined = result.isJoined();
if ( entityGraphTraversalState != null) {
traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable );
fetchTiming = traversalResult.getFetchStrategy();
joined = traversalResult.isJoined();
}
else if ( loadQueryInfluencers.hasEnabledFetchProfiles() ) {
if ( fetchParent instanceof EntityResultGraphNode ) {
@ -491,8 +491,8 @@ else if ( fetchDepth > maximumFetchDepth ) {
if ( !( fetchable instanceof BasicValuedModelPart ) ) {
fetchDepth--;
}
if ( entityGraphSemanticTraverser != null ) {
entityGraphSemanticTraverser.backtrack( result.getPreviousContext() );
if ( entityGraphTraversalState != null ) {
entityGraphTraversalState.backtrack( traversalResult.getPreviousContext() );
}
}
};

View File

@ -382,14 +382,6 @@ public Fetch generateFetch(
creationState.getSqlAstCreationState().getSqlExpressionResolver(),
creationState.getSqlAstCreationState().getCreationContext()
);
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
sqlAstCreationState.getFromClauseAccess().registerTableGroup(
fetchablePath,
tableGroupJoin.getJoinedGroup()
);
return tableGroupJoin.getJoinedGroup();
}
);

View File

@ -61,13 +61,13 @@
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.EntityGraphSemanticTraverser;
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
import org.hibernate.sql.results.graph.instantiation.internal.DynamicInstantiation;
import org.hibernate.sql.results.internal.StandardEntityGraphSemanticTraverserImpl;
import org.hibernate.sql.results.internal.StandardEntityGraphTraversalStateImpl;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
@ -85,7 +85,7 @@ public class StandardSqmSelectTranslator
// prepare for 10 root selections to avoid list growth in most cases
private final List<DomainResult> domainResults = CollectionHelper.arrayList( 10 );
private final EntityGraphSemanticTraverser entityGraphSemanticTraverser;
private final EntityGraphTraversalState entityGraphTraversalState;
private int fetchDepth;
@ -101,10 +101,10 @@ public StandardSqmSelectTranslator(
if ( fetchInfluencers != null
&& fetchInfluencers.getEffectiveEntityGraph() != null
&& fetchInfluencers.getEffectiveEntityGraph().getSemantic() != null ) {
this.entityGraphSemanticTraverser = new StandardEntityGraphSemanticTraverserImpl( fetchInfluencers.getEffectiveEntityGraph() );
this.entityGraphTraversalState = new StandardEntityGraphTraversalStateImpl( fetchInfluencers.getEffectiveEntityGraph() );
}
else {
this.entityGraphSemanticTraverser = null;
this.entityGraphTraversalState = null;
}
}
@ -284,7 +284,7 @@ private Fetch buildFetch(NavigablePath fetchablePath, FetchParent fetchParent, F
FetchTiming fetchTiming = fetchable.getMappedFetchStrategy().getTiming();
boolean joined = false;
EntityGraphSemanticTraverser.Result result = null;
EntityGraphTraversalState.TraversalResult traversalResult = null;
final SqmAttributeJoin fetchedJoin = getFromClauseIndex().findFetchedJoinByPath( fetchablePath );
@ -306,10 +306,10 @@ private Fetch buildFetch(NavigablePath fetchablePath, FetchParent fetchParent, F
// there was not an explicit fetch in the SQM
alias = null;
if ( entityGraphSemanticTraverser != null ) {
result = entityGraphSemanticTraverser.traverse( fetchParent, fetchable, isKeyFetchable );
fetchTiming = result.getFetchStrategy();
joined = result.isJoined();
if ( entityGraphTraversalState != null ) {
traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable );
fetchTiming = traversalResult.getFetchStrategy();
joined = traversalResult.isJoined();
}
else if ( fetchInfluencers.hasEnabledFetchProfiles() ) {
if ( fetchParent instanceof EntityResultGraphNode ) {
@ -366,7 +366,6 @@ else if ( fetchDepth > maxDepth ) {
getSqlExpressionResolver(),
getCreationContext()
);
lhs.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup();
}
);
@ -416,8 +415,8 @@ else if ( fetchDepth > maxDepth ) {
);
}
finally {
if ( entityGraphSemanticTraverser != null && result != null ) {
entityGraphSemanticTraverser.backtrack( result.getPreviousContext() );
if ( entityGraphTraversalState != null && traversalResult != null ) {
entityGraphTraversalState.backtrack( traversalResult.getPreviousContext() );
}
}
}

View File

@ -12,23 +12,23 @@
/**
* @author Nathan Xu
*/
public interface EntityGraphSemanticTraverser {
public interface EntityGraphTraversalState {
/**
* POJO class to store the result of applied entity graph traversal, including
* <ul>
* <li>previous entity graph node so later on traverser can backtrack to it</li>
* <li>previous entity graph node so later on {@link EntityGraphTraversalState} object can backtrack to it</li>
* <li>whether the new graph node should be eagerly loaded or not</li>
* <li>whether the new graph node fetching is joined</li>
* </ul>
*/
class Result {
class TraversalResult {
private GraphImplementor previousContext;
private FetchTiming fetchTiming;
private boolean joined;
public Result(GraphImplementor previousContext, FetchTiming fetchTiming, boolean joined) {
public TraversalResult(GraphImplementor previousContext, FetchTiming fetchTiming, boolean joined) {
this.previousContext = previousContext;
this.fetchTiming = fetchTiming;
this.joined = joined;
@ -64,5 +64,5 @@ public boolean isJoined() {
* @param exploreKeySubgraph true if only key sub graph is explored; false if key sub graph is excluded
* @return traversal result; never be null
*/
Result traverse(FetchParent parent, Fetchable fetchable, boolean exploreKeySubgraph);
TraversalResult traverse(FetchParent parent, Fetchable fetchable, boolean exploreKeySubgraph);
}

View File

@ -68,7 +68,6 @@ public EmbeddableFetchImpl(
creationState.getSqlAstCreationState().getSqlExpressionResolver(),
creationState.getSqlAstCreationState().getCreationContext()
);
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup();
}

View File

@ -21,7 +21,7 @@
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.sql.results.graph.EntityGraphSemanticTraverser;
import org.hibernate.sql.results.graph.EntityGraphTraversalState;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
@ -29,12 +29,12 @@
/**
* @author Nathan Xu
*/
public class StandardEntityGraphSemanticTraverserImpl implements EntityGraphSemanticTraverser {
public class StandardEntityGraphTraversalStateImpl implements EntityGraphTraversalState {
private final GraphSemantic graphSemantic;
private GraphImplementor currentGraphContext;
public StandardEntityGraphSemanticTraverserImpl(EffectiveEntityGraph effectiveEntityGraph) {
public StandardEntityGraphTraversalStateImpl(EffectiveEntityGraph effectiveEntityGraph) {
assert effectiveEntityGraph != null;
if ( effectiveEntityGraph.getSemantic() == null ) {
throw new IllegalArgumentException( "The graph has not defined semantic: " + effectiveEntityGraph );
@ -49,7 +49,7 @@ public void backtrack(GraphImplementor previousContext) {
}
@Override
public Result traverse(FetchParent fetchParent, Fetchable fetchable, boolean exploreKeySubgraph) {
public TraversalResult traverse(FetchParent fetchParent, Fetchable fetchable, boolean exploreKeySubgraph) {
final GraphImplementor previousContextRoot = currentGraphContext;
AttributeNodeImplementor attributeNode = null;
if ( appliesTo( fetchParent ) ) {
@ -101,7 +101,7 @@ assert exploreKeySubgraph && isJpaMapCollectionType( pluralAttributeMapping )
joined = fetchable.getMappedFetchStrategy().getStyle() == FetchStyle.JOIN;
}
}
return new Result( previousContextRoot, fetchTiming, joined );
return new TraversalResult( previousContextRoot, fetchTiming, joined );
}
private Class<?> getEntityCollectionPartJavaClass(CollectionPart collectionPart) {