在 grails 事务上捕获 RuntimeExceptions
Catching RuntimeExceptions on grails transactions
目前,我们有一个调用事务服务的 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 块中通过,但日志仍然表明仍然抛出异常。我认为这就是事务回滚的原因。
但是如果我直接将 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)
这意味着当服务方法 运行s 它使用在您的作业中创建的当前事务。棘手的部分来了,当一个功能有多个事务部分时,它会评估每个部分的回滚条件,所以当你的 method() 抛出 RuntimeException 时,它会将你的事务设置为 rollbackOnly但一直持续到该事务结束(当您的 Model.withTransaction.. 完成时)并且就在那一刻它回滚了所有内容。
更深入一点,您可以在三个部分将事务设置为 rollbackOnly。
- 如果updateDatabase()抛出异常,所有的Transaction都会被设置为rollbackOnly
- 如果method()抛出Exception,所有的Transaction都会被设置为rollbackOnly
- 如果您传递给 withTransaction{..} 的闭包抛出异常,所有事务将被设置为 rollbackOnly。
Transaction结束后事务会回滚,那个时刻是withTransaction{..}结束后
所以您需要非常小心地处理您的交易。
要解决您的问题,您可以通过在您的服务中仅将 updateDatabase() 设置为事务性来使您的 method() 不是事务性的class 并删除服务名称上方的@Transactional。
class YourService {
@Transactional
def updateDatabase() {
//...
}
def method() {
//...
}
}
仅将一种方法设置为@Transactional 只会将该方法设置为事务性的,而不是让所有方法都是事务性的。
我做了一个项目作为示例,所以你可以运行 测试并自己检查,我还设置了 log4j 来显示事务的生命周期,这样你就可以更好地理解它,你只需要运行 MyProcessorIntegrationSpec.groovy
https://github.com/juandiegoh/grails-transactions-rollback
希望对您有所帮助!
目前,我们有一个调用事务服务的 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 块中通过,但日志仍然表明仍然抛出异常。我认为这就是事务回滚的原因。
但是如果我直接将 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)
这意味着当服务方法 运行s 它使用在您的作业中创建的当前事务。棘手的部分来了,当一个功能有多个事务部分时,它会评估每个部分的回滚条件,所以当你的 method() 抛出 RuntimeException 时,它会将你的事务设置为 rollbackOnly但一直持续到该事务结束(当您的 Model.withTransaction.. 完成时)并且就在那一刻它回滚了所有内容。
更深入一点,您可以在三个部分将事务设置为 rollbackOnly。
- 如果updateDatabase()抛出异常,所有的Transaction都会被设置为rollbackOnly
- 如果method()抛出Exception,所有的Transaction都会被设置为rollbackOnly
- 如果您传递给 withTransaction{..} 的闭包抛出异常,所有事务将被设置为 rollbackOnly。
Transaction结束后事务会回滚,那个时刻是withTransaction{..}结束后
所以您需要非常小心地处理您的交易。
要解决您的问题,您可以通过在您的服务中仅将 updateDatabase() 设置为事务性来使您的 method() 不是事务性的class 并删除服务名称上方的@Transactional。
class YourService {
@Transactional
def updateDatabase() {
//...
}
def method() {
//...
}
}
仅将一种方法设置为@Transactional 只会将该方法设置为事务性的,而不是让所有方法都是事务性的。
我做了一个项目作为示例,所以你可以运行 测试并自己检查,我还设置了 log4j 来显示事务的生命周期,这样你就可以更好地理解它,你只需要运行 MyProcessorIntegrationSpec.groovy
https://github.com/juandiegoh/grails-transactions-rollback
希望对您有所帮助!