You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate @Synchronize场景下find()与createQuery()查询结果不一致问题

Hibernate @Synchronize注解使用疑问:find()与createQuery()查询结果不一致问题

问题场景

定义两个实体类:Subject.java(对应SUBJECTS表)和不可变视图实体RetrieveSubject.java(通过@Subselect映射查询语句,并用@Synchronize关联TBL_SUBJECTS表)。

事务内操作流程:

  1. 修改ID为4的Subject名称为"Chemistry"
  2. 调用entityManager.find(RetrieveSubject.class,4)得到旧值"Physics"(符合find()不提前刷新的特性)
  3. 后续用createQuery查询同一ID的RetrieveSubject,仍得到旧值"Physics"(此时数据库已被Hibernate的update语句更新)
  4. 若注释掉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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:31:01