How does Hibernate invalidate Query-Cache?

There are only two hard things in Computer Science: cache invalidation and naming things.
– Phil Karlton

According to beikov in this post Hibernate ORM 6 currently stores the full data for query caches. I am really interested in how Hibernate invalidates this query cache, both in the current version 6.2 and in the future version 6.5.

Let’s say we have 2 enitites Post and Comment:

@Cache(region = "post-cache", usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "post")
public class Post implements Serializable {

    @Id
    @Column(name = "post_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long postId;

    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();
}
@Cache(region = "comment-cache", usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "comment")
public class Comment implements Serializable {

    @Id
    @Column(name = "comment_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long commentId;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "post_id")
    private Post post;
}

And then we have a Repository for Post:

public interface PostRepository extends JpaRepository<Post, Long> {

    @QueryHints(value = {
            @QueryHint(name = HibernateHints.HINT_CACHEABLE, value = "true"),
            @QueryHint(name = HibernateHints.HINT_CACHE_REGION, value = "post-query-cache")
    }, forCounting = false)
    @EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = "comments")
    @Query("""
            SELECT post
            FROM Post post
            WHERE post.postId = :postId
            """)
    Optional<Post> findByPostId(@Param("postId") Long postId);

As you can see, the query method is told to fetch the comments as well. Now, this setup works fine in Hibernate 6.2. But how does Hibernate decide when to clear/invalidate the query cache? Here my questions:

  1. Does Hibernate take the EntityGraph into account to track related entities for cache invalidation?
  2. Does Hibernate invalidate the query cache every time an existing Comment gets updated or deleted?
  3. Does Hibernate depend somehow on a specific way Comment gets updated, e.g. via dirty check, or is there no concern at all?

To summarize my concerns:

  1. Does Hibernate take care of all possibilities and can I rely on Hibernate, so that Comments in the related collection of the Post entity are always up to date when I use the query cache, even when the collection is marked FetchType.LAZY?

  2. Am I right that starting with Hibernate v6.5 all these concerns will disappear, since from this point on Hibernate will only store the related entity-id in the query cache and will then try to rehydrate the entity directly from its dedicated entity cache?

Specific question for upcomming ORM 6.5:

  1. Will I have to annotate the collection in the Post entity for proper collection caching, even when I only use query cache?

Many thanks for any clarification.
Cheers, Slevin

I am really interested in how Hibernate invalidates this query cache, both in the current version 6.2 and in the future version 6.5.

Hibernate ORM knows all the “query spaces” i.e. tables that are touched for a query. Whenever one of the tables that are involved in a query is mutated, Hibernate ORM “invalidates” all cache entries. The invalidation is very coarse, so you shouldn’t use caching if you expect a lot of changes to the underlying tables. Also see Hibernate ORM User Guide for more information.

  1. Does Hibernate take the EntityGraph into account to track related entities for cache invalidation?

Yes

  1. Does Hibernate invalidate the query cache every time an existing Comment gets updated or deleted?

Yes

  1. Does Hibernate depend somehow on a specific way Comment gets updated, e.g. via dirty check, or is there no concern at all?

No

  1. Does Hibernate take care of all possibilities and can I rely on Hibernate, so that Comments in the related collection of the Post entity are always up to date when I use the query cache, even when the collection is marked FetchType.LAZY?

Yes

  1. Am I right that starting with Hibernate v6.5 all these concerns will disappear, since from this point on Hibernate will only store the related entity-id in the query cache and will then try to rehydrate the entity directly from its dedicated entity cache?

Not sure which specific concerns you have. Can you please restate them?

  1. Will I have to annotate the collection in the Post entity for proper collection caching, even when I only use query cache?

Yes, you should do that already if you want the collection entries to be cached. Also see Hibernate ORM User Guide

1 Like

Hi beikov,

many thanks for taking the time to answer my questions.

I have 3 follow up questions, if I may:

  1. Currently it is possible to use the Query cache for caching DTO projections as well. Since ORM 6.2 stores the whole values for query caches, am I right, that in this specific case only the selected columns are stored?
  2. How will Hibernate handle these DTO cachings with ORM 6.5, since only the related entity ids get stored. Or will there be a specific option to remain on the current behavior?
  3. I have all x-to-y relations tagged with Fetch.LAZY so the only way they get fetched is via dedicated queries. Since the query cache works fine without having the entity collections annotated for caching I’m wondering, if I’m missing something here. I always thought this cache collection annotation is for entity caching only. However, will there be a difference with ORM 6.5, so that I am forced to annotate them as well? And does not annotating has any impact in terms of performance?

Many thanks again for your valuable insights.
Cheers, Slevin

  1. Currently it is possible to use the Query cache for caching DTO projections as well. Since ORM 6.2 stores the whole values for query caches, am I right, that in this specific case only the selected columns are stored?

If you select an entity to pass to a DTO constructor, then it will cache the whole entity state. With ORM 6.5 you can configure it to only cache the identifier though which will also be the default if the entity is also cached.

  1. How will Hibernate handle these DTO cachings with ORM 6.5, since only the related entity ids get stored. Or will there be a specific option to remain on the current behavior?

The default is dependent on whether the passed entity type is cacheable or not. Either way, you will have options to configure how entities should be represented in query caches.

  1. I have all x-to-y relations tagged with Fetch.LAZY so the only way they get fetched is via dedicated queries. Since the query cache works fine without having the entity collections annotated for caching I’m wondering, if I’m missing something here. I always thought this cache collection annotation is for entity caching only. However, will there be a difference with ORM 6.5, so that I am forced to annotate them as well? And does not annotating has any impact in terms of performance?

Caching collections is only useful if you expect the collection to rarely change and the collection target entity is also being cached. ORM 6.5 doesn’t change anything here. If you query cache a join fetched collection though, it will by default only store the target entity ids if the target entity is marked as cacheable i.e. this is just like selecting a cacheable entity.

1 Like

Well, at least for now I’m a couple of inches smarter - feels great ;). Many Thanks for these clear answers.