hibernate - 在 grails 事务上捕获 RuntimeExceptions

标签 hibernate grails grails-orm quartz-scheduler

目前,我们有一个调用事务服务的 grails 作业。当服务抛出异常时,hibernate 的行为变得很奇怪。我们正在使用 grails 2.4.4 和 hibernate:3.6.10.18。

所以在我的工作中,我在执行方法上有这个:

Model.withTransaction { ->
    try {
        service.updateDatabase()
        service.method()//throws runtime exception
        } catch(RuntimeException e) {
            //do something
    }
}

奇怪的是,updateDatabase 操作会回滚。查看日志,我可以验证它是否在 catch block 中通过,但日志仍然表明仍然抛出异常。我认为这就是事务回滚的原因。

但是如果我直接在作业上抛出 RuntimeException,它不会回滚数据库事务并且异常会被干净地捕获。在我的印象中,这应该是正确的行为,并且应该与从服务内部抛出异常相同。

Model.withTransaction { ->
    try {
        service.updateDatabase()
        throw new RuntimeException()
        } catch(RuntimeException e) {
            //do something
    }
}

这正常吗? 这是一个错误吗?

最佳答案

预期的行为是:

Model.withTransaction { -> // Creates new Transaction
    try {
        service.updateDatabase() // uses the same Transaction that was created before
        service.method() // uses the same Transaction that was created before
                         // throws runtime exception
                         // sets Transaction as rollbackOnly
        } catch(RuntimeException e) {
            //do something
    }
} // as the Transaction was set as rollbackOnly it rollbacks everything that was done before

基本上这是预期的行为,现在是解释。

您所有的服务方法都是事务性的,因为您的服务在其名称上方有一个@Transactional。

@Transactional
class MyTransactionalService {
...
}

默认情况下,每个事务性方法都设置了 PROPAGATION.REQUIRED 属性。

/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)

这意味着当服务方法运行时,它使用在您的作业中创建的当前事务。棘手的部分来了,当一个功能有多个事务部分时,它会评估每个部分的回滚条件,因此当您的 method() 抛出 RuntimeException 时,它会将您的事务设置为 rollbackOnly 但继续直到该交易结束(当您的 Model.withTransaction.. 完成时)并且就在那一刻它回滚了所有内容。

更深入一点,您可以在三个部分将事务设置为 rollbackOnly。

  1. 如果 updateDatabase() 抛出异常,所有事务将被设置为 rollbackOnly
  2. 如果method()抛出一个Exception,所有的Transaction都会被设置为rollbackOnly
  3. 如果您传递给 withTransaction{..} 的闭包抛出异常,则所有事务都将设置为 rollbackOnly。

当 Transaction 结束时,事务将回滚,那一刻是在 withTransaction{..} 完成之后。

所以您需要非常小心地处理您的交易。

要解决您的问题,您可以通过在您的服务类中仅将 updateDatabase() 设置为事务性并从上面删除 @Transactional 来使您的 method() 不是事务性的服务名称。

class YourService {

    @Transactional
    def updateDatabase() {
        //...
    }

    def method() {
        //...
    }
}

仅将一个方法设置为@Transactional 会使该方法成为事务性的,而不是让所有方法都成为事务性的。

我做了一个项目作为例子,所以你可以运行测试并自己检查它,我还设置了 log4j 来显示事务的生命,这样你就可以更好地理解它,你只需要运行 MyProcessorIntegrationSpec.groovy

https://github.com/juandiegoh/grails-transactions-rollback

希望对您有所帮助!

关于hibernate - 在 grails 事务上捕获 RuntimeExceptions,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31605789/

相关文章:

hibernate - 在 Spring Boot/Hibernate/JPA 中指定连接表的表名和字段名

grails - groovy/grails,无法从 map 上删除

web-services - Grails CXF Web服务,包括WSDL中的所有方法

grails - 从外部 grails 项目引用域对象 - 在 Grails 应用程序之外使用了类 [] 上的方法

grails - beforeDelete() 中的服务访问

java - JPA/hibernate : What use is an ORM (Hibernate) when it doesn't get all JPA mappings right?

java - Hibernate 自动递增 ID 和复合键

java - 我可以在添加新表后自动更新 Netbeans 中的 Hibernate pojos 和映射吗?

grails - 测试资源 Controller 的 UrlMapping 路径

grails - 保持从 Grails 域到数据库的列顺序