我一直在发现,我已经存在的事务正在EJB标记为@ejb.transaction
type="Required"
的任何方法中进行提交。这可以正确吗?
我的期望是,EJB“需要”一个事务意味着:如果已经有一个事务,它将在完成时有礼貌地保持未提交状态,以便无论调用了begin()的人都可以在调用commit()
或rollback()
之前继续使用它进行进一步的操作。 [当然,如果首先没有事务,那么EJB方法将同时调用begin()
和commit()
/rollback()
。]
我的期望是错误的,还是应该寻找配置错误?
需要补充一点的是,我正在EJB中使用Hibernate 3。我在调用EJB方法之前获得了UserTransaction。 EJB生成的包装器在退出时调用ServerTransaction.commit()
,Hibernate将其 Hook 并利用其机会关闭其Session。我收到的错误是Hibernate延迟加载异常,因为当我尝试访问Hibernate持久对象上的getter时,该 session 已关闭。因此,从技术上讲,我不确定100%是否确定我观察到的ServerTransaction.commit()
是否提交了我启动的UserTransaction
(也许ServerTransaction.commit()
并不总是跟着“真正的”提交?),但是如果不这样做,那该怎么做?是Hibernate关闭Session的依据吗?
更新:我相信我上面的假设是正确的,但是我的观察结果有些偏离。我自己提供的答案,请参见下文。
最佳答案
可能是邪恶的
我个人不喜欢REQUIRED交易属性,因此强烈建议不要使用它。
懒惰地创建事务(这是必需的)导致无法真正知道事务的实际启动时间和位置以及何时提交。那不是好事。人们应该明确设计交易边界。
强制性和UserTransaction是灵魂伴侣
您使用UserTransaction
的愿望非常好,并且可以与CMT一起使用-通过容器提供的UserTransaction启动的JTA事务与容器为您启动的JTA事务之间没有区别。
我建议的方法是将所有必需的用法切换为 MANDATORY 。使用MANDATORY,容器将为您而不是启动交易。相反,它将通过确保除非进行事务处理才能调用它来保护您的bean。这是一个了不起的和未充分利用的功能。想要创建一个真正确定性的交易应用并执行它的人,MANDATORY是最好的 friend 。通过此设置,您可能会爱上CMT。
在这种情况下,可以用UserTransactions
启动事务,然后容器就像您的保镖将人们踢到路边,除非他们在尝试调用您的代码之前已经适本地启动了事务。
注意事项@Resource UserTransaction
将通过注入(inject)java:comp/UserTransaction
将通过查找
在类级别使用的@TransactionAttribute(MANDATORY)
将影响该确切类的方法(即fooClass.getDecaredMethods()
方法)。 super 类和子类的方法将默认为@TransactionAttribute(REQUIRED)
,除非这些类也已显式标记为@TransactionAttribute(MANDATORY)
userTransaction.begin()
和userTransaction.commit()
并进行相关的异常处理,请考虑使用@TransactionAttribute(REQUIRES_NEW)
。您的交易边界仍将被记录在案并且是明确的。 RuntimeException
也会导致将事务标记为回滚。对于定制的运行时异常类,请根据具体情况使用@ApplicationException
禁用此功能。
失去交易环境
有几件事可能导致正在进行的事务停止,挂起或传播到被调用的bean。EntityTransaction
或java.sql.Connection.commit()
)将规避事务管理。