Ste*_*ins 6 java jpa transactions java-ee ejb-3.0
考虑一下我正在使用的一些代码的简化视图:
@Stateless(...)
@Remote(...)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class FirstEjbType {
@EJB(...)
private SecondEjbType secondEjb;
@EJB(...)
private ThirdEjbType thirdEjb;
public void doSomething() {
secondEjb.doSomething(); // WRITES SOMETHING TO THE DATABASE
thirdEjb.doSomething(); // CAN'T SEE THAT SOMETHING IN THE DATABASE!
}
Run Code Online (Sandbox Code Playgroud)
我已将TransactionAttribute注释设置为MANDATORY类级别.我理解这意味着doSomething()必须在提供的事务中调用所有方法.在这种情况下,我们使用容器管理的事务.
将TransactionAttribute未使用的全部SecondEjbType或ThirdEjbType...既没有类,也没有方法的水平.我明白这意味着secondEjb.doSomething()并thirdEjb.doSomething()会为双方提供的事务中进行操作firstEjb.doSomething().
但是,我真的错过了什么!如代码注释所示... secondEjb将数据写入数据库,并将thirdEjb该数据作为其操作的一部分进行读取.由于所有这些都在同一个事务中运行,我不希望隔离级别存在任何问题. 但是,无论出于何种原因,secondEjb数据库写入都不可见thirdEjb.
我已经将跟踪一直转到最大值,并且显然没有异常或错误或回滚问题......初始写入对后续读取不可见.我并不认为自己是交易管理方面世界上最伟大的大师...我是否错过了一些明显的东西,或者我的概念理解基本正确,问题可能在其他地方?
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="pu" transaction-type="JTA">
<jta-data-source>jdbc/datasource</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="toplink.cache.shared.default" value="false"/>
</properties>
</persistence-unit>
</persistence>
Dav*_*ins 15
要检查的第一件事是bean 2和3用于@PersistenceContext EntityManager获取EntityManager而不是 @PersistenceUnit EntityManagerFactory后跟createEntityManager()调用.
其次,检查DataSource实际设置是否参与JTA事务(autoCommit或相关属性应该关闭).
最后,检查传播的快速而脏的方法是调用EntityManager.getDelegate()方法并检查结果对象在整个预期的事务范围内是否相同.
这里的内容是如何工作的......在EntityManager创建bean时注入它是一个假的,简单的外观.当您尝试在事务中使用EntityManager引用时,容器将实际挖掘当前事务,找到在事务范围中存储的真实 EntityManager并将您的调用委托给该 EntityManager(如果已经没有EntityManager)事务,容器将创建一个并添加它).这个真实的对象将是价值getDelegate().如果getDelegate()in secondEjb.doSomething()和not 的值不相同(==)thirdEjb.doSomething()那么你就没有得到预期的传播,而且每个都在与不同的持久化语境进行对话.
另外,请注意,在类上应用MANDATORY实际上只影响在该类中定义的方法,而不是在超类中.如果未在超类上指定@TransactionAttribute,则无论子类如何注释,这些方法都使用默认值.我只提到它,因为它可能会影响您对代码的理解.
Pas*_*ent 12
我已经在类级别将TransactionAttribute注释设置为MANDATORY.我理解这意味着必须在提供的事务中调用所有方法,如doSomething().在这种情况下,我们使用容器管理的事务.
使用MANDATORY在类级别意味着容器应抛出异常给调用者,如果有正在进行中没有交易时的任何方法FirstEjbType被调用.
出于好奇,谁开始交易呢?是否有特殊原因不使用默认值REQUIRED?
在SecondEjbType或ThirdEjbType中根本不使用TransactionAttribute ......在类和方法级别都没有.我理解这意味着secondEjb.doSomething()和thirdEjb.doSomething()都将在为firstEjb.doSomething()提供的事务中运行
这意味着默认事务属性被使用,即REQUIRED,和一个REQUIRED方法是保证在事务内执行(容器将开始一个如果需要).
所以你的情况,secondEjb.doSomething()并thirdEjb.doSomething()应确实的事务内执行firstEjb.doSomething().
我错过了一些明显的东西,或者我的概念理解是否基本正确,问题可能在其他地方?
(事务范围)持久性上下文传播的经验法则是持久化上下文在JTA事务传播时传播.但是有一些限制.JPA规范如下:
5.6.3持久性上下文传播
如5.1节所述,单个持久化上下文可以对应于一个或多个JTA实体管理器实例(全部与同一实体管理器工厂相关联).
随着JTA事务的传播,持久化上下文在实体管理器实例中传播.
持久化上下文的传播仅适用于本地 环境.持久性上下文不会传播到远程层.
Sahoo(来自GlassFish团队)更明确地描述了持久化上下文传播中的传播规则:
3.(规则#3)即使远程EJB恰好在同一个JVM或同一个应用程序的一部分中运行,也不要指望在调用远程EJB时传播PC.
因此,假设您在任何地方都使用JPA,我希望在同一事务中调用的业务方法只有在您不使用Remote接口时才会继承相同的持久化上下文(我不知道这是否是对规范的GlassFish特定解释但是Sahoo对此限制很清楚).
PS:JPA假设一个READ_COMMITTED隔离级别(因此乐观锁定可以工作),标准JPA不允许自定义隔离级别设置(好吧,一些提供商允许全局或按请求更改它,但这是不可移植的).
PPS:我不相信隔离级别是你问题的关键.
我从这里的所有答案中学到了很多东西,对人们的感谢无以言表。然而,我相信我的问题已经把水搅浑了,以至于最好从一个不同的问题开始。
从一个 EJB 跳转到下一个 EJB 并返回似乎与任何事情都没有任何关系。为了简化问题,我尝试使用与一个 EJB 完全隔离的测试用例。我采用了该secondEjb.doSomething()方法,该方法将实体持久保存到数据库中。在该方法的末尾,我添加了一个em.flush(),并尝试使用 JPA 查询再次检索该实体。
尽管我仍然采用与刚刚持久化实体完全相同的方法,但它对于后续查询是不可见的。我在其他地方做了一些额外的研究,看起来这可能只是事务上下文中 JPA 的正常隔离模式。事务启动后,该事务中的其他查询尚无法看到未提交的数据。
如果我对链接的 CodeRanch 讨论的总结是准确的,那么 JPA 就会“恶心”!:) 不管怎样,我重构了代码来完全避免这个问题。
| 归档时间: |
|
| 查看次数: |
17640 次 |
| 最近记录: |