Hibernate 4升级至5后出现UnsupportedLockAttemptException锁模式不支持问题求助
解决Hibernate 5升级后的UnsupportedLockAttemptException问题
这个问题我在从Hibernate 4升级到5的时候也碰到过,核心原因是同一事务内对同一ID的实体既做了读取又做了写入操作,加上Hibernate 5对Session缓存和不可变实体的处理逻辑更严格,导致了锁模式不支持的异常。咱们一步步来解决:
首先定位问题根源
从错误栈可以看到,异常来自ImmutableEntityEntry.setLockMode,说明你的MailHistory实体要么被标记了@Immutable注解,要么Hibernate将其识别为不可变实体。当你在同一Session(事务)中:
- 先通过
getMailHistoryByReqId加载了某个reqId的MailHistory实例到Session缓存 - 之后又尝试创建同一个reqId的新
MailHistory并持久化 - DAO的
persist方法里可能调用了session.refresh(),触发了对缓存中已有实体的锁操作,但不可变实体不允许这种操作,于是抛出异常
具体解决方案
1. 检查并调整实体类的@Immutable注解
如果你的MailHistory实体类加了@Immutable:
- 如果业务上允许该实体被修改或重新持久化,直接移除
@Immutable注解即可,这是最直接的解决方式 - 如果业务必须保持实体不可变,那就要调整操作逻辑,避免同一事务内对同一ID的实体既读又写
2. 清理Session缓存,避免实体冲突
在读取完MailHistory后,立即将其从Session缓存中移除,这样后续创建新实体时就不会有缓存冲突。修改你的getMailHistoryByReqId方法:
public MailHistory getMailHistoryByReqId(String reqId) { MailHistory mh = session.get(MailHistory.class, reqId); if (mh != null) { session.evict(mh); // 将已加载的实体从Session缓存中清除 } return mh; }
这样后续执行createNewMailHistory时,Session里没有旧的实体实例,就不会触发冲突的锁操作。
3. 拆分事务,隔离读写操作
把循环内的读写逻辑放到一个新的事务中,每次处理一个reviewer时都使用独立的Session,避免跨操作的缓存干扰。调整定时任务代码:
@Transactional(readOnly = false) @Scheduled(cron = "${mail.cron.reviewer.task.reminder}") public void sendPendingReviewerTaskRemainderEmail() { // 初始化pendingTasks等逻辑... for(Reviewers reviewer: pendingTasks) { // 每个reviewer的处理都用新事务 handleSingleReviewerTask(reviewer); } } // 用REQUIRES_NEW传播级别,每次调用都会开启新事务 @Transactional(propagation = Propagation.REQUIRES_NEW) private void handleSingleReviewerTask(Reviewers reviewer) { MailHistory mh = mailHistoryDAO.getMailHistoryByReqId(reviewer.getReqId()); // 业务判断逻辑... if (someCondition) { mailHistoryDAO.createNewMailHistory(reviewer.getReqId(), params); } }
4. 检查DAO的persist方法,移除不必要的refresh调用
查看MailHistoryDAO.persist方法的代码,如果里面有调用session.refresh(mailHistory),可以尝试移除这个调用——因为在插入新实体时,refresh操作是不必要的,反而会触发对Session缓存的检查,导致冲突。
内容的提问来源于stack exchange,提问作者gene b.
相关产品推荐
相关产品推荐

