如何使事务在 Grails 中工作
Posted
技术标签:
【中文标题】如何使事务在 Grails 中工作【英文标题】:How To Make Transactions Work In Grails 【发布时间】:2010-12-10 09:10:16 【问题描述】:总结 一个父母可以有很多孩子。你如何编写一个服务,如果在添加父级后添加子级时出错,则整个事务会回滚。比如添加父p1,成功添加子c1,那么添加子c2时出现错误,p1和c1都要回滚。
详细问题
在下面的代码中,对孩子的 name 属性有一个唯一的约束。因此,如果您尝试使用不同的父项添加两次相同的名称,则不应添加子记录,而应回滚父记录。
我的问题是父记录没有被回滚。
我正在使用带有 Grails 1.2-M2 和 Tomcat 6.018 的带有 InnoDB 的 mysql。
数据源
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
dataSource
configClass = GrailsAnnotationConfiguration.class
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
dialect = org.hibernate.dialect.MySQLInnoDBDialect
zeroDateTimeBehavior="convertToNull" //Java can't convert ''0000-00-00 00:00:00' to TIMESTAMP
username = "root"
password = "12345"
loggingSql=false
hibernate
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='com.opensymphony.oscache.hibernate.OSCacheProvider'
// environment specific settings
environments
development
dataSource
dbCreate = "create-drop" // one of 'create', 'create-drop','update'
url = "jdbc:mysql://localhost:3306/transtest?zeroDateTimeBehavior=convertToNull"
test
dataSource
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/transtest?zeroDateTimeBehavior=convertToNull"
production
dataSource
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/transtest?zeroDateTimeBehavior=convertToNull"
我有以下简单的域类:
家长:
class Parent
static hasMany = [ children : Child ]
String name
static constraints =
name(blank:false,unique:true)
儿童
class Child
static belongsTo = Parent
String name
Parent parent
static constraints =
name(blank:false,unique:true)
简单的数据输入 GSP
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Sample title</title>
</head>
<body>
<h1>Add A Record</h1>
<g:form action="add" name="doAdd">
<table>
<tr>
<td>
Parent Name
</td>
<td>
Child Name
</td>
</tr>
<tr>
<td>
<g:textField name="parentName" />
</td>
<td>
<g:textField name="childName" />
</td>
</tr>
<tr><td><g:submitButton name="update" value="Update" /></td></tr>
</table>
</g:form>
</body>
</html>
控制器
class AddrecordController
def addRecordsService
def index =
redirect action:"show", params:params
def add =
println "do add"
addRecordsService.addAll(params)
redirect action:"show", params:params
def show =
服务
class AddRecordsService
// boolean transactional = true //shouldn't this be all I need?
static transactional = true // this should work but still doesn't nor does it work if the line is left out completely
def addAll(params)
println "add all"
println params
def Parent theParent = addParent(params.parentName)
def Child theChild = addChild(params.childName,theParent)
println theParent
println theChild
def addParent(pName)
println "add parent: $pName"
def theParent = new Parent(name:pName)
theParent.save()
return theParent
def addChild(cName,Parent theParent)
println "add child: $cName"
def theChild = new Child(name:cName,parent:theParent)
theChild.save()
return theChild
【问题讨论】:
【参考方案1】:或者,您可以在保存域对象时使用 failOnError 属性 - 如果保存因验证错误而失败,则会引发异常。
def addChild(cName,Parent theParent)
println "add child: $cName"
def theChild = new Child(name:cName,parent:theParent)
theChild.save(failOnError:true)
return theChild
也可以通过将 grails-app/conf/Config.groovy 中的 grails.gorm.failOnError 属性设置为 true 来全局启用此行为
有关更多信息,请参阅“保存”的用户指南文档:http://grails.org/doc/latest/ref/Domain%20Classes/save.html
【讨论】:
看来 theChild.save(failOnError:true) 有效,但在 Config.groovy 中设置属性文件却没有。顺便说一句 - 我在这里有一个后续问题:***.com/questions/1640666/…【参考方案2】:您还需要确保在服务内引发 RuntimeException,以便事务自动回滚。
所以我会这样做:
def addParent(pName)
println "add parent: $pName"
def theParent = new Parent(name:pName)
if(!theParent.save())
throw new RuntimeException('unable to save parent')
return theParent
def addChild(cName,Parent theParent)
println "add child: $cName"
def theChild = new Child(name:cName,parent:theParent)
theChild.save()
if(!child.save())
throw new RuntimeException('unable to save child')
return theChild
然后在控制器中捕获异常并呈现错误。
另一种方法是关闭自动交易并使用 Parent.withTransaction 如果出现验证错误,则手动将事务标记为回滚。
【讨论】:
感谢您添加这些重要细节。 >您还需要确保在 >service 中引发 RuntimeException,以便事务自动回滚 >back。那是我的问题!似乎 grails 应该按照惯例这样做。 我认为 1.2 版有一个配置选项可以让 save() 在验证失败时抛出异常而不是返回 null 我有一个后续问题:***.com/questions/1640666/… .save(failOnError:true) 会完成这个吗?【参考方案3】:我认为应该是:
class AddRecordsService
static transactional = true;// note *static* not boolean
【讨论】:
谢谢!是的,它绝对应该是静态的。但是,它仍然不起作用。我认为如果未指定,它实际上应该默认为 true。但是将这条线全部删除也不起作用。也许这是一个 grails 错误?我更正了上面的代码。 事实证明 boolean 确实有效,但应该是静态的。真正的问题是没有抛出异常,如下一个答案中所述。以上是关于如何使事务在 Grails 中工作的主要内容,如果未能解决你的问题,请参考以下文章
Grails - Ionic - AngularJS - 在这个环境中工作是个好主意吗?
Grails:Spring Security CAS 在 2.2.3 中工作,但在 2.3.0 中不工作
如何在 IntelliJ IDEA Grails 项目中为 groovy 方法获取 javadocs / groovydocs?