本文介绍了服务器抛出OptimisticLockException后,事务没有完全回滚的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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

  // UpdateAccount.java 
@Stateless
@Path(账户)
公共类UpdateAccount {

@PersistenceContext
EntityManager em;
$ b @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){
returnaccount not found;
} else {
return executeUpdate(account);
}
}

字符串executeUpdate(帐户帐户){
Integer newValue = account.getValue()+ 1;

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

返回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);

返回历史;
}
}

//Account.java
@实体
公共类帐户{

@Id
私人长ID;

@Column
私有整数值;

@版本
私人长版;

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

//AccountHistory.java
@Entity
public class AccountHistory {

@Id
私人长ID;

@Column
private Integer valueBefore;

@Column
private Integer valueAfter;

@ManyToOne
@JoinColumn(name =idaccount)
私人账户;

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




  1. 我期望所有更改回滚吗?我错了吗?

  2. 我可以手动强制完成回滚吗?我尝试手动管理事务,
    捕获OLE并调用回滚(如)。但是,当我捕获异常时,事务已经被标记为回退。

我将应用程序部署到jboss-使用JRE 1.7的eap-6.1 / jboss-as-7.1.1Final,并使用Hibernate(这些服务器的默认版本)。我的persistence.xml文件非常简单。我没有设置任何额外的属性。

解决方案

当事务回滚时,该事务内所做的所有更改都将滚动

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



因此,Hibernate并不控制什么是回滚或不回滚。这是底层的事务管理器。

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


Let's say I have an entity bean AccountBean with version field (javax.persistence.Version annotation). During transaction my application modifies this entity and performs database operations on other entities (inserts and updates rows). Some of those entity beans have @Version field but not all of them.

When the same AccountBean entity is modified concurrently by 2 threads, OptimistickLockException is thrown and (at least according to server log) the transaction is rolled back. However, only changes made to the conflicted AccountBean entity are actually rolled back - everything else is committed to database.

**EDIT: **I added simple source code to illutrate the issue; The application is a REST web service; Two test threads call concurrently operation "update" with the same account id. Once again the OLE is thrown and yet the supposedly rolled back transaction commits to data base new AccountHistory entity :/Since transactions are managed by container the transaction is started when method update is called and commited when it retursn value; That's also when OLE is thrown.

//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. Am I wrong to expect all the changes rolled back?
  2. Can I manually force complete roll back? I tried manually managing the transaction,catching OLE and than calling rollback (as described on Adam Bien'sblog). However, when I catch the exception the transaction isalready marked as rolled back.

I deploy my application on jboss-eap-6.1/jboss-as-7.1.1Final with JRE 1.7, and use Hibernate (version defaults for those servers). My persistence.xml file is as simple as it gets. I haven't set any extra properties.

解决方案

When the transaction roll backs, all the changes made within that transaction will be roll backed as well.

For local transaction this is handled by the JDBC connection, and for global ones by rolling back all enlisted JDBC transactions.

So, Hibernate doesn't control what gets roll backed or not. It's the underlying transaction manager that does the trick.

The only case when some changes are committed and some are roll-backed is when your service code uses multiple transactions (e.g nested transactions, REQUIRES_NEW) or when there are two consecutive transactional services being called from a non-transactional one. So if the second rolls back the first is committed anyway, because the first service is not transactional, hence any successive service call is enlisted in it's own transaction.

这篇关于服务器抛出OptimisticLockException后,事务没有完全回滚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-26 06:37