9

まとめ 親は多くの子を持つことができます。親を追加した後、子を追加するときにエラーが発生した場合、トランザクション全体がロールバックされるようなサービスをどのように記述しますか。たとえば、親 p1 を追加し、子 c1 を正常に追加した後、子 c2 を追加するときにエラーが発生した場合、p1 と c1 の両方をロールバックする必要があります。

詳細な問題

次のコードでは、子の name プロパティに一意の制約があります。したがって、別の親に同じ名前を 2 回追加しようとすると、子レコードは追加されず、親レコードはロールバックされます。

私の問題は、親レコードがロールバックされていないことです。

Grails 1.2-M2 および Tomcat 6.018 で MySQL と InnoDB を使用しています。

情報源

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
    }

}
4

3 に答える 3

5

また、トランザクションを自動的にロールバックするには、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
}

次に、コントローラーで例外をキャッチし、エラーをレンダリングします。

もう1つの方法は、自動トランザクションをオフにしてParent.withTransactionを使用し、検証エラーが発生した場合にトランザクションにロールバックのマークを手動で付けることです。

于 2009-10-27T09:54:26.030 に答える
3

私はそれがそうであるべきだと信じています:

class AddRecordsService {
    static transactional = true;// note *static* not boolean
}
于 2009-10-27T07:04:58.500 に答える
2

または、ドメイン オブジェクトを保存するときに 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 に設定することで、グローバルに有効にすることもできます。

詳細については、'save' のユーザー ガイド ドキュメントを参照してください: http://grails.org/doc/latest/ref/Domain%20Classes/save.html

于 2009-10-27T16:07:21.300 に答える