捕获DataIntegrityViolationException后更新实体遇AssertionFailure问题求助
你遇到的org.hibernate.AssertionFailure错误,核心原因是Hibernate的Session在抛出DataIntegrityViolationException后已经处于无效的脏状态,此时不能再用这个Session执行任何数据库操作,哪怕你加了@Transactional(noRollbackFor = DataIntegrityViolationException.class)也没用——事务没回滚,但Session已经损坏。
下面给你几个简单可行的解决方案,不用复杂的TransactionTemplate:
方案一:先查询再操作(最推荐)
直接跳过异常捕获逻辑,先检查实体是否存在,再决定创建或更新。这种方式更高效,也避免了异常带来的Session问题。
@Transactional public void createOrUpdate(Entity arg) { // 先查询是否存在符合条件的实体 eRepository.findExisting(arg.user, arg.reason).ifPresentOrElse( // 存在则更新 existing -> { existing.setComment(arg.comment); eRepository.save(existing); }, // 不存在则创建 () -> eRepository.save(arg) ); }
注意:如果是高并发场景,可能会出现多个线程同时查询到“不存在”然后执行创建,导致唯一约束冲突。这种情况可以给查询加悲观锁(比如findExisting方法用@Lock(LockModeType.PESSIMISTIC_WRITE)),或者依赖数据库层面的唯一约束原子性处理冲突。
方案二:拆分事务,开启新Session
把更新逻辑放到一个新的事务中,这样会自动创建一个干净的Session,避开原来的无效Session。
@Transactional public void create(Entity arg) { try { eRepository.save(arg); } catch (DataIntegrityViolationException e) { // 调用新事务的方法执行更新 updateExisting(arg); } } // 用REQUIRES_NEW开启独立事务,创建新Session @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateExisting(Entity arg) { eRepository.findExisting(arg.user, arg.reason).ifPresent(existing -> { existing.setComment(arg.comment); eRepository.save(existing); }); }
注意:Spring的事务代理只对public方法生效,且不能在同一个类中直接调用(否则代理不生效)——如果要在同一个类调用,需要自己注入当前Bean的代理对象,或者把这个方法抽到单独的Service类里。
方案三:清除当前Session的脏状态
在catch块中调用EntityManager的clear()方法,清空Session缓存中的无效数据,让Session恢复可用状态。
@Autowired private EntityManager entityManager; @Transactional(noRollbackFor = DataIntegrityViolationException.class) public void create(Entity arg) { try { eRepository.save(arg); } catch (DataIntegrityViolationException e) { // 清除Session中的脏数据,重置状态 entityManager.clear(); // 现在可以正常执行查询和更新了 eRepository.findExisting(arg.user, arg.reason).ifPresent(existing -> { existing.setComment(arg.comment); eRepository.save(existing); }); } }
注意:clear()会清除Session中所有未提交的实体变更,所以要确保此时没有其他需要保留的操作,这里因为save已经失败,所以是安全的。
内容的提问来源于stack exchange,提问作者littleAlien

