3.4.5.3. 事务交互示例
- 嵌套事务的回滚
-
如果嵌套事务是通过
getTransaction()
创建并回滚,则无法提交外层事务。例如:void methodA() { Transaction tx = persistence.createTransaction(); try { methodB(); (1) tx.commit(); (4) } finally { tx.end(); } } void methodB() { Transaction tx = persistence.getTransaction(); try { tx.commit(); (2) } catch (Exception e) { return; (3) } finally { tx.end(); } }
1 调用方法创建嵌套事务 2 假设发生异常 3 处理异常并退出 4 在这里将抛出异常,因为事务被标记为仅回滚(rollback only)。 如果使用
createTransaction()
创建methodB()
中的事务,那么回滚它将不会影响methodA()
中的外层事务。 - 在嵌套事务中读取和修改数据
-
首先看一下使用
getTransaction()
创建的依赖嵌套事务:void methodA() { Transaction tx = persistence.createTransaction(); try { EntityManager em = persistence.getEntityManager(); Employee employee = em.find(Employee.class, id); (1) assertEquals("old name", employee.getName()); employee.setName("name A"); (2) methodB(); (3) tx.commit(); (8) } finally { tx.end(); } } void methodB() { Transaction tx = persistence.getTransaction(); try { EntityManager em = persistence.getEntityManager(); (4) Employee employee = em.find(Employee.class, id); (5) assertEquals("name A", employee.getName()); (6) employee.setName("name B"); tx.commit(); (7) } finally { tx.end(); } }
1 使用 name == "old name" 加载实体 2 给字段设置新值 3 调用方法创建嵌套事务 4 获取与方法 methodA 中相同的 EntityManager 实例 5 使用同样的标识符加载实体 6 字段值是新的,因为我们使用相同的持久化上下文,并且根本没有调用 DB 7 此时不进行实际的提交 8 更改提交到 DB,它将包含 "name B" 现在,看一下使用
createTransaction()
创建的独立嵌套事务的相同示例:void methodA() { Transaction tx = persistence.createTransaction(); try { EntityManager em = persistence.getEntityManager(); Employee employee = em.find(Employee.class, id); (1) assertEquals("old name", employee.getName()); employee.setName("name A"); (2) methodB(); (3) tx.commit(); (8) } finally { tx.end(); } } void methodB() { Transaction tx = persistence.createTransaction(); try { EntityManager em = persistence.getEntityManager(); (4) Employee employee = em.find(Employee.class, id); (5) assertEquals("old name", employee.getName()); (6) employee.setName("name B"); (7) tx.commit(); } finally { tx.end(); } }
1 使用 name == "old name" 加载实体 2 给字段设置新值 3 调用方法创建嵌套事务 4 创建新的 EntityManager 实例, 因为这是一个新事务 5 使用相同的标识符加载一个实体 6 字段值是旧的,因为一个旧的实体实例被从数据库加载了 7 变更被提交到 DB,现在 "name B"值被存储到数据 DB 8 由于启用乐观锁,这里将发生异常,提交失败 在最后一个例子中,只有当实体支持乐观锁时,即只有它实现了
Versioned
接口时,才会发生第(8)点的异常。