flush()在Java web服务中提交事务

flush()在Java web服务中提交事务,java,oracle,jpa,jax-ws,ejb-3.0,Java,Oracle,Jpa,Jax Ws,Ejb 3.0,编辑:感谢大家的回答,但问题在于我的数据源配置,它实际上处于自动提交模式。有关详细信息,请参阅 该方法的Javadoc和在Google中搜索它似乎都表明flush方法只将挂起的语句发送到数据库,而不提交事务。但我创建的一个简单的测试web服务(在Java 7、Oracle 11gR2、JBoss 7.1中,web服务打包为jar文件)似乎表明了另一种情况: 这是表创建脚本: CREATE TABLE test( id INTEGER NOT NULL, name VARCHAR2

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

该方法的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会话“查看”该行)


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

要在EJB方法中回滚事务,您应该调用
setRollbackOnly()
方法,否则退出方法甚至抛出异常都会导致提交事务。
有关更详细的解释,您可以参考。

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

  • 在JBoss中,选中数据源配置中的使用JTA?复选框
  • 在Weblogic中,选中数据源配置的“事务”选项卡中的“支持全局事务”复选框
此外,为了消除任何混淆,我的代码中的事务管理是正确的。抛出
运行时异常
会回滚异常。为什么呢?嗯,我们有:

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

但什么是系统例外?本教程似乎不再涉及该主题,因此让我们搜索该主题。在第382页中,我们有:

系统异常是指java.rmi.RemoteException(或其子系统之一)的异常- 类)或不是应用程序异常的运行时异常

好的,那么运行时异常可能是应用程序异常?不,不是,因为在第380页中我们有:

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


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

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

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

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

我确信您没有得到包装PersistenceException(表示回滚标记),而是得到了抛出的RuntimeException

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


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

不太可能。如果删除throw语句,将
EJBContext
注入我的bean并调用
context.setRollbackOnly()我仍然得到表中的那一行。这听起来很奇怪。您的配置中必须有某种东西将数据库标记为自动提交或类似的东西。
flush
方法将实体与数据库同步,但在事务边界内,因此插入的行应该在事务内可见,一旦提交,否则它们应该消失。仔细检查您的配置(JDBC驱动程序、持久化单元中的autocommit属性等)。谢谢,您的评论让我找到了实际问题,我在另一个答案中写道。然后请改进您的答案,因为它是错误的。我在问题中编写的代码正确地回滚了事务(如果设置正确!),并且不需要调用setRollbackOnly(),没关系,反正Java EE 6规范就是这么说的,也许JBoss实现并不完全符合它。我已经验证了@mavroprovato在Glassfish 3.1.2.2容器上编写的代码,它成功地回滚了事务。JavaEE6规范说:
如果bean抛出一个应用程序异常,那么回滚不是自动的