【问题标题】:How to recover from database errors in Grails within a transaction如何在事务中从 Grails 中的数据库错误中恢复
【发布时间】:2015-04-03 19:55:35
【问题描述】:

简而言之,我要解决的是如何使用 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
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"
       }
   }
}

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

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

它表示原始失败的域 id。


修订:

Vahid,感谢您的建议。我已经试过了。我意识到一个问题是我正在跨越事务边界传递对象。所以我尝试用 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 仍然失败。很奇怪……

【问题讨论】:

  • 为您的传播尝试了 REQUIRES_NEW ?我在这里写了一些关于酸的笔记github.com/vahidhedayati/documentation/blob/master/grails/… 最近阅读了这个blog.perezalcolea.info/2014/06/09/… 和这个sacharya.com/transactions-in-grails
  • Vahid - 请在我添加修订的原始 cmets 中查看我的评论。
  • 如果您在验证和保存之前尝试 newObj.merge() 会怎样,您是否还设法阅读了关于休眠批处理的第三个链接的最后一部分。不过,永远不会推荐它用于控制器。如果您有时间youtube.com/watch?v=JNey9T--rLE 观看 Burt 很好地解释交易。需要自己多看几次。
  • @JohnMartin 这是实际代码吗?因为您有方法 'saveDomain(domainObj)' 但您在前面的部分中只调用了 'nestedService.saveDomain()' 而没有参数。在修订部分,您正在执行需要“新”的“新 Domain.get”。和 'force:true' 而不是 'flush:true'。
  • @Ramsharan - 我在模拟代码,所以有几个错别字并不让我感到惊讶。

标签: exception grails transactions grails-orm


【解决方案1】:

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

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

【讨论】:

  • 我遇到的问题是它受到当前域约束,因此 validate() 返回 true。问题是 save() 失败并自动回滚整个事务,而不管 noRollbackFor 注释如何。一旦发生错误,每次后续更新都会失败。有点奇怪……
  • 如果您在 Action/Function 级别上使用 Transactional Annotation,请尽量不要在 Service 定义中使用它(将其设置为 false )。默认情况下,所有服务都是事务性的,因此您不必明确将其定义为事务性的。查看 [grails.github.io/grails-doc/3.0.x/guide/services.html]. 的 Grails 服务部分
猜你喜欢
  • 2017-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
相关资源
最近更新 更多