Search code examples
grailsgrails-ormgrails-2.0grails-domain-classgrails-controller

Grails edit working abnormally updating database on value assign


I am using grails-2.1.1. When I load the edit page, I am assigning some value in the edit action in controller. But it is updating my table! although I am not saving. How can I stop it?

Here is my code below. My edit action in controller:

def edit() {
    def accTxnMstInstance = AccTxnMst.get(params.id)
    if (!accTxnMstInstance) {
        flash.message = message(code: 'default.not.found.message', args: [message(code: 'accTxnMst.label', default: 'AccTxnMst'), params.id])
        redirect(action: "list")
        return
    }

    accTxnMstInstance?.accTxnDtls?.each {
        if (it?.debitCoa != null && it?.debitCoa != "") {
            String debitCoaVal = ""
            List<String> items = Arrays.asList(it?.debitCoa?.split("\\s*~\\s*"))
            items.each {
                List itemList = new ArrayList()
                List<String> subItems = Arrays.asList(it.split("\\^"))
                subItems.each {
                    itemList.add(it)
                }
                itemList.add("false")
                itemList.add("0")

                itemList.each {
                    debitCoaVal += it.toString() + "^"
                }
                debitCoaVal += "~"
            }

            it?.debitCoa = debitCoaVal
            debitCoaVal = ""
        }

        if (it?.creditCoa != null && it?.creditCoa != "") {
            String creditCoaVal = ""
            List<String> items = Arrays.asList(it?.creditCoa?.split("\\s*~\\s*"))
            items.each {
                List itemList = new ArrayList()
                List<String> subItems = Arrays.asList(it.split("\\^"))
                subItems.each {
                    itemList.add(it)
                }
                itemList.add("false")
                itemList.add("0")

                itemList.each {
                    creditCoaVal += it.toString() + "^"
                }
                creditCoaVal += "~"
            }

            it?.creditCoa = creditCoaVal
            creditCoaVal = ""
        }
    }

    [accTxnMstInstance: accTxnMstInstance]
}

You can see that I am not saving after assigning the value just passing to view.


Solution

  • Grails uses the Open Session In View (OSIV) pattern, where at the beginning of the web request a Hibernate session is opened (and stored in a thread-local to make it easily accessible) and at the end of the request as long as there wasn't an exception, the Hibernate session is flushed and closed. During any flush, Hibernate looks at all "active" object instances and loops through each persistent property to see if it is "dirty". If so, even though you didn't explicitly call save(), your changes will be pushed to the database for you. This is possible because when Hibernate creates an instance from a database row it caches the original data to compare later to the potentially-changed instance properties.

    A lot of the time this is helpful behavior, but in cases like this it gets in the way. There are lots of fixes though. One drastic one is to disable OSIV, but this is generally a bad idea unless you know what you're doing. In this case there are two things you can try that should work.

    One is to change AccTxnMst.get(params.id) to AccTxnMst.read(params.id). This will not cause the instance to be strictly "read-only" because you can still explicitly call save() and if something was modified, all of the instance changes will be persisted. But the caching of the original data isn't done for instances retrieved using read(), and there's no dirty checking during flush for these instances (which isn't possible anyway since there's no cached data to compare with).

    Using read() is a good idea in general when retrieving instances that are not going to be updated (whether you make property changes or not), and makes the code more self-documenting.

    Another option is to call discard() on the instance before the controller action finishes. This "detaches" the instance from the Hibernate session, so when the OSIV filter runs at the end of the request and flushes the Hibernate session, your instance won't be considered dirty since Hibernate won't have access to it.

    read() only makes sense for individual instances retrieved by id, whereas discard() is useful for any instance, e.g. if they're in a mapped collection or were retrieved by a non-id query (e.g. a dynamic finder, criteria query, etc.)