سؤال

ملخصيمكن للوالد أن يكون لديه العديد من الأطفال.كيف تكتب خدمة بحيث إذا حدث خطأ بعد إضافة أحد الوالدين عند إضافة طفل، فسيتم التراجع عن المعاملة بأكملها.على سبيل المثال، أضف الأصل p1، وأضف الطفل c1 بنجاح، ثم عند إضافة الطفل c2 يحدث خطأ، يجب إرجاع كل من p1 وc1.

مشكلة مفصلة

في التعليمة البرمجية التالية، يوجد قيد فريد على خاصية اسم الطفل.لذا، إذا حاولت إضافة نفس الاسم مرتين مع أصل مختلف، فلا ينبغي إضافة السجل الفرعي ويجب إرجاع السجل الأصلي.

مشكلتي هي أنه لا يتم التراجع عن السجل الأصلي.

أنا أستخدم MySQL مع InnoDB مع Grails 1.2-M2 وTomcat 6.018.

مصدر البيانات

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)
    }
}

إدخال بيانات بسيط نظام الأفضليات المعمم

<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
    }

}
هل كانت مفيدة؟

المحلول

تحتاج أيضًا إلى التأكد من طرح 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 ووضع علامة على المعاملة يدويًا للتراجع إذا كان هناك خطأ في التحقق من الصحة.

نصائح أخرى

أعتقد أنه ينبغي أن يكون:

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

وبدلاً من ذلك، يمكنك استخدام خاصية 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.gorm.failOnError في Grails-app/conf/Config.groovy على true

لمزيد من المعلومات، راجع مستندات دليل المستخدم لـ "حفظ": http://grails.org/doc/latest/ref/Domain%20Classes/save.html

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top