【发布时间】: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