Fetching Strategy
Eager Fetching
Eagerly fetched associations or collections will be fetched when loading their parent object. Retrieving unnecessary data imposes extra load on both the database server and the application server and may also reduce the concurrency of the system, creating too many unnecessary read locks at the database level. In JPA, all toOne(@OneToOne, @ManyToOne) mappings default to eager fetching, others default to lazy fetching, while as far as Hibernate concerned, all mappings default(recommended) to lazy fetching.
Annotation: @OneToMany(fetch = FetchType.EAGER)
Actually, Hibernate offers two options in terms of eager fetching: FetchMode.JOIN and FetchMode.SELECT.
FetchMode.JOIN
In this mode, Hibernate will generate a big select with a number of outer joins to eagerly fetch associations or collections.
Annotation: @Fetch(FetchMode.JOIN)
FetchMode.SELECT
In this mode, Hibernate will generate a number of relatively small select to eagerly fetch associations or collections one by one.
Lazy Fetching
Annotation: @Fetch(FetchMode.SELECT)
For associations, lazy fetch can be divided into:
Proxy Fetching
For associations, a single-valued association is fetched when a method other than the identifier getter is invoked upon the associated object.
For parent object itself, session.load(identifier) wouldn't hit database instead just return a proxy carrying its identifier, but if you invokesession.get(1), it would hit database. Therefore, you can invoke session.get() instead of session.load() to avoid proxy generation at run-time.
No-proxy" Fetching
The association is fetched even when only the identifier is accessed. This approach requires buildtime bytecode instrumentation and is rarely necessary.
Annotation: @org.hibernate.annotations.Proxy(lazy = false)
For collections, lazy fetch can be divided into:
Lazy Collection Fetching
A collection is fetched when the application invokes an operation upon that collection.
Annotation: @ManyToOne(fetch = FetchType.LAZY)
"Extra-lazy" Collection Fetching
size(), isEmpty(), contains() methods don't trigger fetching collections in contrast to non-extra lazy collection fetching.
Annotation: @org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
N+1 Select Problem
When applying lazy fetching, it has potential to cause so-called N+1 select problem, which in short means the entire transaction requires 1 initial select that retrieves the parent objects plus N additional selects to load the children collections of each parent. It can be handled by following approaches:
Batch Fetching
If one association or collection of a parent object is accessed, go ahead and initialize the same associations or collections of several other parent objects in the same SELECT query. This turn N+1 queries into N/s + 1 queries, where s is the batch size that you can set.
Annotation: @org.hibernate.annotations.BatchSize(size = 10)
Sub-select Fetching
For batch fetching, you’d need to figure out an optimum batch size by trial. A much better optimization is sub-select fetching, which initialize the same associations or collections of ALL other parent objects in the same SELECT query. It combines the initial query and later query by using initial query as a sub-select in the where clause of later query.
Annotation: @org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
Guidelines
Normally, you would be always using lazy fetching for all associations or collections(declare this as global strategy in mapping file or annotations).
Run-Time(Code-Level) Declarations
However, sometimes in some use cases you definitely know that certain association or collection objects will be needed right after loading its parent. In such cases, you can apply run-time declarations which enables eager fetching for one query alone by calling setFetchMode() orFetchMode.JOIN when query by HQL or criteria.
Explicitly Initializing
A LazyInitializationException will be thrown by Hibernate if an uninitialized collection or proxy is accessed outside of the scope of the Session. Therefore, we need to ensure that a proxy or collection is initialized before closing the Session. If we know session is about to be closed, before that we can use Hibernate.initialize() to explicitly fetch associations or collections(Hibernate.isinitialize() can be used to test). For example, Hibernate.initialize(solution.getDataplans()) will trigger fetching all data plans of solution even if it is originally lazy fetched.
Cache
To be continued...