EJB JTA / JPA CMT事务回滚会影响子事务

时间:2013-06-24 16:46:53

标签: java jpa glassfish ejb

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

下面的示例显示了本质:进程EJB在其自己的TN中在其启动时调用审计者,执行数据库操作但确定失败,设置回滚,然后在完成时调用审计员。 Auditor 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()时,它会安静地进行,直到它确定失败并设置回滚。此时,第二次审计调用将客户端的事务中止抛出,就像它是当前事务的一部分而不是单独的事务一样。此外,第一次审计调用不会在数据库中放入任何数据 - 即使成功也不会提交,直到父提交(这是正常的吗?)这在单独的事务中应该是非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这个代码中控制点的**条目。

ADDED:这个second log在FINEST上有EclipseLink级别。

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

5 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:1)

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

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

马特,你写道:

  

因此,如果我收到嵌套事务,则不清楚如何创建隔离的,定义的审计提交,这些提交在父回滚时不会回滚。

也许em.refresh()会解决这个问题?也许你的父UnitOfWork回滚db到开始状态。

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

答案 2 :(得分:0)

他们应该是独立的。它可能是某个地方的错误,请你把最好的交易日志包括在内。

答案 3 :(得分:0)

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

您写道:

  

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

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

  

“......请注意,2阶段提交不会增加单一资源的价值 -   仅适用于跨多个资源的事务。高效   如果,事务管理器将回退到简单的1阶段提交   仅涉及单一资源,但这只是建议   而不是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)。

答案 4 :(得分:0)

我发现了一堆引用,这些引用一起暗示虽然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表明它们支持嵌套的“工作单元”,其中嵌套提交不是独立的,而是延迟到父提交。这实际上符合我所看到的行为。

因此,如果我获得嵌套事务,则不清楚如何创建用于审计的隔离的,定义的提交,这些提交在父回滚时不会回滚。