Wie man Transaktionen Work In Grails Stellen
-
06-07-2019 - |
Frage
Zusammenfassung Ein Elternteil kann viele Kinder haben. Wie schreibt man einen Dienst, so dass, wenn nach einem Elternteil des Hinzufügen ein Fehler ist, wenn ein Kind hinzufügen, wird die gesamte Transaktion zurückgesetzt. Zum Beispiel Eltern p1 hinzufügen, erfolgreich Kind c1 hinzufügen, dann, wenn das Hinzufügen Kind c2 ein Fehler auftritt, sollten beide p1 und c1 zurück gerollt werden.
Detaillierte Problem
Im folgenden Code gibt es eine eindeutige Einschränkung auf den Namen des Vermögens des Kindes. Also, wenn Sie versuchen, den gleichen Namen zweimal mit einer anderen Mutter hinzufügen möchten, dann sollte das Kind Datensatz nicht hinzugefügt werden und der übergeordnete Datensatz sollte zurückgerollt werden.
Mein Problem ist, dass der übergeordnete Datensatz wird nicht zurückgerollt.
Ich bin mit MySQL w / InnoDB mit Grails 1.2-M2 und Tomcat 6.018.
Datenquelle
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"
}
}
}
Ich habe die folgenden einfachen Domain-Klassen:
Eltern :
class Parent {
static hasMany = [ children : Child ]
String name
static constraints = {
name(blank:false,unique:true)
}
}
Kind
class Child {
static belongsTo = Parent
String name
Parent parent
static constraints = {
name(blank:false,unique:true)
}
}
Einfache Dateneingabe 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>
Controller
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 = {}
}
Service
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
}
}
Lösung
Sie müssen auch ein Runtime sicherstellen, dass innerhalb des Service, um für die Transaktion automatisch zurückgerollt werden geworfen wird.
Also ich dies tun würde:
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
}
und dann Ausnahmen in der Steuerung fangen und die Fehler machen.
Der andere Weg ist die automatischen Transaktionen zu aktivieren und verwenden Parent.withTransaction und markieren Sie manuell die Transaktion für Rollback, wenn es einen Fehler bei der Überprüfung ist.
Andere Tipps
Ich glaube, es sollte sein:
class AddRecordsService {
static transactional = true;// note *static* not boolean
}
Alternativ können Sie die Eigenschaft FailOnError verwenden, wenn Sie Ihre Domain-Objekte Sparend - wenn das Speichern für einen Validierungsfehler fehlschlägt, dann wird es eine Ausnahme aus,
. def addChild(cName,Parent theParent) {
println "add child: ${cName}"
def theChild = new Child(name:cName,parent:theParent)
theChild.save(failOnError:true)
return theChild
}
Dieses Verhalten kann auch global aktiviert wird mit von der grails.gorm.failOnError Eigenschaft in Grails-app / conf / Config.groovy auf true
Weitere Informationen finden Sie im Benutzerhandbuch docs für 'speichern': http://grails.org/doc/latest/ref/Domain%20Classes/save.html