EJB/JPA事务边界

7
我将阅读 EJB 事务边界和事务边界
让我们集中关注 RequiresNew 属性
这是来自链接的修改后的图表。

enter image description here

假设method-B被注释为RequiredNew attribute
根据理论,当method-A调用method-B时,将启动一个新事务并暂停已经启动的事务,当method-B返回时,新事务将被提交。
现在考虑一下,在 S1 部分中,我们使用 entitymanager.persist() 创建了一个 JPA 实体,然后将此实体传递给 method-B,该方法设置了实体的 name 字段。
现在当我们从 method-B 返回时,由于实体没有被由 method-A 启动的挂起事务提交到数据库中,它如何能够执行 commit 操作呢?

PS:数据库运行在读提交隔离级别。

1个回答

7
在这种情况下发生的事件由以下因素决定:
  1. JPA持久化上下文及其与JTA事务的关系。
  2. 本地接口上Java的“按引用传递”类似参数传递的行为。 (有关按引用传递的注释,请参见本答案末尾的注释)
使用@PersistenceContext注释创建实体管理器会导致根据您的persistence.xml文件定义创建事务范围内的实体管理器和相关的持久化上下文。该上下文将跟踪在persistence.xml中指定类型的实体。实体在持久化上下文中变为托管状态后,可以进行持久化、查找(em.find())或合并。该上下文将与当前运行的JTA事务关联。当该事务结束时,可以刷新并提交持久化上下文中存在的更改,或者如果事务本身回滚,则回滚更改。
假设在示例场景中使用了Bean2的本地接口。当调用Bean2-MethodB时,由于该方法带有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)注释,因此会启动一个新事务。调用方法的事务被挂起。新创建的事务没有关联任何持久性上下文。但是,传入的实体将引用由Bean1-MethodA处理的实体的同一实例,并且该实例是Bean1的实体管理器持久性上下文中的受管实体。因此,即使与此持久性上下文关联的事务被挂起,也没有限制从另一个事务内修改该实体。
因此,事件的名义顺序是;
  1. 调用Bean1-MethodA,启动事务TransactionA。
  2. 创建事务范围的实体管理器,将持久化上下文与TransactionA关联。
  3. Bean1-MethodA调用Bean2-MethodB并将“entity”作为参数传递。
  4. 新事务TransactionB开始,TransactionA暂停。
  5. Bean2-MethodB修改了entity.name字段。
  6. Bean2-MethodB完成,TransactionB提交。
  7. TransactionB JTA持久化资源提交-entity在与TransactionA关联的持久化上下文中,因此未被提交。
  8. Bean1-MethodA和它关联的TransactionA恢复。
  9. Bean2-MethodB中所做的实体更改对于Bean1-MethodA和持久化上下文是可见的。
  10. Bean1-MethodA完成,TransactionA提交,持久化上下文更改被刷新/提交到数据库。
  11. -->数据库包含在Bean2-MethodB中进行的字段更改。

当Bean2-MehodB的事务回滚时会发生什么?

值得注意的是,如果在Bean2-MethodB中更改实体字段,并且Bean2-MethodB的事务回滚,则类字段的更改不会被撤消。任何JTA资源都将回滚,但实体字段的更改仍将反映在数据库中,如果Bean1-MehodA成功完成,则可能导致潜在的不一致性。也许现实世界的问题可能会迫使采用这种解决方案,但最好修改可以撤销这些更改的事务中的实体。 上述情况已在eclipse-mars/WildFly8.2/HibernateJPA/Derby上进行测试 使用远程EJB调用 在这里,实体参数被序列化,导致在Bean2-MethodB中复制了该实体。这个副本与Bean1-MethodA中使用的对象不同,它是一个脱离的实体,并且不与Bean1-MethodA共享。(有时被称为按值传递,请参见本答案末尾的注释)。为了使更改反映在Bean1-MethodA的持久性上下文中,实体需要返回到Bean1-MethodA,然后使用实体管理器将其合并到持久性上下文中。无论远程EJB调用是否在事务内进行,都需要执行此合并操作。
关于“按引用传递”和“按值传递”的注释。
所有Java中的参数都是按照值传递定义的。有关Stack Overflow上的一个很好的扩展讨论,请参见Java是“按引用传递”还是“按值传递”?。重要的是,对于本地接口,Java会传递共享实例的引用副本 - 指针 - 这就是人们通常理解的“按引用传递”。远程接口在远程端创建实体实例的副本,因此调用方法无法看到对此副本的任何更改。这有时被称为按值传递。

在阅读了您的回答后,我因为Java感到有些紧张。非常感谢您的解释。 - Bhuvan
http://stackoverflow.com/users/2410148/user2410148 - 不用谢 - 是的,有点吓人。你觉得你能把你的问题标题编辑成类似于“EJB/JPA事务边界”的东西吗?因为这更准确地反映了我们在这里的情况。谢谢。 - NickJI

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接