When it comes to working with an ORM tool, everybody acknowledges the importance of database design and Entity-to-Table mapping. These aspects get a lot of attention, while things like fetching strategy might be simply put-off.
In my opinion, the entity fetching strategy shouldn't ever be separated from the entity mapping design, since it might affect the overall application performance, unless properly designed.
Before Hibernate and JPA got so popular, there was a great deal of effort put into designing each query, because you had to explicitly select all the joins you wanted to select from, and all the columns you were interested in. And if that was not enough, the DBA would optimize the slow running queries.
In JPA times, the JPA-QL or HQL queries are fetching Entities along with some of their associated relationships. This eases development, as it frees us from manually choosing all table fields we are interested in, and sometimes joins or additional queries are automatically generated for serving our needs.
This is a double-edged sword. On one hand you can deliver features faster, but if your automatically generated SQL queries are not efficient, your application overall performance might suffer significantly.
So what is the entity fetching strategy, anyway?
When JPA loads an entity it also loads all the EAGER or "join fetch" associations too. As long as the persistence context is opened, navigating the LAZY associations results in fetching those as well, through additional executed queries.
By default, the JPA @ManyToOne and @OneToOne annotations are fetched EAGERly, while the @OneToMany and @ManyToMany relationships are considered LAZY. This is the default strategy, and Hibernate doesn't magically optimizes your object retrieval, it only does what is instructed to do.
While small projects don't require a thorough entity fetching planning, medium to large applications shouldn't ever ignore it.
Planning your fetching strategy from the very beginning, and adjusting it all along the development cycle isn't a "premature optimization", it's just a natural part of any ORM design.
The deafult fetch strategy is the one you define thorugh the JPA mapping, while the manual join fetching is when you use JPA-QL queries.
The best advice I can give you is to favor the manual fetching strategy (defined in JPA-QL queries using the fetch operator). While some @ManyToOne or @OneToOne associations make sense to always be fetched eagerly, most of the time, they aren't needed for every fetching operation.
For children associations it's always safer to mark them LAZY and only "join fetch" them when needed, because those can easily generate large SQL result sets, with unneeded joins.
If you enjoyed this article, I bet you are going to love my book as well.
Having most of the associations defined as LAZY requires us to use the "join fetch" JPA-QL operator and retrieve only the associations we need to fulfill a given request. If you forget to "join fetch" properly, the Persistence Context will run queries on your behalf while you navigate the lazy associations, and that might generate "N+1" problems, or additional SQL queries which might have been retrieved with a simple join in the first place.
More on my original article.