2009-10-27 19 views
9

概要 親は多くの子を持つことができます。親を追加した後に子を追加するときにエラーが発生すると、トランザクション全体がロールバックされるようなサービスをどのように作成しますか?たとえば、親p1を追加し、子c1を追加した後、子c2を追加するとエラーが発生すると、p1とc1の両方をロールバックする必要があります。次のコードでトランザクションをGrailsで動作させる方法

詳細な問題

、子のnameプロパティのユニークな制約があります。したがって、同じ名前を別の親に2回追加しようとすると、子レコードを追加して親レコードをロールバックするべきではありません。

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

Grails 1.2-M2とTomcat 6.018で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 
    } 

} 

答えて

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 を使用して、検証エラーがある場合にトランザクションをロールバックに手動でマークすることです。

+0

これらの重要な詳細を追加していただきありがとうございます。 –

+0

>また、 >トランザクションを自動的にロールバックするために、 >サービス内にRuntimeExceptionがスローされるようにする必要があります。 それは私の問題でした! grailsのようなものが慣例によってこれを行うべきだと思われる。 –

+0

バリデーションが失敗した場合にnullを返す代わりに、save()が例外をスローするように設定するオプションがあると思います – leebutts

3

私はそれがあるべきと考えている:

class AddRecordsService { 
    static transactional = true;// note *static* not boolean 
} 
+0

ありがとうございました! はい、間違いなく静的になっているはずです。しかし、それはまだ動作しません。 実際には、指定されていないと、デフォルトになると思われます。しかし、すべての行を一緒に削除しても動作しません。 おそらくこれはgrailsのバグでしょうか? 上記のコードを修正しました。 –

+0

ブール値は実際には機能しますが、静的である必要があります。実際の問題は次の答えで説明されているように例外を投げていませんでした。 –

2

また、ドメインオブジェクトを保存するときにfailOnErrorプロパティを使用することもできます。保存が検証エラーで失敗した場合、例外がスローされます。この動作はまた、ユーザガイドのドキュメントを参照してください、詳細については真

にGrailsのアプリ/ confに/ Config.groovyでgrails.gorm.failOnErrorプロパティを設定することで、グローバルで有効にすることができる

def addChild(cName,Parent theParent) { 
    println "add child: ${cName}" 
    def theChild = new Child(name:cName,parent:theParent) 
    theChild.save(failOnError:true) 
    return theChild 
} 

「保存」:http://grails.org/doc/latest/ref/Domain%20Classes/save.html

+0

theChild.save(failOnError:true)は機能しますが、Config.groovyでプロパティファイルを設定すると表示されません。 こちら - フォローアップの質問があります: http://stackoverflow.com/questions/1640666/how-to-know-the-cause-of-validation-error –

関連する問題