Configuring Caffeine or Ehcache
Dependencies
Hibernate 6 talks to cache providers through the standard JCache (JSR-107) API using the hibernate-jcache module. Pick one provider:
<!-- Hibernate's JCache integration (version managed by Spring Boot) -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jcache</artifactId>
</dependency>
<!-- Option A: Caffeine through its JCache adapter -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>jcache</artifactId>
</dependency>
<!-- Option B: Ehcache 3 (use the jakarta classifier with Hibernate 6) -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<classifier>jakarta</classifier>
</dependency>Turn It On
Configuration
Enable the L2 cache and cache only entities you mark (ENABLE_SELECTIVE):
application.yml
spring:
jpa:
properties:
hibernate:
cache:
use_second_level_cache: true
region.factory_class: jcache
generate_statistics: true # to see hits and misses while learning
jakarta.persistence.sharedCache.mode: ENABLE_SELECTIVE
cache:
jcache:
config: classpath:ehcache.xml # only for Ehcache; Caffeine can use defaultsMark the Entities
import jakarta.persistence.*;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "categories")
@Cacheable // JPA: this entity may be cached
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // Hibernate: how
public class Category {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
@OneToMany(mappedBy = "category")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // collections are cached separately (ids only)
private List<Product> products = new ArrayList<>();
}Note: A cached collection stores only the ids of its elements. If Product itself is not cached, Hibernate must load each product by id, which can make things slower. Cache the element entity too, or do not cache the collection.
Seeing It Work
Statistics Output
Load the same category in two separate requests with statistics enabled:
curl localhost:8080/api/categories/1 # request 1
curl localhost:8080/api/categories/1 # request 2Example output
Hibernate: select c1_0.id,c1_0.name from categories c1_0 where c1_0.id=?
... Session Metrics {
1 JDBC statements executed;
1 L2C puts; 0 L2C hits; 1 L2C misses;
}
... Session Metrics {
0 JDBC statements executed;
0 L2C puts; 1 L2C hits; 0 L2C misses;
}Tip: In a cluster (several app instances behind a load balancer) a local Caffeine cache in each JVM can serve stale data after another node updates the row. Use a short expiry, READ_ONLY data only, or a distributed provider (Infinispan, Hazelcast, Redis via Redisson JCache).