JPA has two levels of cache:

  • Persistence Context level cache (L1 Cache) – according to JPA specification an instance of Persistence Context can have at most one references to the instance of given entity representing a row in the database.
  • Second level cache (L2 Cache) –  this cache is shared and entries in it live longer. Internal construction of this cache can vary between JPA providers of course – there can be subtypes of this cache like entity cache, relations cache or query cache.

You car read about those in many articles, including this great article by Caren McDonald.

There are two less obvious thing related to use of L1 Cache. One thing is given straight in Caren McDonald’s article:

L1 Cache is related to Persistence Context not the transaction.

A second not so obvious thing is that when you do the following criteria query:

or do a JPA QL Query then the query will be executed against database each time it gets called. This is because you would need to use query cache and L2 cache to avoid those queries.

In order to avoid querying the database when looking for entity with given identifier more than one time in the scope of one Persistence Context you must use EntityManager find:

Shorter, more readable and more effective. Of course we could make the criteria query version shorter, use some utility methods – the point it would still execute the query. Of course in many cases it won’t matter as much, as you would have to query for given id a few times. But when designing a generic utility or some shared repository be aware of this mechanism.