Резюме
Родитель может иметь много детей. Как написать сервис так, чтобы, если после добавления родителя возникла ошибка при добавлении потомка, откатилась вся транзакция. Например, добавьте родительский элемент p1, успешно добавьте дочерний элемент c1, затем при добавлении дочернего элемента c2 произойдет ошибка, оба параметра p1 и c1 следует откатить.
Подробная задача
В следующем коде есть уникальное ограничение на свойство name дочернего элемента. Поэтому, если вы попытаетесь добавить одно и то же имя дважды с другим родителем, дочерняя запись не должна быть добавлена, а родительская запись должна быть откатана.
Моя проблема в том, что родительская запись не откатывается.
Я использую MySQL с InnoDB с Grails 1.2-M2 и Tomcat 6.018.
Источник данных
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
}
}