views:

25

answers:

2

I am porting a Grails application from Oracle to MySQL database. The original Oracle version is a legacy database which uses a few complex Views leveraging Oracle's INSTEAD OF INSERT OR UPDATE feature which MySQL doesn't have. As a workaround I have implement Insert and Update methods on the Domain classes which point to these kinds of Views. For example,

class AdminUser {
    //...
    def update() {
        if(this.validate()) {
            Sql sql = Utils.getHibernateSql()

            sql.execute(
                "update table_x ...",
                [...]
            )

            sql.execute(
                "update table_y ...)",
                [...]
            )

            sql.execute(
                "update table_z ...",
                [...]
            )

            return true
        }

        return false
    }
    //...
}

Here is a description of the problem I am running into at the moment:

  1. Within a service method I load an instance of AdminUser using AdminUser.get
  2. I modify the loaded instance and call update(), all appears well
  3. Once the service method finishes executing an exception is thrown due to something calling save on the instance.

How can I prevent the magical save happening in step (3) (I recently read somewhere that Grails will automatically save a modified Domain class instance which hasn't yet been saved upon exiting a service method, but I can't seem to find the link to that resource right now)?

+1  A: 

You're leaving the modified instance in the hibernate session with dirty fields. When the hibernate session gets flushed, it will try to save the object again with the usual save() method.

One solution would be to discard your object from the hibernate session after you've manually saved the changes. For example:

def update() {
    if(this.validate()) {
        Sql sql = Utils.getHibernateSql()

        sql.execute(
            "update table_z ...",
            [...]
        )

        ...

        this.discard()  // remove the object from the hibernate session
        return true
    }

In addition, you can add this to your Config.groovy to require that objects be saved explicitly:

hibernate.flush.mode="manual"
ataylor
Excellent, that solved it, thank you!
Stephen Swensen
A: 

If you use read() instead of get(), then the object will not auto-persist changes. You can still call save() explicitly, but the standard behavior where the OpenSessionInView interceptor flushes all unsaved dirty instances will skip the instances loaded with read().

Burt Beckwith
I'm not sure read() is appropriate: I need to modify, validate, and perform manual insert / update on the domain instance. From what you are saying it sounds like I could still do all those things even using read(), I had assumed the object would throw an exception or something if I tried to modify it. But either way, I feel more comfortable with the solution given by @ataylor. Thank you.
Stephen Swensen
As I said, you can still call save() (and/or validate()) explicitly and it will persist. But it doesn't auto-persist.
Burt Beckwith