如何从事务中的Grails中的数据库错误中恢复

时间:2015-04-03 19:55:35

标签: exception grails transactions gorm

简而言之,我正在尝试解决的是如何使用Hibernate从Grails应用程序中恢复某些数据库错误,并继续跳过作为一批更改的一部分的失败行更新的事务。

该应用程序使用Grails 2.3.11,但我也尝试使用版本1.3.8,但结果类似。

基本上有一个Grails服务类迭代导入的记录列表,并尝试相应地更新关联的主记录。在某些情况下, domain.save(flush:true) 调用期间可能会发生异常,例如 org.hibernate.exception.DataException 由于类似问题(数据截断:列数据太长,等等)而抛出。

此时我尝试过:

  • 禁用交易
  • 为每条记录使用 domainObj.withTransaction()
  • 尝试各种@Transactional注释
  • 在捕获异常后调用 domain.clearErrors() domain.discard()
  • 使用带有 noRollbackFor 的Transactional注释的嵌套服务进行尝试,如下所示
  • 许多其他方法,但我尝试过的任何方法都有效

示例代码:

@Transactional
class UpdateService {
    public updateBatch(Integer batchId) {
       ...
       list.each { record -> 
           record.value = 123
           try {
              nestedService.saveDomain()
           } catch (e) {
              record.clearErrors()
              record.discard()
           }
       }
       batch.status = "POSTED"
       batch.save()
   }
}

@Transactional
class NestedService {

   @Transactional(propagation = Propagation.REQUIRED, noRollbackFor = RuntimeException.class)
   public void saveDomain(domainObj) throws RuntimeException {
       if (domainObj.validate() && domainObj.save(flush:true) {
           log.info "domain $domain was saved"
       }
   }
}

一旦发生错误,我似乎无法清除hibernate会话。在每个后续记录更新时,我收到错误:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction

表示原始失败的域名。


修订:

瓦希德,谢谢你的建议。我试过了。我意识到一个问题是我跨越事务边界传递对象。所以我尝试使用NestedService类执行以下操作:

@Transactional(propagation = Propagation.REQUIRE_NEW)
public void saveDomain(domainObj) {
   def newObj = new Domain.get(domainObj.id)
   newObj.properties = domainObj.properties
   if (newObj.validate() && newObj.save(force:true) ) { ... }

我希望这可以工作,但原来的domainObj仍然失败,即使我没有调用它上面的保存。很奇怪......

1 个答案:

答案 0 :(得分:0)

一种简单的方法是循环然后使用validate()。如果确实失败了,那么只需存储失败实体的id并继续。

if(!domainObject.validate()){
    // store Id for trying it again later ?
}else{
    // Save
}