在Grails服务中回滚事务

13

我一直在使用Grail的回滚功能,将所有服务更新为事务性服务,当服务抛出RuntimeException时会自动回滚。在大多数情况下,我都是这样做的:

def domain = new Domain(field: field)
if (!domain.save()) {
   throw new RuntimeException()
}

无论如何,我想验证这确实会回滚事务......这让我想到,此时事务是否已经提交。另外,如果没有提交,设置flush:true会改变吗?我不太熟悉Spring/Hibernate如何处理所有这些:)

2个回答

17

没错,那就可以了。

Grails中,默认情况下,事务是在Service方法级别处理的。如果方法正常返回,则事务将被提交;如果抛出RuntimeException,则事务将回滚。

请注意,这意味着即使在服务器方法中使用flush:true保存对象,如果抛出RuntimeException,则数据库更改仍将被回滚。

例如:

class MyService {

 def fiddle(id,id2){
   def domain = Domain.findById(id)

   domain.stuff = "A change"
   domain.save( flush:true ) // will cause hibernate to perform the update statements

   def otherDomain = OtherDomain.findById(id2)      

   otherDomain.name = "Fiddled"

   if( !otherDomain.save( flush:true )){ // will also write to the db
     // the transaction will be roled back 
     throw new RuntimeException("Panic what the hell happened")
   }                                                           
 }
}

对于Grails,我不是100%清楚的是,如果在纯Java / Spring世界中抛出了一个已检查的异常会发生什么,默认行为是事务拦截器提交事务,尽管可以在配置中覆盖此行为。

注意:有一个警告,即您的数据库必须支持要更新的表上的事务。是的,这是针对MySQL的批评 :)

这也适用于Domain.withTransaction方法。


扩展RuntimeException的自定义异常应该没问题,对吧?此外,您可以通过设置:dialect = org.hibernate.dialect.MySQLInnoDBDialect在dataSource中启用MySQL事务 :) - RyanLynch
2
是的,任何RuntimeException都会强制执行回滚,并且InnoDB是MySQL的一个不错的选择,只有在您真正需要性能时才考虑其他表类型。 - Gareth Davis
1
请注意,拥有任何已检查的异常将导致事务 不会 回滚。请参见下面的答案获取更多信息。 - Charles Wood

4

我想对已接受的答案添加附加评论,但这太长了,无法在注释中完成。

我对Grails不是100%清楚的是,如果抛出已检查的异常会发生什么。

默认情况下,该异常必须未被检查,否则事务将不会回滚。显然这是Spring的一个特性。

如果您确实想要在方法上检查异常,可以将服务方法显式标记为@Transactional,并使用rollbackFor参数列出仍应导致回滚的异常。(请注意,我实际上没有测试过这个功能。)

请注意,将服务中的任何一个方法标记为@Transactional会禁用自动包装其其他方法的事务。因此,如果您对其中之一执行此操作,则必须对其中所有方法执行此操作。确保您真正需要声明这些已检查的异常;)

您可以在http://docs.grails.org/latest/guide/services.html上阅读更多信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接