【问题标题】:DB constraint violation not throwing Exception in Hibernate数据库约束冲突未在 Hibernate 中引发异常
【发布时间】:2014-02-17 06:58:36
【问题描述】:

我有以下代码:

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

userDAO1.save(userRecord) 违反了完整性约束 - 所以在整个代码运行之后,没有任何内容写入 userDAO1 引用的表。

但是,userDAO1.save() 语句不会引发错误/异常 - 因此 userDAO2.save() 也会被执行。

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

如何检查 DataIntegrityViolationException 是从哪里引发的,并在 userDAO1.save() 违反约束时阻止执行 userDAO2.save()?

我尝试在此代码周围添加@Transactional 注释,但这也不起作用。

堆栈跟踪:

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)

我贴出来的代码在UpdateUserCommand里面,用@Transactional(rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW)注解

【问题讨论】:

  • 阅读 save() 或 persist() 或 merge() 或 DAO.save() 方法中调用的任何内容的 javadoc。它不会告诉在调用此方法时执行了插入查询。而且我非常怀疑堆栈跟踪是否为空。将e.printStackTrace() 添加到您的catch 块中,您会看到它。并将 DataIntegrityViolationException 包装在您的 ApplicationException 中。
  • 与答案无关,但您是否在服务层捕获DataIntegrityViolationException 异常?查看您的代码,您似乎正在这样做。不是一个好主意。 Service 层与 Dao 层异常无关。
  • 我为发布目的简化了代码 - 有一个单独的业务层。 @Nizet - 我添加了一个 printStackStrace(),唯一提到的类是内部 spring 类 - 我没有写任何东西。

标签: java spring hibernate jpa


【解决方案1】:

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

@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)
    }
}

然后在你的 HibernateUserDAO1 中:

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

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

编辑: 另请注意,Hibernate 不会选择使用 @Transactional 注释的私有方法,因为 Hibernate 依赖于从您的类实现的接口创建代理对象。没有接口定义 = 没有代理对象 = 没有 Hibernate Session。所以你不能调用带有@Transactional 注释的私有方法。我会尝试使您的 SessionFactory 成为抽象超类中的对象,并让 DAO 都继承自此。更好的选择是使用 2 个事务管理器,每个管理器都指向不同的数据库,然后指定要保存哪些数据库。这样你就可以只使用 1 个 DAO,并使用你需要的任何会话工厂来进行保存。

【讨论】:

  • 我发布的代码在一个用“@Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class)”注释的方法中。 userDAO2.save() 语句不应该也回滚吗?
  • 你是否真的使用了 2 个不同的 DAO,如果你是,那么 userDAO2.save() 将不会被回滚,因为它(通常)会有不同的 SessionFactory 实例,因此会有不同的 Session。 Hibernate 只会回滚会话中导致异常的事务。
  • 您能否发布堆栈跟踪中所说的任何内容,以及您的服务层、您的休眠配置和正在处理的对象的相关摘录?
  • 现在我想起来了,你为什么要使用 2 个不同的 DAO 来保存同一个对象?
  • 我已经添加了堆栈跟踪。 2 DAO 指向两个不同的表(一个在不同的数据库中)
【解决方案2】:

是什么让您相信在执行语句 userDAO1.save() 时没有引发 DataIntegrityViolationException?另外,为什么你认为语句 userDAO2.save() 也被执行了?

如果上述观点是根据在 IDE 调试控制台(例如 Eclipse 的调试控制台)中观察代码执行进度得出的,那么解释可能是错误的。

请尝试通过插入一些如下调试语句并执行代码来观察结果。这可能会帮助您找出失败的根本原因 -

try {
    userDAO1.save(userRecord);
    System.out.println("-- After userDAO1.save(userRecord) --");
    userDAO2.save(userRecord);
    System.out.println("-- After userDAO2.save(userRecord) --");
} catch(DataIntegrityViolationException e) {
    e.printStackTrace();
    throw new ApplicationException("Contraint violated")
}

【讨论】:

  • userDAO2 指向的表中增加了一条新记录。我还尝试在 userDAO1.save() 周围放置一个 try {} catch(Exception e){},但没有捕获到异常。
  • 嗯...这可能是因为 Hibernate 默认只在相应的 Transaction/Session 对象被提交/关闭时才执行所需的 SQL 语句。因此,调用 save() 时不会引发异常。请尝试捕获您提交或关闭 Transaction 或 Session 对象的异常。
猜你喜欢
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 2012-08-10
  • 2011-03-05
  • 1970-01-01
  • 2015-05-27
  • 1970-01-01
  • 2021-02-10
相关资源
最近更新 更多