开发者

Transactional behavior of Grails Service classes with chained calls

I am parsing xml files in my application and I am struggling a bit on how to design this.

I allow for uploading of "incomplete" trees of our xml schemas, meaning that as long as the tree is well formed, it can be any sub-child of the root node. Most of the nodes have child nodes that contain only some text (properties), but I have not included any of that in my small xml structure example.

<root>
    <childFoo>
        <childBar>
        </childBar开发者_开发技巧>
    </childFoo>
</root>

Any one of those nodes are allowed. Now, i have designed a XmlInputService that has methods to parse the various nodes. And i just detect in the controller what kind of node it is, and hand it over to the service method accordingly.

So to keep my code DRY and nice, I re-use my methods in the higher levels. If I pass of a document of type Root to the service, it will parse whatever fields in root that belong directly in root in there, and pass of the children nodes (that represent children in my domain class structure) to the appropiate parsing method in the service.

Now, if a user uploads xml that contains constraint violations, i.e. an element with a non-unique name etc, I obviously want to roll this back.

Lets say i call parseRoot() and go downwards, calling parseChildFoo().

In there i call parseChildBar() for every Bar child in there. If one of the Bar children cannot validate because of constraints or whatever, I obviously want to cascade the roll back of the transaction all the way up to parseFoo().

How would I achieve this?


If you have a grails Service that has method that takes care of the parsing, you should throw a an exception that extends java.lang.RuntimeException from your service so that the user can be informed that they need to modify their xml. So, your controller will catch that exception and provide the user with a meaningful error message

The rolling back of any database-modifications will be done automatically by Grails/Spring whenever a runtimeexception is thrown from a service method.

The advantage of the approach that I am describing over Victor's answer is that you don't have to write any code to let the transaction roll-back in case of failure. Grails will do it for you. IMO, to use the withTransaction closure inside a service method makes no sense.

More info here


Make those validity rules validation constraints on domain objects.

When save() violates the constraints, throw an exception and catch it on top parse level, then roll back an entire transaction. Like:

meServiceMethod() {
    ...
    FooDomainClass.withTransaction { status ->
        try {
            parseRoot(xml)
        } 
        catch (FooBarParentException e) {
            status.setRollbackOnly()
            // whatever error diagnostics
        }
    }
    ...
}

Or you can simply let the exception fly out of service method to controller - service methods are transactional by default.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜