Spring Boot中Class实体懒加载缓存失效问题求助
Class实体缓存失效问题排查与解决
问题场景
Class实体属于读多写少的类型,需要实现全局缓存(包括通过UserClassRegistration懒加载调用、以及其他服务调用的场景)。该实体使用复合主键ClassPK,尝试在ClassRepository的查询方法上添加@Cacheable注解后,缓存未生效——两次调用API仍触发两次数据库查询。
相关代码
1. UserClassRegistration实体
public class UserClassRegistration{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @JoinColumn(name = "email", insertable = false, updatable = false) @ManyToOne(fetch = FetchType.LAZY) User user; @JoinColumn(name = "class_id", referencedColumnName = "id", insertable = false, updatable = false) @JoinColumn(name = "semester", referencedColumnName = "semester", insertable = false, updatable = false) @ManyToOne(fetch = FetchType.LAZY) private Class aClass; }
2. Class实体(复合主键)
public class Class { @EmbeddedId @Expose ClassPK classPK; String timetable; int maxStudent; }
3. ClassPK主键类
public class ClassPK { @Column(name = "id") String id; @Column(name = "semester") String semester; }
4. 尝试的Repository配置
public interface ClassRepository extends JpaRepository<Class,ClassPK> { @Cacheable(value = "classes") Class findByClassPK(ClassPK classPk); }
日志配置
logging.level.org.hibernate.orm.jdbc.bind=TRACE logging.level.org.springframework.cache=TRACE
解决方法
1. 给复合主键ClassPK重写equals()和hashCode()
Spring Cache依赖缓存键的equals()和hashCode()判断是否命中缓存。如果ClassPK未重写这两个方法,即使属性相同的对象也会被视为不同键,导致缓存失效。
修改ClassPK:
public class ClassPK { @Column(name = "id") String id; @Column(name = "semester") String semester; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ClassPK classPK = (ClassPK) o; return Objects.equals(id, classPK.id) && Objects.equals(semester, classPK.semester); } @Override public int hashCode() { return Objects.hash(id, semester); } // 补充getter/setter(JPA映射需要) public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSemester() { return semester; } public void setSemester(String semester) { this.semester = semester; } }
2. 启用Spring Cache全局支持
确保项目启动类或配置类上添加@EnableCaching注解,开启Spring Cache功能:
@SpringBootApplication @EnableCaching public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
3. 配置Hibernate二级缓存覆盖懒加载场景
通过UserClassRegistration懒加载aClass时,调用的是Hibernate的实体查询,默认不走Spring Cache。需配置Hibernate二级缓存来覆盖这类场景:
步骤1:添加依赖(Maven)
<dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-jcache</artifactId> </dependency>
步骤2:配置application.properties
# 开启Hibernate二级缓存 spring.jpa.properties.hibernate.cache.use_second_level_cache=true # 指定缓存提供者 spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory # 仅对标注缓存的实体启用缓存 spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE # 开启查询缓存(可选,针对JPQL/Criteria查询) spring.jpa.properties.hibernate.cache.use_query_cache=true
步骤3:给Class实体添加Hibernate缓存注解
因为Class是读多写少,选用READ_ONLY缓存策略:
import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @Cache(usage = CacheConcurrencyStrategy.READ_ONLY) public class Class { @EmbeddedId @Expose ClassPK classPK; String timetable; int maxStudent; }
4. 验证缓存生效
重启应用后再次调用API,查看日志:
- Spring Cache生效:会出现
Cache hit for key...或Cache put for key...的TRACE级日志 - Hibernate二级缓存生效:会出现
HHH000359: Optimistic cache hit之类的Hibernate日志
内容的提问来源于stack exchange,提问作者Nguyen Manh Cuong
相关产品推荐
相关产品推荐

