Grails: how to structure transactions when I want to continue validating even after the transaction has already failed

后端 未结 2 1277
臣服心动
臣服心动 2021-01-26 16:45

My users are uploading a csv or xls or whatever and each line is going to be an instance of a domain object I save. If any of the lines fail I want the whole thing rolled back,

相关标签:
2条回答
  • 2021-01-26 17:15

    here is what i am thinking:

    1 . Set a flag that signals ALL CLEAR and commit the transaction manually at the end if all is clear.

    or

    2 . Commit each line in a separate transaction capturing errors of failed lines and skipping over failures.

    0 讨论(0)
  • 2021-01-26 17:16

    You can validate the objects without having to save them: ( http://grails.org/doc/2.0.x/guide/validation.html#validatingConstraints). So in a service you can create all of the objects, then validate all of the objects, then save all of the objects. Something similar to:

    def serviceMethod(data) {
        def listOfObjects = createObjectsFromData(data)
        listOfObjects*.validate()
        def anErrorOccurred = listOfObjects.find {it.hasErrors()} != null
        if(anErrorOccurred) {
            return listOfObjects
        }
        listOfObjects*.save(validate: false) //you could use the validate:false or leave it out.  I figure since we've already validated that you could do without re-validating.
    }
    

    This way you can collect all of your errors and not have to worry about rolling back the transaction. Problem with this setup is you'll be creating N number of objects and holding onto all of them. If your file is longer than 100k rows (a slightly educated guess on where you'll start to suffer) then this might cause some performance issues. If you don't like the above method you could handle the transaction manually: ( http://grails.org/doc/2.0.x/ref/Domain%20Classes/withTransaction.html)

    def serviceMethod(data) {
        MyDomainClass.withTransaction { status ->
            def listOfObjects = []
            data.each {
                def domainObject = createObjectFromData(it)
                lisOfObjects << domainObject.save()
            }
            def anErrorOccurred = lisOfObjects.find {it.hasErrors()} != null
            if(anErrorOccurred) {
               status.setRollbackOnly()  //will roll back all of the transactions surrounded by the .withTransaction {}
            }
        }
    }
    

    You're still holding onto all of the objects here (since you want to retrieve ALL errors that occur). One way I can think of to avoid holding onto all of the objects would be to create the objects one at a time and validate them one by one adding errors to a list when applicable, but then you'd have to recreate all of the objects when they all pass validation which doesn't seem very efficient either.

    0 讨论(0)
提交回复
热议问题