2

如果由于某种原因保存失败,以下代码将抛出 grails.validation.ValidationException。但结果是一般错误。我怎样才能知道错误的实际原因,以便将其报告给用户?

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

这是返回的堆栈跟踪。我碰巧知道它是由违反唯一约束引起的,因为我是故意造成的,但是跟踪中没有任何内容表明这是导致违反某些其他约束的原因。

org.codehaus.groovy.runtime.InvokerInvocationException: grails.validation.ValidationException: Validation Error(s) Occurred During Save

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)

    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436)

    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374)

    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)

    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)

    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)

    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)

    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)

    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)

    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)

    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)

    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)

    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)

    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)

    at java.lang.Thread.run(Thread.java:619)

Caused by: grails.validation.ValidationException: Validation Error(s) Occurred During Save

    at AddRecordsService.addChild(AddRecordsService.groovy:30)

    at AddRecordsService$addChild.callCurrent(Unknown Source)

    at AddRecordsService.addAll(AddRecordsService.groovy:11)

    at AddRecordsService$$FastClassByCGLIB$$e47d68f4.invoke(<generated>)

    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)

    at AddRecordsService$$EnhancerByCGLIB$$cdfdcc61.addAll(<generated>)

    at AddRecordsService$addAll.call(Unknown Source)

    at AddrecordController$_closure2.doCall(AddrecordController.groovy:14)

    at AddrecordController$_closure2.doCall(AddrecordController.groovy)

    ... 32 more

更新

好的,至少目前看来,让事务回滚并找出导致错误的唯一方法是检查保存是否失败,获取 failedobject.errors 并抛出 RuntimeException。但是现在如何将错误传递回调用控制器?以下不起作用。

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

       //theChild.save(failOnError:true)
       //theChild.save()


        if(!theChild.save()){
            println theChild.errors
             throw new RuntimeException(theChild.errors)
            //throw new RuntimeException('unable to save child')
        } 


        return theChild
    }
4

4 回答 4

2

Old question but if somebody stumbles upon it: in a service, the Grails (2.1) idiom is to call save() and throw a ValidationException if save failed.

 if (!theChild.save()) {
     throw new ValidationException(theChild.errors)
 } 

You typically do not call save(failOnError: true). Instead, in your controller, you catch the ValidationException:

try {
    service.addChild(child)
} catch (ValidationException e) {
    errors.allErrors.each {
        // do something with each error code
    }
}

Same idea if you do all the work in your controller. In that case, you do not need to throw then catch an exception of course, you can process the child.errors on the spot.

于 2013-01-14T13:35:38.053 回答
0

Granted, the save(failOnError:true) approach currently yields too little information about what validation caused the problem. (Do you really want to parse exception messages in the controller anyway?) However, there are multiple alternatives you might consider to deal with two different issues, transaction rollback and communicating validation error information.

One alternative is to avoid the persistence of the data altogether. First, call theChild.validate() to see if theChild can be saved. If no validations fail, validate() returns true, so call theChild.save(). If validation errors occur, they will be recorded in the errors object on theChild. The newly created child returned to the calling controller can then be inspected for errors or simply displayed to the user in the view.

Another alternative is to not use declarative transactions; i.e. set static transactional = false on the service. Then, your code could manage the transaction itself; something like this:

Child.withTransaction { txStatus ->
   try {
     child.save(failOnError:true)
   } catch (ValidationException e) {
     // rollback, but swallow exception, requiring caller to check child for errors
     txStatus.setRollbackOnly()
   }
}

Finally, your question implies you want the controller to do something with the errors information, such as rendering the original page so a user can correct entries. Since theChild is what's validated, returning the child won't work when an exception is being thrown (your own or ValidationException). However, the child will have errors populated during the save() call. Instead of passing in a String and Parent, then instantiating a Child in the service, you should consider instantiating the Child in the caller and passing it in by reference. Then, when the save() fails, the child's errors will be populated, even if the save(failOnError:true) results in a ValidationException. The caller can catch the ValidationException and then pass the child back as part of the model for the view.

Personally, I think the first alternative (call validate() first) or the last (instantiate the child outside the service) is preferable to managing transactions manually. In Grails, the declarative transaction management is all or nothing for the entire service. That is, if you set static transactional = false for the service, you'll have to manually manage all transactions in every method of the service. The KISS principle should be applied here.

于 2009-10-30T06:31:20.930 回答
0

我认为 ValidationException 引用了验证失败的对象?如果确实如此,则检查错误列表中的错误属性。

如果没有,您将需要捕获它,然后检查 theChild.errors 是否有错误。

于 2009-10-29T02:41:24.867 回答
0

尝试使用 grails 命令类进行验证REFER HERE

例子 :

class ParentCommand  {
    .....field.....
     ........constraint.....
   }


def addChild(cName,ParentCommand cmd) {
     if(cmd.hasErrors()) {
    render (cmd.errors as JSON).toString();
    return "false";
} 
于 2014-01-28T14:45:44.403 回答