8

编辑感谢大家的回答,但问题出在我的数据源配置上,它实际上处于自动提交模式。有关详细信息,请参阅下面的答案

EntityManager.flush()方法的 Javadoc和在 Google 中搜索它似乎都表明该flush方法只将待处理的语句发送到数据库并且不提交事务。但是我创建的一个简单的测试 Web 服务(在 Java 7、Oracle 11gR2、JBoss 7.1 中并且 Web 服务被打包为一个 jar 文件)似乎另有说明:

这是表创建脚本:

CREATE TABLE test(
    id INTEGER NOT NULL,
    name VARCHAR2(20), 
    CONSTRAINT test_pk PRIMARY KEY ("ID")
);
CREATE SEQUENCE test_seq;

这是对应的实体:

@Entity @Table(name = "TEST")
public class Test implements Serializable {

    private static final long serialVersionUID = 9192814682033048425L;

    @Id @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_SEQ")
    @SequenceGenerator(name="TEST_SEQ",sequenceName="TEST_SEQ", allocationSize = 1)
    private Integer id;

    @Column(name = "NAME")
    private String name;

    // Getters and setters...
}

和测试网络服务:

@Stateless @WebService(serviceName = "TestService")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class TestServiceBean implements TestService {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void createTest(String name) {
        Test test = new Test();
        test.setName(name);
        entityManager.persist(test);
        entityManager.flush();

        throw new RuntimeException();
    }
}

我的理解是:

  • 调用该createTest方法时,应用程序启动一个新事务
  • persist()方法生成要发送到数据库的 INSERT 语句
  • flush()方法将 INSERT 语句发送到数据库但不提交事务!
  • RuntimeException 导致事务回滚。

但显然我的理解是错误的:每次我运行 web 服务方法时,我都会在表中得到一个新行。此外,使用调试器单步执行此方法会显示在调用该方法时插入了该行flush()(我可以使用 SQL Developer 从另一个数据库会话“看到”该行)。

有人可以解释这种行为吗?

4

3 回答 3

8

似乎flush()终究没有什么不妥。问题是我没有在 JBoss 中正确设置数据源。这里的教训是,如果你想在 EBJ 中使用容器管理的事务,那么你需要:

  • 在 JBoss 中,选中Use JTA? 数据源配置中的复选框。
  • 在 Weblogic 中,选中数据源配置的 Transactions 选项卡中的Supports Global Transactions复选框。

此外,为了消除任何混淆,我的代码中的事务管理是正确的。抛出 aRuntimeException 回滚异常。这是为什么?好吧,从 Java EE 6教程中,我们有:

如果抛出系统异常,容器会自动回滚事务。

但是什么是系统异常?本教程似乎没有进一步涉及该主题,因此让我们搜索EJB 规范。在第 382 页中,我们有:

系统异常是 java.rmi.RemoteException(或其子类之一)的异常或不是应用程序异常的 RuntimeException。

好的,那么也许 RuntimeException 是一个应用程序异常?不,不是,因为在第 380 页我们有这个:

作为检查异常的应用程序异常可以通过在 bean 的业务接口、无接口视图、主接口、组件接口和 Web 服务端点的方法的 throws 子句中列出来定义。作为未经检查的异常的应用程序异常被定义为应用程序异常,方法是使用 ApplicationException 元数据注释对其进行注释,或者在部署描述符中使用 application-exception 元素对其进行表示。

所以,因为我没有做上面列出的任何事情,所以我在代码中抛出的异常确实是系统异常,如果您已将数据源设置为使用 JTA ,则确实会回滚事务。

于 2012-11-18T17:22:44.537 回答
0

要在 EJB 方法中回滚事务,您应该调用该setRollbackOnly()方法,否则即使抛出异常退出方法也会导致事务被提交。有关更详细的说明,您可以参考Java EE 6 教程

于 2012-11-18T15:12:54.330 回答
0

引用 JPA 规范第 23 页的 JSR-317:

属性访问器方法抛出的运行时异常会导致当前事务被标记为回滚。当持久性运行时用于加载或存储持久性状态时,此类方法引发的异常会导致持久性运行时将当前事务标记为回滚,并引发包装应用程序异常的 PersistenceException。

因此,您抛出的 RuntimeException 应该从实体 bean 设置器中抛出,而不是从 EJB 中抛出。

我敢肯定你没有得到包装 PersistenceException - 这表示回滚的标记 - 相反你得到了你抛出的 RuntimeException。

尝试从 EJB 中抛出 RollbackException 而不是 RuntimeException !!!

或者如规范所述,从实体 bean 设置器中抛出 RuntimeException。

于 2012-11-19T05:48:16.140 回答