我遇到了事务回滚问题,因为父事务已回滚,所以使用 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。
编辑:我已经修改了代码以准确显示导致问题的原因并仅返回给一个持久性管理器。