【问题标题】:EJB JTA/JPA CMT transaction rollback affects child transactionsEJB JTA/JPA CMT 事务回滚影响子事务
【发布时间】:2013-06-21 06:28:20
【问题描述】:

我遇到了事务回滚问题,其中使用 REQUIRES_NEW 注释的子事务正在回滚,因为父事务已回滚。我很困惑,因为我曾认为 JTA/JPA 独立处理这些事务,因此回滚一个不会影响另一个。我正在使用 Java 1.6.0_24、EJB 3.1、GlassFish 3.0.1、JPA 2、MS SQL Server(非 XA)2008、CMT。

下面的示例显示了本质:在其自己的 TN 中的流程 EJB 在其开始时调用审计器,执行 DB 操作但确定失败,设置回滚,然后在完成时调用审计器。审计员 EJB 调用 JPA 在单独的事务 (REQUIRES_NEW) 中创建审计记录,并且在事务成功时可以正常工作。代码显示了一个通用审计结构,省略了所有不必要的代码——泛型似乎是问题的一部分。

public interface ErrorDescriptor {  
    public String getName ();  
}  

public interface GenericAuditor<T extends ErrorDescriptor> {
    public void log(T errorType);
}

public abstract class AbstractGenericAuditorImpl<T extends ErrorDescriptor>
    implements GenericAuditor<T> {
}

public enum AuditType implements ErrorDescriptor {
   BEGIN, FAILED;
    public String getName() { return name(); }
}

public interface Auditor extends GenericAuditor<AuditType> {
    // The absence of the following 2 lines causes the problem
    @Override
    public void log(AuditType errorType);
}

@Stateless
public class AuditorImpl
    extends AbstractGenericAuditorImpl<AuditType>
    implements Auditor {
    @PersistenceContext ("EntityPersistenceManagement")
    protected EntityManager entityManager;

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void log (AuditType e) {
        ErrorEvent errorEvent = new ErrorEvent (); // entity
        errorEvent.setName (e.getName());
        entityManager.persist (errorEvent);
   }
}

@Stateless
public class ProcessImpl implements Process {
    @Resource private EJBContext ejbContext;
    @EJB private Auditor auditor;

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void method1() {
    auditor.log(AuditType.BEGIN);

    // Perform series of actions including database ops
    // Takes up to 1 minute
    // Somethings happen that detects failure
    ejbContext.setRollbackOnly ();

    auditor.log(AuditType.FAILED);
}
}

问题在于,当调用 parent.method1() 时,它会安静地进行,直到它确定失败并设置回滚。此时,第二次审计调用会抛出 Client's Transaction Aborted,就好像它是当前事务的一部分而不是单独的事务一样。此外,第一个审计调用不会将数据放入数据库中——即使成功它也不会在父提交之前提交(这正常吗?)这应该是单独事务中的非 XA。

我读过很多文章声称事务是独立的,但this one 建议嵌套事务在父事务提交之前不会提交,并且如果父事务回滚,子事务也会回滚。如果那是真的,我不知道如何提交部分或状态工作,因为在提交最多的事务之前不会提交任何内容。

JPA Config--我最初使用单个数据源,但后来切换到两个,但我没有观察到差异。

<persistence-unit name="EntityPersistenceManagement" transaction-type="JTA">
<jta-data-source>jdbc/app1</jta-data-source>
<properties>
    <property name="eclipselink.logging.level" value="INFO"/>
    <property name="eclipselink.weaving.internal" value="false"/>
    <property name="eclipselink.weaving" value="false"/>
    <property name="eclipselink.weaving.fetchgroups" value="false"/>
    <property name="eclipselink.weaving.changetracking" value="false"/>
    <property name="eclipselink.weaving.lazy" value="false"/>
    <property name="eclipselink.weaving.internal" value="false"/>
    <property name="eclipselink.weaving.eager" value="false"/>

    <property name="eclipselink.cache.shared.default" value="false"/>
    <property name="eclipselink.cache.shared" value = "false"/>
    <property name="eclipselink.query-results-cache" value="false"/>

    <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
</properties>

<exclude-unlisted-classes>true</exclude-unlisted-classes>
<class>org.foo.entities.AppRecord</class>
<class>org.foo.entities.ErrorEvent</class>

谁能说回滚应该如何工作?也许这在 XA 下有所不同?

我已上传此annotated transaction log 查找代码中控制点的** 条目。

添加:此second log 的 EclipseLink 级别为 FINEST。

编辑:我已修改代码以准确显示导致问题的原因并仅返回给一个持久性管理器。

【问题讨论】:

  • 您发布的 sn-p 是正确的,应该完全按照您的描述工作。您能否给我们 JPA 配置(以检查您是否指定了正确的 JTA 配置)
  • 这应该像你描述的那样工作——我使用相同的技术来审计错误并且它有效。尝试在 GF 管理控制台中打开 tx manager 日志,看看会发生什么。
  • 感谢您的回复-您确认嵌套事务不是独立的(父回滚导致子回滚)?我看不出这如何与 EJB 3.1 规范的 13.6.2.4 相协调。我一直在读到 JTA/JPA 不支持嵌套事务(仅限平面事务),但它们确实存在。
  • JTA/JPA 不支持嵌套事务的事实与您的问题和解决方案无关。当您从正在运行的 EJB 事务调用具有 REQUIRES_NEW tx 属性的 EJB 方法时,必须创建一个完全独立于“原始”事务的全新事务。它们不会嵌套。
  • @Csaba 太好了,我更希望 EJB 在没有嵌套事务的情况下工作,但是你能帮我理解为什么所谓的完全独立的事务会在父事务回滚时回滚吗?

标签: java jpa glassfish ejb


【解决方案1】:

他们应该是独立的。这可能是某个地方的错误,您能否将失败交易的日志包含在最好的地方。

【讨论】:

  • 添加了主要问题的链接——日志很大。
  • 我的意思是最好的 EclipseLink 日志,您的日志不包括任何 EclipseLink 日志记录
  • 日志只显示了一个“Created new JavaEETransactionImpl”,因此看起来 Glassfish 并没有创建两个事务。确保您已正确编译/部署代码。
  • 取消部署应用程序、重新启动 GlassFish(清除 OSGI 等)是必要的测试技术,但我尝试了许多不同的测试都没有成功。我的实际审计员类继承自抽象实用程序 (EAO) 超类,这些超类在其他应用程序中运行良好,因此我怀疑影响事务的声明存在一些细微差别。 REQUIRES_NEW 被忽略,一切都发生在一个事务中。
【解决方案2】:

您的进程似乎在全局事务(分布式)中运行,因此,如果一个事务失败,其余事务将不会提交。 (就像你描述的行为)

你写道:

"JPA Config--我原来用的是单数据源后来换了 到两个,但我没有观察到差异。”

对我来说,这就是问题所在:当您添加新资源时,事务管理器会自动假定现在需要分发事务。

"...请注意,2-Phase-Commit 不会为单个资源增加价值- 仅适用于跨越多个资源的事务。一个高效的 事务管理器将退回到简单的 1-Phase-Commit 如果 只接触一个资源,尽管这只是建议 而不是 J2EE 规范所要求的”

来自书Expert One on One J2EE Development without EJB p。 233

我在阅读你上次的日志,对我来说很明显事务是全局的,因此,你应该尝试改变这种行为。

我不知道你的框架实际上是如何工作的,但我很好奇日志中出现的一个类:

com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate

我看到它实现了com.sun.enterprise.transaction.spi.JavaEETransactionManagerDelegate 接口。

根据the source code's comments

使用 JTS 支持 XA 事务的 JavaEETransactionManagerDelegate 的实现。

public class JavaEETransactionManagerJTSDelegate

实现仅支持具有单个非 XA 资源的本地事务的 JavaEETransactionManagerDelegate。

public class JavaEETransactionManagerSimplifiedDelegate

我不知道如何配置您的框架,但您似乎需要告诉他您的情况必须使用其他实现(JavaEETransactionManagerSimplifiedDelegate)。

【讨论】:

  • 感谢您的建议。我最初在单个非 XA 数据源以及 MySQL 下观察到此问题,但我将重新测试该条件以确认。
  • 重新测试具有相同的行为,事务日志显示所有事务具有相同的 id。
  • 我试图发表评论购买它太长了,所以我修改了答案
  • 我认为您的解释是正确的,但我已经将交易及其父项标记为 REQUIRES_NEW。我在 GlassFish 3.0.1 中直接使用 EJB 3.1,所以我没有做任何特殊的配置事务管理器。我已在问题中添加了更多信息。
【解决方案3】:

我发现了一堆参考资料,它们共同暗示虽然 EJB 不支持嵌套事务,但 EclipseLink (GlassFish) 支持,这至少可以解释问题。

EJB 3.1 Spec 第 13.1.2 节明确指出事务是扁平的,并且没有嵌套事务。第 15.6.1.2 节说兼容容器不得使用嵌套事务。第 13.6.2.4 节说 REQUIRES_NEW 将暂停当前事务并在第二个事务提交后恢复。

JPA/JTA WikiBooks 在“嵌套事务”中再次声明它们不受支持,然后给出与我所看到的行为相匹配的全面定义。

The JTA 1.0.1 Spec 第 3.1 节说嵌套事务不是必需的,这可能意味着它们是可能的。

但是这个EclipseLink wiki entry 表明他们确实支持嵌套的“工作单元”,其中嵌套的提交不是独立的,而是推迟到父提交。这实际上符合我看到的行为。

因此,如果我得到嵌套事务,则不清楚如何创建隔离的、明确的审计提交,当父级回滚时不会回滚。

【讨论】:

    【解决方案4】:

    此问题是 GlassFish https://java.net/jira/browse/GLASSFISH-8319 的一个错误,已在 v 3.1 中记录为已解决。

    【讨论】:

    • +1 查找错误报告!我总是新的泛型是邪恶的;)
    【解决方案5】:

    我认为我有非常相似的问题(有一种情况)。在内部事务中,我在 db 中有一个更新,但是当父事务完成时,我有一个 OptimisticLockException,告诉记录已经更新。

    从 EcipeLink 文档了解 UnitOfWorks,我认为(至少它有效=)已经解决了这个问题。问题是父 UnitOfWork 不知道我在 RequiresNew 事务中所做的更改。并且在父事务'Parent' UnitOfWork 的成功结束中包含 UPDATE 的更改。 所以我在 RequiresNew 调用之后添加了 em.refresh() 。内部交易的结果 - 已应用。父事务 - 成功完成。

    马特,你写道:

    因此,如果我得到嵌套事务,则不清楚如何创建隔离的、明确的审计提交,当父级回滚时不会回滚。

    也许 em.refresh() 会解决这个问题?也许您的父 UnitOfWork 将数据库回滚到启动状态。

    马特,感谢您提供大量参考资料。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-15
      • 1970-01-01
      • 1970-01-01
      • 2020-10-02
      相关资源
      最近更新 更多