JPA事务异常疑问:提交前报错却触发提交致回滚失败
问题描述
测试JPA事务时,在em.persist(enro)行制造了外键缺失错误,原本预期直接进入异常块,但实际程序执行commit后才跳转到异常块。执行em.getTransaction().rollback()时,系统提示“No transaction is currently active”,想知道原因是什么?此时回滚是不是无效的?另外,在catch块中添加事务活跃性判断的处理方式是否合理?
原方法代码
public int addclassOrEnrollment(Enrollment enro, Boolean createClass) { int i=0; EntityManager em=null; try {em=ConnectionClass.getManager(); em.getTransaction().begin(); // transaction was started successfully if(createClass==true) { enro.getClassroom().setCreaterId(enro.getUser());; em.persist(enro.getClassroom()); } em.persist(enro.getUser()); System.out.println("error test 1"); em.persist(enro); // here will occur error beause of trying to add FK without parent record in other table System.out.println("here is 2"); em.getTransaction().commit();// at this line jumped to Exception block i=1; // this line didn't excute } catch (Exception e) { System.out.println("error :"+e.getMessage());// this line excuted System.out.println("next error");// this line excuted em.getTransaction().rollback(); // here i got error message saying "No transaction is currently active" // TODO: handle exception }finally { if(em.isOpen()) { em.close(); } } return i; }
优化后的catch块代码
} catch (Exception e) { if (em != null && em.getTransaction().isActive()) { em.getTransaction().rollback(); } System.out.println("error :"+e.getMessage()); // TODO: handle exception }
问题解答
1. 异常在commit阶段才抛出的原因
JPA的persist()方法仅将实体对象纳入持久化上下文管理,并不会立即执行对应的SQL语句。所有待执行的数据库操作会被缓存,直到调用commit()时才会批量提交到数据库执行,此时数据库才会触发外键约束检查,所以外键缺失的错误会在commit阶段抛出,而非persist()调用时。
2. 回滚提示无活跃事务且回滚无效的原因
当commit()抛出异常时,JPA会自动将当前事务标记为非活跃状态——因为提交失败后,该事务已无法正常继续执行。此时调用rollback(),由于事务已经终止,自然会提示“No transaction is currently active”,这种情况下回滚操作确实无效。
3. 添加事务活跃性判断的合理性
这种处理方式是合理的:
- 若异常是在
commit()之前抛出(比如其他运行时异常),事务仍处于活跃状态,此时执行回滚可以撤销之前的数据库操作; - 若异常是
commit()阶段抛出的,事务已被标记为非活跃,此时无需也无法执行回滚,判断可以避免报错。
内容的提问来源于stack exchange,提问作者Naing Ko
相关产品推荐
相关产品推荐

