【问题标题】:How to know exception occurred within grails transaction?如何知道 grails 事务中发生异常?
【发布时间】:2014-07-16 21:25:33
【问题描述】:

我有一个服务方法,它在事务中执行一些操作。

public User method1() {
    // some code...
    Vehicle.withTransaction { status ->
        // some collection loop
        // some other delete
        vehicle.delete(failOnError:true)
    }

    if (checkSomething outside transaction) {
        return throw some user defined exception
    }

    return user
}

如果存在运行时异常,我们不必捕获该异常,事务将自动回滚。但是如何确定由于某些异常而回滚的事务,我想抛出一些用户友好的错误消息。 delete() 调用也不会返回任何内容。

如果我通过捕获异常(超类)在事务中添加 try/catch 块,则它不会进入该异常块。但我期待它进入那个块并抛出用户友好的异常。

编辑1:添加try/catcharround withTransaction是个好主意

知道如何解决这个问题吗?提前致谢。

【问题讨论】:

    标签: exception grails transactions


    【解决方案1】:

    如果我正确理解您的问题,您想知道如何捕获异常、确定异常是什么并向用户返回消息。有几种方法可以做到这一点。我会告诉你我是怎么做的。

    在开始编写代码之前,我可能会提出一些建议。首先,您不需要在服务中显式声明事务(我使用的是 v2.2.5)。服务默认是事务性的(没什么大不了的)。

    其次,如果在执行服务方法时发生任何异常,事务将自动回滚。

    第三,我建议从save() 中删除failOnError:true(我认为它不适用于delete()...我可能错了?)。我发现在服务中运行validate()save() 然后将模型实例返回到控制器更容易,在该控制器中可以在闪存消息中使用对象错误。

    以下是我喜欢如何使用服务方法和控制器中的 try/catch 处理异常和保存的示例:

    class FooService {
        def saveFoo(Foo fooInstance) {
            return fooInstance.save()
        }
    
        def anotherSaveFoo(Foo fooInstance) {
            if(fooInstance.validate()){
                fooInstance.save()
            }else{
                do something else or
                throw new CustomException()
            }
    
            return fooInstance
        }
    
    }
    
    class FooController {
        def save = {
            def newFoo = new Foo(params)
            try{
                returnedFoo = fooService.saveFoo(newFoo)
            }catch(CustomException | Exception e){
                flash.warning = [message(code: 'foo.validation.error.message',
                        args: [org.apache.commons.lang.exception.ExceptionUtils.getRootCauseMessage(e)],
                        default: "The foo changes did not pass validation.<br/>{0}")]
                redirect('to where ever you need to go')
                return
    
            }
    
            if(returnedFoo.hasErrors()){
                def fooErrors = returnedFoo.errors.getAllErrors()
    
                flash.warning = [message(code: 'foo.validation.error.message',
                        args: [fooErrors],
                        default: "The foo changes did not pass validation.<br/>${fooErrors}")]
                redirect('to where ever you need to go')
                return
            }else {
                flash.success = [message(code: 'foo.saved.successfully.message',
                                    default: "The foo was saved successfully")]
                redirect('to where ever you need to go')
            }
        }
    }
    

    希望这会有所帮助,或者从更有经验的 Grails 开发人员那里获得一些其他意见。

    以下是我发现的其他几种获取异常信息以传递给用户的方法:

    request.exception.cause
    request.exception.cause.message
    response.status
    

    一些其他相关问题的链接可能会有所帮助:

    Exception handling in Grails controllers

    Exception handling in Grails controllers with ExceptionMapper in Grails 2.2.4 best practice

    https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html

    【讨论】:

    • 谢谢@CheddarMonkey。因此,您建议使用 try/catch 围绕服务调用。那么我在问题中的“编辑1”是正确的方法吗??!!
    • 您希望控制器中的 try/catch 围绕对服务的调用,就像它包装 returnedFoo = fooService.saveFoo(newFoo) 的示例中一样
    • 是的,但是在我们的控制器中,我们试图避免 try/catch 并使其变得简单,只需调用服务,服务就会放置响应消息,控制器只需要传递它.因为在某些场景中,我们对不同类的服务 api 有许多不同的调用,那么控制器将由 try/catch 填充。感谢您的投入。
    • 我不确定您为什么在控制器中遇到 try/catch 问题。如果这是您的偏好,那么我的示例将不适合您。我更喜欢这个模型,因为它将所有相关的错误和异常消息传递给控制器​​,在那里它可以在视图中呈现。我想不出更好的方法将该信息传递给控制器​​。如果您提出其他解决方案,请发布。我很想看看是否有更简单的方法来做到这一点。
    猜你喜欢
    • 2021-02-15
    • 2023-03-26
    • 2013-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-07
    相关资源
    最近更新 更多