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 的,它会在适当的方面触发时提交,但只有在方法完成、抛出异常等之后。所以我们不能部分提交、部分回滚、不完全回滚等。因此,我们需要执行以下操作:
- 开始大外交易
- 开始一个新的嵌套事务,尝试插入一条记录。
- 如果嵌套事务失败,它会被回滚,但外层事务仍然是运行。
- 如果第一个嵌套事务失败,则启动一个新的嵌套事务并插入一条包含新数据的记录,这将防止抛出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 不起作用。
我有一个基于 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 的,它会在适当的方面触发时提交,但只有在方法完成、抛出异常等之后。所以我们不能部分提交、部分回滚、不完全回滚等。因此,我们需要执行以下操作:
- 开始大外交易
- 开始一个新的嵌套事务,尝试插入一条记录。
- 如果嵌套事务失败,它会被回滚,但外层事务仍然是运行。
- 如果第一个嵌套事务失败,则启动一个新的嵌套事务并插入一条包含新数据的记录,这将防止抛出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 不起作用。