我在读
EJB Transaction boundary and Transaction boundary
让我们专注于RequiresNew Attribute
.
这是链接中的修改图
所以说method-B
用 RequiredNew attribute
注释.
所以根据理论当method-A
电话method-B
一个新的事务将开始,已经开始的事务将被暂停,当 method-B
返回新事务将被提交。
现在考虑在 S1
部分我们使用 entitymanager.persist()
创建一个 jpa 实体现在我们将此实体传递给 method-B
其中设置了 name
实体的领域。
现在当我们从 method-B
返回时怎么可能commit
db 中的事务,实体没有被 method-A
启动的挂起事务提交?
PS:在读提交隔离级别运行的数据库。
最佳答案
在这种情况下发生的事情由以下因素控制:
(请参阅本答案末尾关于通过引用传递的注释)
使用
@PersistenceContext
创建实体管理器注释导致创建一个事务范围的实体管理器和相关的持久化上下文,如您的 persistence.xml
所定义的。文件。上下文将跟踪您的 persistence.xml
中指定的类型的实体。 .实体在持久化、找到 ( em.find()
) 或合并后,将在该上下文中进行管理。上下文将与当前运行的 JTA 事务相关联。当此事务结束时,持久性上下文中存在的更改可以刷新并提交 - 如果事务本身回滚,则可以回滚。在示例场景中,假设使用了 Bean2 的本地接口(interface)。当
Bean2-MethodB
被称为新事务已启动,因为该方法用 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
注释。 .调用方法的事务被挂起。没有持久性上下文与新创建的事务相关联。但是,传入的实体将引用由 Bean1-MethodA
处理的实体的同一实例。 ,并且这个实例是 Bean1 的实体管理器的持久化上下文中的一个托管实体。因此,即使与此持久性上下文关联的事务被挂起,从另一个事务中修改该实体也没有限制。所以,名义上的事件顺序是;
Bean1-MethodA
被调用,TransactionA 开始。 Bean1-MethodA
电话Bean2MethodB
并将“实体”作为参数传入。 Bean2-MethodB
修改字段 entity.name
. Bean2-MethodB
对 Bean1-MethodA
可见和持久化上下文 Bean1-MethodA
完成,TransactionA 提交和持久性上下文更改刷新/提交到 DB Bean2-MethodB
中所做的字段更改当 Bean2-MehodB 的 Transaction 回滚时会发生什么?
值得注意的是,如果实体字段在
Bean2-MethodB
中进行了更改, 和 Bean2-MethodB’s
事务回滚,对类字段的更改不会恢复。任何 JTA 资源都将回滚,但实体字段更改仍将反射(reflect)在数据库中 If Bean1-MehodA
成功完成,导致潜在的不一致。也许现实世界的问题可能会迫使这样的解决方案,但可能更好的是修改事务中可以回滚这些更改的实体。以上场景在eclipse-mars/WildFly8.2/HibernateJPA/Derby 上测试过
使用远程 EJB 调用
在这里,实体参数被序列化,导致
Bean2-MethodB
中的实体副本。 .此副本与 Bean1-MethodA
中使用的对象不同,它是一个分离的实体,不与 Bean1-MethodA
共享. (这有时称为传值,请参阅本答案末尾的注释)。为了在 Bean1-MethodA’s
中反射(reflect)更改实体需要返回到 Bean1-MethodA
的持久性上下文然后使用实体管理器合并到持久性上下文中。无论远程 ejb 调用是否在事务中进行,都需要进行这种合并。注意“传递引用”和“传递值”。
java中的所有参数都是按定义传递的。有关堆栈溢出的良好扩展讨论,请参阅 Is Java "pass-by-reference" or "pass-by-value"? .这里重要的是,对于本地接口(interface),Java 将引用的副本——一个指针——传递给共享实例——这就是人们通常理解的“传递引用”。远程接口(interface)在远程端创建实体实例的副本,因此调用方法无法看到对此副本的任何更改。这有时称为传值。
关于java - EJB/JPA 事务边界,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30636273/