In a Grails app, I understand that you enable the 2nd level cache per domain class by adding
static mapping {
cache true
}
By default the 2nd level cache is only used when get()
is called, but it can also be used for criteria queries and dynamic finders by adding cache true
to the query.
However, I'm still not sure I understand how the query cache works. My best guess is that:
Author.findByName('bob', [cache: true])
is executed, a cache key is computed, which is based on the domain class (Author), the query (findByName) and the query parameters ('bob'). If that key is found in the Author query cache, the cached results are returned instead of executing the queryThis seems reasonable until we consider that a query that returns Book instances may join to the Author table. In that case, it would be necessary to flush both the Book and Author query caches when an Author is saved, deleted, or updated. This leads me to suspect that perhaps there is just one single query cache and it is cleared whenever any cached domain class is saved?
In the Grails docs it mentions that
As well as the ability to use Hibernate's second level cache to cache instances you can also cache collections (associations) of objects.
For example:
class Author {
static hasMany = [books: Book]
static mapping = {
cache true // Author uses the 2nd level cache
books cache: true // associated books use the 2nd level cache
}
}
class Book {
static belongsTo = [author: Author]
static mapping = {
cache true // Book uses the 2nd level cache
}
}
Does the configuration above make sense, i.e. if Author and Book are themselves using the 2nd level cache, is there any benefit to making the Author-Book association also use the 2nd level cache?
Finally, I've read this advice about using the 2nd level query cache, which suggests that it should only be used for infrequently changing domain classes. Are there any circumstances under which one should not enable the 2nd level cache for get()
operations, i.e. any reason why one wouldn't add the following to a domain class
static mapping = {
cache true // Book uses the 2nd level cache
}
Part 1:
Hibernate does the right thing. The query cache is not per entity. There is a single query cache region, shared by all queries, unless you set a specific region for a query. Each time a table is updated, its timestamp in the timestamps cache is updated. Each time a query is executed, the timestamp of each of the tables where the query searches is compared to the timestamp of the cached result. And of course, the cached result is returned only if itstimestamp is more recent than all the table timestamps.
Part 2:
Yes, it makes sense. The cache for the author remembers that the author with ID 456 has the name "foo" and the birth date 1975/07/19. Only the data stored in the author table is remembered. So, caching the association is also useful: instead of making an additional query to get the set of books of the author when calling author.getBooks()
, Hibernate will get the IDs of the books of the author from its cache, and then load each book from the second-level cache. Make sure to cache the Books, though.
Part 3:
I can imagine several reasons: