3

我遇到了事务回滚问题,因为父事务已回滚,所以使用 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。

我读过许多声称事务是独立的文章,但 这篇文章表明嵌套事务在父提交之前不会提交,并且如果父回滚,子事务也会回滚。如果这是真的,我看不出我如何提交部分或状态工作,因为在提交最多的事务之前不会提交任何内容。

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 下有所不同?

我已上传此带注释的事务日志,以查找代码中控制点的 ** 条目。

添加:第二个日志的 EclipseLink 级别为 FINEST。

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

4

5 回答 5

4

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

于 2013-06-28T16:46:18.327 回答
1

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

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

马特,你写道:

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

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

马特,感谢一堆参考。

于 2015-03-04T07:55:31.033 回答
0

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

于 2013-06-25T12:50:57.907 回答
0

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

你写了:

“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接口。

根据源代码的评论

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

public class JavaEETransactionManagerJTSDelegate

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

public class JavaEETransactionManagerSimplifiedDelegate

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

于 2013-06-26T00:42:18.427 回答
0

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

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

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

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

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

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

于 2013-06-26T12:25:13.223 回答