当事务属性为"必需"时,为什么CMT会在退出EJB方法时提交?

ncl*_*ark 8 ejb transactions java-ee java-ee-6 ejb-3.1

我一直发现我已经存在的事务是在标记的EJB的任何方法中提交的@ejb.transaction type="Required".这可能是正确的吗?

我的期望是,一个EJB"要求"交易是指:如果有一个已经存在,它会礼貌地离开它时未提交这样做,不管是谁调用开始()可以继续调用之前将它用于进一步的操作commit()rollback().[当然,如果首先没有事务,那么EJB方法将同时调用begin()commit()/ rollback().]

我的期望是错误的,还是我应该寻找配置错误?

可能有必要补充说我在EJB中使用了Hibernate 3.我在调用EJB方法之前获取了UserTransaction.EJB生成的包装器ServerTransaction.commit()在退出时调用,Hibernate将其挂钩并利用该机会关闭其Session.我得到的错误是一个Hibernate延迟加载异常,因为当我尝试访问Hibernate持久化对象上的getter时会话被关闭.所以从技术上来说,我不是百分之百确定ServerTransaction.commit()我是否观察到我必须承诺UserTransaction我开始(可能ServerTransaction.commit()并不总是真正遵循"真正的"提交?),但如果它没有 - 那么基于什么基础是Hibernate关闭会议?

更新: 我相信我的上述假设是正确的,但我的观察有点偏.请参阅下面的我自己提供的答案.

Dav*_*ins 20

要求可能是邪恶的

我个人不喜欢REQUIRED事务属性,并强烈反对使用它.

懒惰地创建事务(这是必需的事务)导致不知道事务何时何地实际启动以及何时提交事务.这不是一件好事.人们应明确设计交易边界.

MANDATORY和UserTransaction是灵魂伴侣

您使用的愿望UserTransaction非常好,没有什么区别之间的JTA事务通过由容器或JTA事务提供的UserTransaction开始为您启动由容器-与CMT工作.

我建议的方法是将所有必需的用法切换到MANDATORY.使用MANDATORY,容器不会为您启动交易.相反,它将通过确保无法调用它来保护您的bean,除非事务正在进行中.这是一个惊人的,未充分利用的功能.MANDATORY是任何想要创建真正确定的交易应用程序并加以强制执行的人的最好朋友.通过这种设置,您可能会爱上CMT.

在这种情况下,开始交易,UserTransactions然后容器就像你的大保镖将人踢到路边,除非他们在尝试调用你的代码之前已经适当地启动了一个事务.

注意事项

  • Servlet或BMT EJB可以使用UserTransaction.
  • CMT bean不能使用UserTransaction,但是它们可以参与UserTransaction启动的事务(如上所述,JTA事务是JTA事务).
  • BMT bean无法参与现有交易.如果在事务正在进行时调用BMT bean,则在调用BMT bean之前,事务将被容器挂起,并在方法完成后恢复.
  • @Resource UserTransaction 将通过注入为您提供用户交易
  • java:comp/UserTransaction 将通过查找获取用户事务
  • @TransactionAttribute(MANDATORY)在类级别使用会影响那个确切类的fooClass.getDecaredMethods()方法(即方法).超类和子类的方法将默认为@TransactionAttribute(REQUIRED)除非这些类也被明确注释@TransactionAttribute(MANDATORY)
  • 如果您厌倦了调用userTransaction.begin()userTransaction.commit()执行相关的异常处理,请考虑@TransactionAttribute(REQUIRES_NEW).您的交易边界仍将记录在案并明确.
  • RuntimeException从CMT bean的方法抛出的s将导致事务被标记为回滚,即使您在调用代码中捕获并处理异常也是如此.用于@ApplicationException根据具体情况自定义运行时异常类禁用此功能.

丢失您的交易上下文

有些事情可能导致正在进行的事务停止,挂起或以其他方式不传播到被调用的bean.

  • BMT bean停止事务传播.如果正在进行的事务调用BMT bean,那么该事务将在调用BMT bean之前暂停,并在bean返回后恢复.BMT bean可以是交易的起源,但不能参与现有交易.如果传播神秘失败,请确保在事务中期没有对BMT bean的无意调用.
  • 除了容器提供的UserTransaction或容器提供的方法(如SessionContext.setRollbackOnly)之外,不要使用任何其他方法来管理事务.使用"资源管理器特定的事务划分API"(如JPA)EntityTransactionjava.sql.Connection.commit() 绕过事务管理.
  • 不要开始自己的线程.事务传播基于每个线程进行.Java EE 中没有标准API支持跨多个线程的事务.如果您让线程从启动自己的线程或使用@Asynchronous,您将保留现有的事务.


ncl*_*ark 1

仔细检查会发现与上面建议的答案不同的答案。我实际上看到的是,我启动的 UserTransaction 仍然保持打开状态,但 CMT 在进入 EJB 方法时创建了一个新事务,尽管有“Required”属性。

我相信这一切的发生是因为我违反了规则。:) 使用 CMT 时,您不应该访问 UserTransaction API。CMT 很高兴地忽略了我的 UserTransaction 并启动了自己的 UserTransaction,并取代了所有事务边界的仲裁者。由于它启动了事务,它也提交了事务,当然我的 UserTransaction 保持不变。

在我看来,这似乎是脆弱和愚蠢的,也许是一种天真的观点,但在我读到它们时,它似乎符合“规则”。我不知道为什么 CMT 选择不与从更高级别开始的 UserTransactions 合作。也许是为了迫使开发人员“做正确的 J2EE 事情”并创建另一个会话 bean 层来处理更广泛的事务上下文。这会起作用,因为 CMT 将管理外部事务,因此可以征用任何内部事务,并且我相信在这种情况下,内部 EJB 不会提交“伞”事务;CMT 会等到外部事务完成,然后提交整个事务。事实上,它必须如此。

我没有心情在这个 EJB 已经臃肿的应用程序中创建更多会话 EJB,但这可能是唯一的解决方案,而不是在一大堆我不想碰的地方删除 CMT。