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

Hibernate托管实体ID相同状态不一致问题排查求助

同一事务内Hibernate托管实体ID相同但状态不一致问题

问题背景

我们在AWS托管的EC2英特尔实例上遇到了异常数据行为:存在ID相同但状态不同的Hibernate托管实体,该问题本地无法复现,仅能通过日志确认。

实体类定义

class History {
    private Enum status;

    @OneToMany(mappedBy = HistoryEntry_.HISTORY)
    @OrderBy("id ASC")
    @BatchSize(size = 10)
    private List<HistoryEntry> entries = new LinkedList<>();
}

// 关联系统的两个部分
class HistoryEntry {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "history_id", updatable = false, nullable = false)
    private History history;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "process_id", updatable = false, nullable = false)
    private ProcessInfo processInfo;
}

class ProcessInfo {
    @OneToMany(mappedBy = HistoryEntry_.PROCESS_INFO)
    // 仅用于事务内的确定性排序
    @OrderBy("id ASC")
    @BatchSize(size = 10)
    private List<HistoryEntry> entries = new LinkedList<>();
}

初始数据状态

History history1 = new History(Status.PENDING);
ProcessInfo processInfo1 = new ProcessInfo();
ProcessInfo processInfo2 = new ProcessInfo();
HistoryEntry entryA = new HistoryEntry(history, processInfo1);
HistoryEntry entryB = new HistoryEntry(history, processInfo1);
  • 状态为PENDING的history1包含条目列表[A, B]
  • processInfo1包含条目列表[A, B]
  • processInfo2无条目

事务内操作流程

操作全程在单个事务内执行

// 返回包含A、B两个条目的列表
List<HistoryEntry> entries = historyEntryRepo.findAllByProcessInfoAndNotArchived(processInfo1);

// 基于ID比较,得到仅包含history1的集合
Set<History> histories = entries.stream()
  .map(HistoryEntry::getHistory)
  .toSet();

// 预期仅包含一个元素
if(histories.size() > 1){
  throw new IllegalStateException();
}

History history1 = histories.iterator().next();
entityManager.lock(history1, LockModeType.OPTIMISTIC_FORCE_INCREMENT);

HistoryEntry entryC = new HistoryEntry();

entryC.setHistory(history1);
history1.getEntries().add(entryC);
history1.status = Status.COMPLETED;

entryC.setProcessInfo(processInfo2);
processInfo2.getEntries().add(entryC);

historyEntryRepo.save(entryC);

数据不一致问题

在同一事务内验证各History实体状态:

Set<History> histories1 = processInfo1.getEntries().stream()
  .map(HistoryEntry::getHistory)
  .toSet();
Set<History> histories2 = processInfo2.getEntries().stream()
  .map(HistoryEntry::getHistory)
  .toSet();

两者均返回单个结果history1,但状态字段值不一致:

  • History(id=1, status=PENDING) 不符合预期
  • History(id=1, status=COMPLETED)

临时修复方式

通过重新从仓库查询可恢复数据一致性:

historyEntryRepo.findAllByProcessInfo(processInfo1).stream()
  .map(HistoryEntry::getHistory)
  .toSet();
historyEntryRepo.findAllByProcessInfo(processInfo2).stream()
  .map(HistoryEntry::getHistory)
  .toSet();

此时结果一致且符合预期:History(id=1, status=COMPLETED)

疑问

该问题是否由缓存问题或缓存失效异常导致?


内容的提问来源于stack exchange,提问作者hotzen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 12:25:59