mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-27 22:39:13 +00:00
renaming and some legacy code fixing
This commit is contained in:
parent
5d1aea1897
commit
11c5a1019f
@ -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.
|
||||
====
|
||||
|
@ -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() );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
);
|
||||
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -68,7 +68,6 @@ public EmbeddableFetchImpl(
|
||||
creationState.getSqlAstCreationState().getSqlExpressionResolver(),
|
||||
creationState.getSqlAstCreationState().getCreationContext()
|
||||
);
|
||||
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
|
||||
return tableGroupJoin.getJoinedGroup();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
Loading…
x
Reference in New Issue
Block a user