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

GORM 6.1 regression: Setting fields directly with methods on domain objects doesn't save the values #961

Closed
4 tasks done
abryzakez opened this issue Jun 19, 2017 · 2 comments

Comments

@abryzakez
Copy link

abryzakez commented Jun 19, 2017

Task List

  • Steps to reproduce provided
  • Stacktrace (if present) provided
  • Example that reproduces the problem uploaded to Github
  • Full description of the issue provided (see below)

Steps to Reproduce

  1. Create a new app using GORM 6.1
  2. Create two domain objects, A & B
    2.1. A should have two fields: String description, B b
    2.2. A should have a toString method which prints details of the fields
  3. Create two methods on A, one to update description, one to update b
  4. Create a controller with methods to create A & B entities, set the individual fields on A using the created methods and list A objects

Expected Behaviour

Updating a field directly using a method on A should persist that change to the database

Actual Behaviour

Since GORM 6.1 this no longer saves the value of the fields.

It seems related to dirtyness checking, with the activateDirtyChecking call added to GrailsEntityDirtinessStrategy.onPostLoad in commit a007266

That causes the DirtyCheckable.$changedProperties to be assigned an empty map and GrailsEntityDirtinessStrategy.isDirty returns false as the direct field sets for the fields don't mark them as dirty.

Environment Information

  • Operating System: Mac OS X 10.12 Sierra
  • GORM Version: 6.1.4
  • Grails Version (if using Grails): 3.2.10
  • JDK Version: 1.8.0_131

Example Application

Will attach a .zip; once started with grails run you can test by opening:
http://localhost:8080/foo/createThings
http://localhost:8080/foo/updateDescription
http://localhost:8080/foo/listThings

Which will output null for the description with GORM 6.1.4, switching back to GORM 6.0.11 in gradle.properties makes it output the description as expected.

@abryzakez
Copy link
Author

test-app.zip

@graemerocher
Copy link
Member

in GORM 6.0.x the default was to use Hibernate’s dirty checking mechanism

what this does is store in-memory an array of the values of you entity (edited)

then when flush() is called it does an array comparison of all of the previous values with all of properties of your instance to check whether the object has changed

So, the more properties you have, the worse performance becomes.

In GORM 6.1.x we implemented our own dirty checking strategy that modifies all setters to track changes to your objects as you change them

this approach is hugely more efficient and more performant since Hibernate does not need to do the array comparison dirty check as we know what fields have changed

unfortunately if you modify the instance with internal methods of the class you are changing the internal field directly and we cannot know about the change

so you have 2 options:

  1. call markDirty every time you change an internal field. This will be better for performance
  2. or as per http://gorm.grails.org/latest/hibernate/manual/index.html#upgradeNotes use
   hibernateDirtyChecking: true

this will re-enable hibernate’s dirty checking impl, however your performance will suffer

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

No branches or pull requests

2 participants