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
    }

}
War es hilfreich?

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top