服务器抛出OptimisticLockException后,事务未完全回滚

时间:2014-06-30 13:16:44

标签: java hibernate java-ee jpa optimistic-locking

假设我有一个带有版本字段的实体bean AccountBean(javax.persistence.Version annotation)。在事务期间,我的应用程序修改此实体并对其他实体执行数据库操作(插入和更新行)。其中一些实体bean有@Version字段但不是全部。

当同一个AccountBean实体被2个线程同时修改时,抛出OptimistickLockException并且(至少根据服务器日志)回滚事务。但是,只有对冲突的AccountBean实体所做的更改才会实际回滚 - 其他所有内容都将提交给数据库。

**编辑:** 我添加了简单的源代码来解决这个问题;该应用程序是一个REST Web服务;两个测试线程使用相同的帐户ID同时调用“update”操作。再一次抛出OLE,然而所谓的回滚事务提交给数据库新的AccountHistory实体:/ 由于事务由容器管理,因此在调用方法更新时启动事务,在重新调整值时提交事务;这也是抛出OLE的时候。

//UpdateAccount.java
@Stateless
@Path("account")
public class UpdateAccount {

    @PersistenceContext
    EntityManager em;

    @Path("update")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public String update(Long accountId) {
        Account account = em.find(Account.class, accountId);
        if(null == account) {
            return "account not found";
        } else {
            return executeUpdate(account);
        }
    }

    String executeUpdate(Account account) {
        Integer newValue = account.getValue() + 1;

        em.persist(getAccountHistory(account, newValue));
        account.setValue(newValue);

        return "ok";
    }

    AccountHistory getAccountHistory(Account account, Integer newValue) {
        AccountHistory history = new AccountHistory();
        history.setId(new Date().getTime());
        history.setAccount(account);
        history.setValueBefore(account.getValue());
        history.setValueAfter(newValue);

        return history;
    }
}

//Account.java
@Entity
public class Account {

    @Id
    private Long id;

    @Column
    private Integer value;

    @Version
    private Long version;

    (...)//getters, setters etc
}

//AccountHistory.java
@Entity
public class AccountHistory {

    @Id
    private Long id;

    @Column
    private Integer valueBefore;

    @Column
    private Integer valueAfter;

    @ManyToOne
    @JoinColumn(name = "idaccount")
    private Account account;

    (...)//getters, setters etc
}
  1. 我错误地预计所有更改都会回滚吗?
  2. 我可以手动强制完全回滚吗?我尝试手动管理交易, 捕获OLE而不是调用回滚(如Adam Bien's blog所述)。但是,当我捕获事务的异常时 已被标记为已回滚。
  3. 我使用JRE 1.7在jboss-eap-6.1 / jboss-as-7.1.1Final上部署我的应用程序,并使用Hibernate(这些服务器的版本默认值)。我的persistence.xml文件非常简单。我没有设置任何额外的属性。

1 个答案:

答案 0 :(得分:0)

当事务回滚时,该事务中所做的所有更改也将被回滚。

对于本地事务,这由JDBC连接处理,对于全局事务,通过回滚所有登记的JDBC事务来处理。

因此,Hibernate无法控制得到滚动支持的内容。这是诀窍的基础事务管理器。

提交某些更改并且某些更改是滚动支持的唯一情况是,当您的服务代码使用多个事务(例如嵌套事务,REQUIRES_NEW)时,或者从非事务性事务中调用两个连续事务服务时。因此,如果第二次回滚,则第一次回滚是承诺的,因为第一次服务不是交易的,因此任何连续的服务呼叫都被列入其自己的交易中。