数据库约束违规不会在Hibernate中抛出异常 [英] DB constraint violation not throwing Exception in Hibernate

查看:378
本文介绍了数据库约束违规不会在Hibernate中抛出异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

  try {
userDAO1.save(userRecord);
userDAO2.save(userRecord);

catch(DataIntegrityViolationException e){
抛出新的ApplicationException(违反违约)
}

userDAO1.save(userRecord)违反了完整性约束 - 所以在整个代码运行后,userDAO1并没有写入表中。 然而,userDAO1.save()语句不会抛出错误/异常 - 所以userDAO2.save()也会被执行。



但DataIntegrityViolationException被捕获,堆栈跟踪为空



如何检查如果userDAO1.save()违反约束,DataIntegrityViolationException抛出并阻止userDAO2.save()被执行?



我试着在此代码上添加@Transactional注释,但也没有效果。



堆栈跟踪:

  org.springframework.dao.DataIntegrityViolationException:ORA-00001:唯一约束(UNIQUE_EMAIL)违反
; SQL [n / a];约束[UNIQUE_EMAIL];嵌套的例外是org.hibernate.exception.ConstraintViolationException:ORA-00001:唯一约束(UNIQUE_EMAIL)违反

在org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:643)
。在org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:104)
在org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:516)
在org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
在org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
在org.springframework .transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionIntercept or.invoke(TransactionInterceptor.java:120)
在org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
在org.springframework.aop.framework.JdkDynamicAopProxy.invoke( JdkDynamicAopProxy.java:202)
。在com.sun.proxy。$ Proxy76.updateUser(来源不明)在com.osiris.UserReg.UpdateUserCommand.execute(UpdateUserCommand.java:63

我发布的代码是在UpdateUserCommand中,它用 @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)

解决方案

好的,这有点儿棘手的,但我会尽我所能。
当@Transactional注释的方法退出时,Hibernate只会提交一个事务。因此,DataIntegrityViolationException仅在该方法返回后才可捕获。您无法让Hibernate不能调用 UserDAO2.save(),因为它无法检测到发生了违规行为。我将在下面提供一个例子

  @Service 
/ *这些变量名是为了清楚起见而使用的,实际上我自己使用这些名字* /
public UserServiceImpl implements UserService {
@Autowired
private HibernateUserDAO1 userDao1;
@Autowired
private HibernateUserDAO2 userDao2

@Transactional
/ *把你的try catch块放到这个方法被调用的地方* /
public void saveUserDao1(用户用户){
userDao1.saveOrUpdate(user);
}

@Transactional
/ *只有调用这个如果saveUserDao1成功* /
公共无效saveUserDao2(用户用户){
userDao2.saveOrUpdate(用户)
}
}

然后在你的HibernateUserDAO1中:

  public void saveOrUpdate(User user){
currentSession()。saveOrUpdate(user);
}

异常只能在服务层之上捕获。理想情况下,你想要做的是,使用2个不同的DAO进行单独保存,并检查第一次成功之前做第二个。



编辑:
也意识到Hibernate不会选择使用@Transactional注释的私有方法,因为Hibernate依赖于从类实现的接口创建Proxy对象。没有接口定义=没有代理对象=没有休眠会话。所以你不能调用@Transactional注解的私有方法。我试图让你的SessionFactory成为一个抽象超类中的一个对象,并且同时拥有这个DAO。更好的选择是使用2个事务管理器,每个事务管理器指向不同的数据库,然后指定哪些数据库事物正在保存。这样,您只需使用1个DAO,并使用您需要的任何会话工厂进行保存。


I have the following code:

try {
    userDAO1.save(userRecord);
    userDAO2.save(userRecord);
}
catch(DataIntegrityViolationException e) {
    throw new ApplicationException("Contraint violated")
}

userDAO1.save(userRecord) violates an integrity constraint - so after the entire code has been run, there is nothing written to the table userDAO1 refers to.

However, the userDAO1.save() statement doesn't throw an error/exception - so userDAO2.save() is executed as well.

But the DataIntegrityViolationException is caught, and the stack trace is null.

How do I check where the DataIntegrityViolationException is thrown from, and prevent userDAO2.save() from being executed if userDAO1.save() violates a constraint?

I tried adding a @Transactional annotation around this code, but that didn't work either.

Stack trace:

org.springframework.dao.DataIntegrityViolationException: ORA-00001: unique constraint (UNIQUE_EMAIL) violated
; SQL [n/a]; constraint [UNIQUE_EMAIL]; nested exception is org.hibernate.exception.ConstraintViolationException: ORA-00001: unique constraint (UNIQUE_EMAIL) violated

    at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:643)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:104)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:516)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy76.updateUser(Unknown Source)
    at com.osiris.UserReg.UpdateUserCommand.execute(UpdateUserCommand.java:63)

The code I've posted is in UpdateUserCommand, which is annotated with @Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW)

解决方案

Ok, this is a bit of a tricky one, but I'll do my best. Hibernate will only commit a transaction when the method annotated with @Transactional exits. Hence your DataIntegrityViolationException will only be catchable after that method returns. There is no way that you can get Hibernate to not call UserDAO2.save() because it can't detect that a violation has occurred. I'll provide an example below

@Service
/*These variable names are used for clarity's sake, I don't actually use these names myself*/
public UserServiceImpl implements UserService{
    @Autowired
    private HibernateUserDAO1 userDao1;
    @Autowired
    private HibernateUserDAO2 userDao2

    @Transactional
    /*Put your try catch block around where this method is called*/
    public void saveUserDao1(User user){
         userDao1.saveOrUpdate(user);
    }

    @Transactional
    /*Only call this if saveUserDao1 succeeds*/
    public void saveUserDao2(User user){
          userDao2.saveOrUpdate(user)
    }
}

Then in your HibernateUserDAO1:

public void saveOrUpdate(User user){
     currentSession().saveOrUpdate(user);
}

The exception can only be caught above your service layer. Ideally what you want to be doing, is individual saves using 2 different DAO's and checking that the first succeeded before doing the second.

EDITED: Also be aware that Hibernate will not pick up private methods annotated with @Transactional because Hibernate depends on creating Proxy objects from the interface that your class implements. No interface definition = no proxy object = no Hibernate Session. So you can't call a private method annotated with @Transactional. I'd try to make your SessionFactory an object in an abstract superclass and have both DAO's inherit from this. A better option is to use 2 transaction managers each pointing to your different databases, then specify which database things are saving too. That way you can use just 1 DAO, and use whichever session factory you require to do your saves.

这篇关于数据库约束违规不会在Hibernate中抛出异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆