Hibernate @Synchronize场景下find()与createQuery()查询结果不一致问题
Hibernate @Synchronize注解使用疑问:find()与createQuery()查询结果不一致问题
问题场景
定义两个实体类:Subject.java(对应SUBJECTS表)和不可变视图实体RetrieveSubject.java(通过@Subselect映射查询语句,并用@Synchronize关联TBL_SUBJECTS表)。
事务内操作流程:
- 修改ID为4的
Subject名称为"Chemistry" - 调用
entityManager.find(RetrieveSubject.class,4)得到旧值"Physics"(符合find()不提前刷新的特性) - 后续用
createQuery查询同一ID的RetrieveSubject,仍得到旧值"Physics"(此时数据库已被Hibernate的update语句更新) - 若注释掉
entityManager.find(RetrieveSubject.class,4)这一行,createQuery能返回正确的"Chemistry"
疑问:为何同时使用find()和createQuery()与仅使用createQuery()时结果不同?
原因解析
这是Hibernate**一级缓存(Session缓存)**和@Immutable、@Synchronize注解的交互逻辑导致的:
- find()的行为:
entityManager.find()会优先从一级缓存中查找实体,缓存中没有时才从数据库加载并放入缓存。由于事务内还未执行flush,此时加载的RetrieveSubject是旧值,被存入一级缓存。 - 一级缓存的优先级:Hibernate的HQL/JPQL查询(
createQuery)默认会先检查一级缓存,匹配到已存在的实体实例时,直接返回缓存中的对象,不会重新从数据库查询——哪怕@Synchronize标记了关联表更新,也不会主动刷新缓存里的不可变实体。 - 仅用createQuery的情况:此时一级缓存中没有
RetrieveSubject实例,HQL查询会直接访问数据库;而事务内的update操作在查询前已经被Hibernate自动flush(查询需要读取最新数据时,Hibernate会触发flush同步数据库状态),所以能拿到更新后的值。 - @Immutable的影响:
RetrieveSubject被标记为@Immutable,Hibernate不会对缓存中的该实体进行更新,即使关联表数据变化,也不会主动替换缓存里的旧实例。
简单总结:find()提前把旧的RetrieveSubject放进了一级缓存,后续createQuery直接复用了缓存里的旧对象;而单独用createQuery时,缓存里没有实例,只能查数据库,此时Hibernate已经flush了update操作,所以拿到新值。
相关代码
Subject.java
package org.example.bean; import jakarta.persistence.*; import jakarta.persistence.Table; import lombok.Data; import org.hibernate.annotations.*; @Entity(name = "subject_entity") @Table(name = "SUBJECTS",uniqueConstraints = @UniqueConstraint(columnNames = {"subjectName"})) @Data @DynamicInsert @DynamicUpdate public class Subject { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) Long subjectId; String subjectName; }
RetrieveSubject.java
package org.example.bean; import jakarta.persistence.Entity; import jakarta.persistence.Id; import lombok.Getter; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Subselect; import org.hibernate.annotations.Synchronize; @Entity(name="retrievesubject") @Getter @Subselect(value="select s.subjectId as sid ,s.subjectName as subname from tbl_subjects s") @Synchronize(value={"TBL_SUBJECTS"},logical = false) @Immutable public class RetrieveSubject { @Id Long sid; String subname; }
业务代码
package org.example.service; import jakarta.persistence.*; import org.example.bean.RetrieveSubject; import org.example.bean.Subject; import java.util.List; import java.util.Scanner; public class SubjectService { public static void main(String[] args) { EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Subject"); EntityManager entityManager = entityManagerFactory.createEntityManager(); Scanner sc = new Scanner(System.in); System.out.print("Enter the number of subjects:-"); int noofSubjects = sc.nextInt(); sc.nextLine(); String subjectName=""; for(int i=1; i<=noofSubjects; i++){ System.out.print("Enter the subject name:-"); subjectName = sc.nextLine(); Subject subject = new Subject(); subject.setSubjectName(subjectName); EntityTransaction entityTransaction = entityManager.getTransaction(); entityTransaction.begin(); entityManager.persist(subject); System.out.println("Subject persisted with id:-"+subject.getSubjectId()); entityTransaction.commit(); } EntityTransaction entityTransaction = entityManager.getTransaction(); entityTransaction.begin(); Subject randomSubject = entityManager.find(Subject.class,4); System.out.println("randomSubjectName:-"+randomSubject.getSubjectName()); randomSubject.setSubjectName("Chemistry"); RetrieveSubject retrieveSubject2 = entityManager.find(RetrieveSubject.class,4);//findline System.out.println(retrieveSubject2.getSubname()); Query retrieveSubject = entityManager.createQuery("select r from retrievesubject r where r.sid=:id"); List< RetrieveSubject> retrieveSubjectList = retrieveSubject.setParameter("id",4).getResultList(); for(RetrieveSubject retrieveSubject1 : retrieveSubjectList) System.out.println(retrieveSubject1.getSid()+"----"+retrieveSubject1.getSubname()); entityTransaction.commit(); } }
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

