2009-10-27 8 views
9

요약 부모는 많은 자식을 가질 수 있습니다. 부모를 추가 한 후에 자식을 추가 할 때 오류가 발생하면 전체 트랜잭션이 롤백되도록 서비스를 작성합니다. 예를 들어, 부모 p1을 추가하고 자식 c1을 성공적으로 추가 한 다음 자식 c2를 추가 할 때 오류가 발생하면 p1과 c1을 모두 롤백해야합니다.Grails에서 트랜잭션을 수행하는 방법

자세한 문제 다음 코드에서

는, 아이의 이름 속성에 고유 제한 조건이있다. 따라서 동일한 이름을 다른 부모와 함께 두 번 추가하려고하면 하위 레코드를 추가하지 말고 상위 레코드를 롤백해야합니다.

제 문제는 상위 레코드가 롤백되지 않는 것입니다.

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 
} 

을 다음 오류를 컨트롤러에서 예외를 포착하고 렌더링 :

그래서 나는이 작업을 수행 할 것입니다.

다른 방법은 자동 트랜잭션을 사용하고 Parent.withTransaction 을 사용하고 유효성 검사 오류가있는 경우 수동으로 트랜잭션을 롤백으로 표시하는 것입니다.

+0

그 중요한 세부 사항을 추가해 주셔서 감사합니다. –

+0

> 또한 > 트랜잭션이 자동으로 롤백되도록하려면 > 서비스 내에 RuntimeException이 throw되는지 확인해야합니다. 그건 내 문제 였어! 관례 적으로 grails가 이렇게해야 할 것처럼 보입니다. –

+0

유효성 검사가 실패 할 경우 null을 반환하는 대신 save() 예외를 throw하도록 구성 옵션이 있다고 생각합니다. – leebutts

3

나는 그것이 있어야 생각 :

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

감사합니다. 예, 확실히 정적 인 것으로되어 있습니다. 그러나 여전히 작동하지 않습니다. 지정하지 않으면 실제로 true로 기본 설정되어 있다고 생각합니다. 그러나 줄을 모두 없애면 작동하지 않습니다. 아마도 이것은 grails 버그입니까? 위의 코드를 수정했습니다. –

+0

실제로 부울은 작동하지만 정적이어야합니다. 실제 문제는 다음 답변에서 설명하는 예외를 던지지 않고있었습니다. –

2

또는 도메인 개체를 저장할 때 failOnError 속성을 사용할 수 있습니다. 유효성 검사 오류로 저장에 실패하면 예외가 throw됩니다.

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 속성을 설정하여 함께 전 세계적으로 활성화 할 수 있습니다에 대한 사용자 가이드 문서를 참조하십시오 '저장': 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 –

관련 문제