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

编辑谢谢大家的答案,但问题出在我的数据源配置,实际上是在自动提交模式。 请参阅下面的答案了解详情。

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... } 

和测试Web服务:

 @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会话的行)。

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

看来flush()毕竟没什么问题。 问题是我没有在JBoss中正确设置数据源。 这里的教训是,如果要在EBJ中使用容器管理的事务,则需要:

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

此外,为了清除任何混淆,我的代码中的事务管理是正确的。 抛出RuntimeException 确实会回滚Exception。 这是为什么? 那么, 从Java EE 6教程我们得到:

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

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

系统exception是一个exception,它是java.rmi.RemoteException(或其子类之一)或RuntimeException,它不是应用程序exception。

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

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

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

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

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

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

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

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

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

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