EJB/JPA事务边界 [英] EJB/JPA Transaction Boundaries

查看:144
本文介绍了EJB/JPA事务边界的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读 EJB事务边界和事务边界

让我们专注于RequiresNew Attribute.

这是链接中的修改图

因此,假设method-BRequiredNew attribute注释.

因此,根据理论,当method-A调用method-B时,将启动一个新事务,而已启动的事务将被挂起,而当method-B返回时,将提交新事务.


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

现在,当我们从method-B返回时,如何像数据库中那样commit进行事务处理,该实体不是由method-A启动的挂起事务提交的?


PS:Db在已提交读隔离级别中运行.

解决方案

在这种情况下发生的情况由以下因素决定:

  1. JPA持久性上下文及其与JTA事务的关系.
  2. Java的"通过引用传递" like 参数在本地接口上传递的行为. (请参阅此答案末尾的通过引用的注释)

使用@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调用Bean2MethodB并传入实体"作为参数.
  4. 新交易-交易B开始,交易A暂停.
  5. Bean2-MethodB修改字段entity.name.
  6. Bean2-MethodB完成并提交TransactionB.
  7. TransactionB JTA持久资源提交- entity 位于与TransactionA相关联的持久性上下文中,因此未提交.
  8. Bean1-MethodA及其关联的TransactionA也将恢复.
  9. Bean1-MethodA和持久化上下文中可见在Bean2-MethodB中进行的实体更改
  10. Bean1-MethodA完成,将TransactionA提交和持久性上下文更改刷新/提交给DB
  11. ->数据库包含在Bean2-MethodB
  12. 中进行的字段更改

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

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

上述场景已在eclipse-mars/WildFly8.2/HibernateJPA/Derby 上进行了测试

处理远程EJB调用

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

引用传递"和值传递"的注意事项.

java中的所有参数均按定义按值传递.要获得良好的扩展讨论,请参见是Java还是";通过引用"或按值传递"?.这里重要的是,对于本地接口,Java将引用的副本(指向共享实例的指针)传递给共享实例,这就是人们通常所理解的按引用传递".远程接口在远程端创建实体实例的副本,因此调用方法无法查看此副本的任何更改.有时也称为传递值.

I was reading EJB Transaction boundary and Transaction boundary

Lets concentrate on RequiresNew Attribute.

Here is the modified diagram from the link

So let say method-B is annotated with RequiredNew attribute.

so according to theory when method-A calls method-B a new transaction will be start and the already started transaction will be suspended, and when method-B returns the new transaction will be committed.


Now consider that in section S1 we create a jpa entity using entitymanager.persist() and now we pass this entity to method-B which set the name field of the entity.

Now when we return from method-B how can it commit the transaction as in db, the entity is not being committed by the suspended transaction started by method-A ?


PS: Db running in read-committed isolation level.

解决方案

What happens in this scenario is governed by:

  1. The JPA Persistence Context and its relationship to JTA transactions.
  2. The behaviour of Java’s "pass-by-reference" like parameter passing on local interfaces. (See note on pass-by-reference at the end of this answer)

Creating an Entity Manager with the @PersistenceContext annotation leads to the creation of a Transaction-Scoped entity manager and associated persistence context as defined by your persistence.xml file. The context will keep track of the entities of the types specified in your persistence.xml. An entity becomes managed in that context after it is persisted, found (em.find()) or merged. The context will be associated with the currently running JTA transaction. When this transaction ends, the changes present in the persistence context can be flushed and committed - or rolled-back if the transaction itself rolls back.

In the example scenario assume Bean2’s local interface is used. When Bean2-MethodB is called a new transaction is started since the method is annotated with @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW). The transaction of the calling method is suspended. No persistence context is associated with the newly created transaction. However, the entity passed in will refer to the same instance of the entity processed by Bean1-MethodA, and this instance is a managed entity in the persistence context of Bean1’s Entity Manager. So, even though the transaction associated to this persistence context is suspended, there are no restrictions on modifying that entity from within another transaction.

So, the nominal sequence of events is;

  1. Bean1-MethodA is called, TransactionA starts.
  2. Transaction-Scoped Entity Manager created, persistence context associated with TransactionA.
  3. Bean1-MethodA calls Bean2MethodB and passes in ’entity’ as a parameter.
  4. New Transaction – TransactionB starts, TransactionA suspended.
  5. Bean2-MethodB modifies field entity.name.
  6. Bean2-MethodB finishes and TransactionB commits.
  7. TransactionB JTA persistent resources commit - entity is in persistence context associated with TransactionA and so is not committed.
  8. Bean1-MethodA resumes as does its associated TransactionA.
  9. Entity changes made in Bean2-MethodB are visible to Bean1-MethodA and the persistence context
  10. Bean1-MethodA finishes, TransactionA commits and persistence context changes are flushed/committed to DB
  11. --> DB contains field change made in Bean2-MethodB

What happens when Bean2-MehodB’s Transaction rolls-back?

It is worth noting that if entity field changes are made in Bean2-MethodB, and Bean2-MethodB’s transaction rolls-back, changes to class fields are not reverted. Any JTA resources will be rolled-back, but the entity field changes would still be reflected in the DB If Bean1-MehodA completes successfully, leading to potential inconsistencies. It maybe that real world problems might force such a solution, but probably better to modify entities in the transaction that can roll those changes back.

The above scenarios were tested on eclipse-mars/WildFly8.2/HibernateJPA/Derby

Working with Remote EJB calls

Here, the entity parameter is serialised resulting in a copy of the entity in Bean2-MethodB. This copy is not the same object as used in Bean1-MethodA, it is a detached entity and is not shared with Bean1-MethodA. (This is sometimes known as pass-by-value, see note at the end of this answer). In order for changes to be reflected in Bean1-MethodA’s persistence context the entity would need to be returned to Bean1-MethodA and then merged into the persistence context using the Entity Manager. This merging would be required regardless of whether or not the remote ejb call was made within a transaction.

Note on "Pass-by-reference" and "Pass-by-value".

All parameters in java are pass by value by definition. For a good - extended - discussion on Stack overflow see Is Java "pass-by-reference" or "pass-by-value"?. What’s important here is that for local interfaces, Java passes a copy of the reference – a pointer - to the shared instance – and this is what people often understand as "pass-by-reference". Remote interfaces create a copy of the entity instance at the remote end so the calling method has no visibility of any changes to this copy. This is sometimes known as pass-by-value.

这篇关于EJB/JPA事务边界的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆