EJB/JPA 交易边界

EJB/JPA Transaction Boundaries

我在读书 EJB Transaction boundary and Transaction boundary

让我们专注于 RequiresNew Attribute

这是 link

的修改图

所以假设 method-B 被注释为 RequiredNew attribute

所以根据理论,当 method-A 调用 method-B 时,一个新的事务将被启动并且已经启动的事务将被挂起,而当 method-B returns将提交新事务。


现在考虑在 S1 部分中,我们使用 entitymanager.persist() 创建一个 jpa 实体,现在我们将此实体传递给 method-B,后者设置实体的 name 字段.

现在,当我们从 method-B return 中如何 commit 数据库中的事务时,实体不会被 method-A 启动的挂起事务提交?


PS: Db 运行 处于已提交读隔离级别。

在这种情况下发生的事情受制于:

  1. JPA 持久性上下文及其与 JTA 事务的关系。
  2. Java 的“pass-by-referencelike 参数在本地接口上的行为。 (请参阅本答案末尾关于传递引用的注释)

使用 @PersistenceContext 注释创建实体管理器会导致创建事务范围实体管理器和关联的持久性上下文,如您的 persistence.xml 文件所定义。上下文将跟踪您 persistence.xml 中指定类型的实体。实体在持久化、找到 (em.find()) 或合并后在该上下文中被管理。上下文将与当前 运行 JTA 事务相关联。当此事务结束时,持久性上下文中存在的更改可以被刷新和提交 - 或者如果事务本身回滚则回滚。

在示例场景中,假设使用了 Bean2 的本地接口。当 Bean2-MethodB 被调用时,一个新的事务被启动,因为该方法被注释为 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)。调用方法的事务被挂起。没有持久性上下文与新创建的事务关联。但是,传入的实体将引用由 Bean1-MethodA 处理的实体的同一实例,并且该实例是 Bean1 的实体管理器的持久性上下文中的托管实体。因此,即使与此持久性上下文关联的事务被暂停,也没有限制从另一个事务中修改该实体。

因此,名义上的事件顺序是;

  1. Bean1-MethodA 被调用,TransactionA 开始。
  2. 已创建事务范围的实体管理器,与事务 A 关联的持久性上下文。
  3. Bean1-MethodA调用Bean2MethodB并传入'entity'作为参数。
  4. 新交易 – 交易 B 开始,交易 A 暂停。
  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 提交和持久性上下文更改 flushed/committed 到 DB
  11. --> 数据库包含在 Bean2-MethodB
  12. 中所做的字段更改

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

值得注意的是,如果在 Bean2-MethodB 中进行了实体字段更改,并且 Bean2-MethodB’s 事务回滚,则不会还原对 class 字段的更改。任何 JTA 资源都将回滚,但如果 Bean1-MehodA 成功完成,实体字段更改仍会反映在数据库中,从而导致潜在的不一致。也许现实世界的问题可能会迫使这样的解决方案,但可能更好地修改事务中可以回滚这些更改的实体。

以上场景在eclipse-mars/WildFly8.2/HibernateJPA/Derby

上测试

使用远程 EJB 调用

此处,实体参数被序列化,生成 Bean2-MethodB 中实体的副本。此副本与 Bean1-MethodA 中使用的对象不同,它是一个分离的实体,不与 Bean1-MethodA 共享。 (这有时被称为 按值传递 ,请参阅此答案末尾的注释)。为了将更改反映在 Bean1-MethodA’s 持久性上下文中,需要将实体返回到 Bean1-MethodA,然后使用实体管理器将其合并到持久性上下文中。无论是否在事务中进行远程 ejb 调用,都需要进行此合并。

关于“按引用传递”和“按值传递”的注意事项。

java中的所有参数根据定义按值传递。有关堆栈溢出的良好扩展讨论,请参阅 Is Java "pass-by-reference" or "pass-by-value"?。这里重要的是,对于本地接口,Java 将引用的副本——一个指针——传递给共享实例——这就是人们通常理解的“按引用传递”。远程接口在远程端创建实体实例的副本,因此调用方法看不到此副本的任何更改。这有时被称为按值传递。