Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Domain Merge does not flush dirty fields to datastore #636

Open
chrisbrookes opened this issue Nov 1, 2022 · 2 comments
Open

Domain Merge does not flush dirty fields to datastore #636

chrisbrookes opened this issue Nov 1, 2022 · 2 comments

Comments

@chrisbrookes
Copy link

I'm trying to manage updates to a domain object through different controller actions with the object stored in the session. At the end of the editing I'm doing a merge to push the changed fields of the object to the datastore. The merge completes without error but the changes are not sent to the database.

I've reproduced with a simple project -
grailsMergeBug-bug-report-01112022.zip. Go to http://localhost:8080/data to reproduce.

The domain object looks like this:

package grailsmergebug

import groovy.transform.ToString

@ToString(includePackage = false, includes = ['name', 'number'], includeNames = true)
class Data {

    String name
    Integer number

    static constraints = {}
}

Changes to the name or number fields do not get merge saved.
I've simulated the editing, storing in session and merge in a controller:

...
    @Transactional
    def index() {

        Data.deleteAll([flush: true], Data.list())
        Data data = new Data(name: "data1", number: 1)
        data.save()
        
        redirect(action: 'putInSession')
    }

    def putInSession() {

        Data data = Data.list().first()
        data.name = "dirtied"
        data.number += 1

        logDirtyStatus(data, "putInSession")
        session.data = data

        redirect(action: 'tryMerge')
    }

    @Transactional
    def tryMerge() {

        Data data = session.data
        logDirtyStatus(data, "tryMerge")

        log.debug "doing merge..."
        def mergeRet = data.merge(failOnError: true, flush: true)
        log.debug "merge return data: $data"
        session.data = null

        redirect(action: 'checkSaved')
    }

    def checkSaved() {

        Data data = Data.list().first()
        log.debug "checkSaved data: $data"

        render "$data -- ${data.name == 'dirtied' ? 'changed OK' : 'did not merge the changes'}"
    }

    private def logDirtyStatus(Data data, String prefix)
    {
        log.debug "[$prefix] val: $data.name | hasChanged: ${data.hasChanged()} | hasChanged(field): ${data.hasChanged("name")} | " +
            "list dirty: ${data.listDirtyPropertyNames()} | get dirty: ${data.getDirtyPropertyNames()} | " +
            "dirty: ${data.dirty} | dirty(field): ${data.isDirty("name")}"
    }
...

Going to checkSaved action in a browser shows that the object has not been updated.

Versions:

grailsVersion=5.2.3
grailsGradlePluginVersion=5.2.3
groovyVersion=3.0.11
gorm.version=7.3.2
@chrisbrookes
Copy link
Author

The only workaround I've found for this is to call markDirty() on the object returned from the merge method:

...
        log.debug "doing merge..."
        def mergeRet = data.merge(failOnError: true, flush: true)
        log.debug "merge return data: $data"
        mergeRet.markDirty() // todo: this forces the save of the merged entity - otherwise the changes do not get pushed out to the DB
...

The object then gets saved after the method exits and the transaction gets flushed / committed. Probably quicker not to pass in flush: true to the merge when doing it this way.

@puneetbehl
Copy link
Contributor

Please push the code to a separate GitHub repo. I am a little confused as to what you are trying to do. One suggestion, I have is to move the code related to GORM operations into a separate service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants