EntityManager.flush()在Java Web服务中提交事务

时间:2012-11-18 14:27:15

标签: java oracle jpa jax-ws ejb-3.0

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

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“查看”来自另一个db会话的行)。

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

3 个答案:

答案 0 :(得分:7)

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

  • 在JBoss中,选中数据源配置中的使用JTA?复选框。
  • 在Weblogic中,选中数据源配置的“事务”选项卡中的支持全局事务复选框。

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

  

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

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

  

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

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

  

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

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

答案 1 :(得分:0)

要在EJB方法中回滚事务,您应该调用setRollbackOnly()方法,否则退出方法甚至抛出异常会导致事务被提交。 有关更详细的说明,请参阅The Java EE 6 Tutorial

答案 2 :(得分:0)

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

  

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

因此,抛出的RuntimeException应该从实体bean setter中抛出而不是从EJB抛出。

我确信你没有得到包装PersistenceException - 它表示回滚的标记 - 而是你得到了你扔的RuntimeException。

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

或者按照规范说明从实体bean设置器中抛出RuntimeException。