hibernate 4 处理唯一索引异常

hibernate 4 handle unique index exception

我有一个基于 hibernate 4 的系统。我在 table 中有一个唯一约束,需要按以下方式处理它:

try{
    getMyService().create(myobj);
}catch(PersistenceException p){
  //constraint fails
    myobj.setConstraintColumn("new non unique value");
   getMyService().create(myobj);//should save it
}

不幸的是我不能改变系统的设计,所以我只需要这样来解决。

编辑

我得到以下异常:

org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)

创建方法代码:

 public E create(E entity) {
        entityManager.persist(entity);
        entityManager.flush();
        entityManager.refresh(entity);
        return entity;
 }

不清楚您的交易边界在哪里。 抛出异常时,您将需要: 1) 确保第一个事务已关闭(它应该是,但不确定 - 查看是否有一个嵌套事务单独尝试 #2) 2) 在您能够再次persist/flush(并随后提交)之前开始一个新事务。

我终于弄明白了。那么,让我一一解释。 首先,看一下

然后,例如我们有一个 class 和这样的方法:

public class MyClass{
   @Transactional
   public void myMethod(){
      ....
   }

}

所以,首先让我们考虑一下 myMethod 完全在它自己的事务中,因为事务是基于 AOP 的,它会在适当的方面触发时提交,但只有在方法完成、抛出异常等之后。所以我们不能部分提交、部分回滚、不完全回滚等。因此,我们需要执行以下操作:

  1. 开始大外交易
  2. 开始一个新的嵌套事务,尝试插入一条记录。
  3. 如果嵌套事务失败,它会被回滚,但外层事务仍然是运行。
  4. 如果第一个嵌套事务失败,则启动一个新的嵌套事务并插入一条包含新数据的记录,这将防止抛出ConstaintViolationException。

所以,在这种情况下,我们创建一个 class:

public class ServiceHelper{
    @Transational(proparation = **Propagation.REQUIRED_NEW**)
    public void tryConstraint throws MyConstraintException{
        try{
           //insert
        }catch(ConstraintViolationException e){
           throw new MyConstraintException(e);
        }catch(Exception ex){
           throw new Exception(ex);
        }
    }

    @Transational(proparation = **Propagation.REQUIRED_NEW**)
    public void insertWithNoConflict throws Exception {
        //Set new data
        //insert, if still CVE or anything other, just throw it , or leave it for unchecked exceptions then
    }


}

我们的服务:

public class MyService{
@Autowired
private ServiceHelper serviceHelper;

@Transactional(propagation = **Propagation.REGUIRED_NEW**)
public void createWithCheck(){
   try{
      serviceHelper.tryConstraint();
   }catch(MyConstraintException e){
       serviceHelper.insertWithNoConflict();
   }
 }
}

但是还有一个奇怪的情况,因为我需要在ServiceHelper中使用MyService方法来创建记录,但是我无法在那里获取它们,因为它会导致循环注入,所以我必须通过服务工厂来获取它们:

MyService service = (MyService)ServicesFactory.getInstance().getBean(MyService.BEAN_ID)

而且我不喜欢它。不过这个方法管用,我今天查了一下

我们应该知道两件事:首先我们不能对方法内部的事务做任何事情,我们不能在那里开始任何新的事务,等等。事务上下文与方法完全相关,它将在方法结束之前仍然存在。第二件事是当我们从同一个代理 class 的方法启动方法时 required_new 不起作用。