【发布时间】: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 在没有嵌套事务的情况下工作,但是你能帮我理解为什么所谓的完全独立的事务会在父事务回滚时回滚吗?