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

Dirty Checking on Map for HStore Type #448

Open
robshep opened this issue Jun 26, 2022 · 9 comments
Open

Dirty Checking on Map for HStore Type #448

robshep opened this issue Jun 26, 2022 · 9 comments

Comments

@robshep
Copy link

robshep commented Jun 26, 2022

Hi,

I have an HStore typed Map<String,String> for hibernate5 module.

if I only update the content of the map, the entity is not dirty tracked and appears clean, and is not persisted on update.

I've tried to add this scenario to the test case for this hibernate v5 module but I can't get the repo to compile as-is (mvn toolchains and other dependencies not found)

The hashCode and equals on my entity works fine - I have tests that demonstrates hashcode changes upon map content changes and equals also breaks when only the map changes.

As a workaround, I have the following Entity property

@Entity
public class MyEntity {
   ...
   private int version;
   // getters and setters omitted.  Note this does not use the @Version annotation.
}

Then (As I'm doing my own audit logging, I know when a change has occurred I can do the following:

if ( mergeUpdated ) {
    myEntity.setVersion(myEntity.getMapProp().hashcode());
}

This statement makes the entity itself dirty and is persisted as expected.

I don't know if this is something that can be adjusted by this library? I've no idea how much of hibernate's instrumentation is in the Map implementation it provides after loading an entity and whether or not that can be forced to check a different way.

Thanks

Rob

@vladmihalcea
Copy link
Owner

You will have to create a replicating test case that demonstrates the issue. Use the existing tests as a reference for the one you'll have to create.

@robshep
Copy link
Author

robshep commented Jun 27, 2022

Thank you.

I did try but the pom references toolchain configuration that I’m not familiar enough with to replicate.

Do you have any pointers for setting up maven to build the codebase?

thanks again

@vladmihalcea
Copy link
Owner

Sure thing. Check out this article.

@robshep
Copy link
Author

robshep commented Jun 27, 2022

Hopefully this fork/branch will work.
It currently has a failing test - expanding the original hstore test.

https://github.com/robshep/hibernate-types/tree/issue_%23448

@robshep
Copy link
Author

robshep commented Jun 27, 2022

We can see the suggestion of forcing dirty on the Book with the following alternative patch:

        doInJPA(new JPATransactionFunction<Void>() {
            @Override
            public Void apply(EntityManager entityManager) {
                Book book = entityManager.unwrap(Session.class)
                        .bySimpleNaturalId(Book.class)
                        .load("978-9730228236");

                book.getProperties().put("price", "$144.95");

                entityManager.persist(book);

                return null;
            }
        });

        doInJPA(new JPATransactionFunction<Void>() {
            @Override
            public Void apply(EntityManager entityManager) {
                Book book = entityManager.unwrap(Session.class)
                        .bySimpleNaturalId(Book.class)
                        .load("978-9730228236");

                assertEquals("$44.95", book.getProperties().get("price")); // Uh-Oh!

                return null;
            }
        });

        doInJPA(new JPATransactionFunction<Void>() {
            @Override
            public Void apply(EntityManager entityManager) {
                Book book = entityManager.unwrap(Session.class)
                        .bySimpleNaturalId(Book.class)
                        .load("978-9730228236");

                Map copyProperties = new HashMap();
                copyProperties.putAll(book.getProperties());
                copyProperties.put("price", "$144.95");
                book.setProperties(copyProperties);

                entityManager.persist(book);

                return null;
            }
        });

        doInJPA(new JPATransactionFunction<Void>() {
            @Override
            public Void apply(EntityManager entityManager) {
                Book book = entityManager.unwrap(Session.class)
                        .bySimpleNaturalId(Book.class)
                        .load("978-9730228236");

                assertEquals("$144.95", book.getProperties().get("price")); // OK

                return null;
            }
        });

@vladmihalcea
Copy link
Owner

If you managed to replicate it, then you can debug it and see why it fails.

Once you identify the problem, send me a Pull Request with the fix proposal to investigate it.

@robshep
Copy link
Author

robshep commented Jun 27, 2022

Sadly I've reached the limit of my knowledge in hibernate internals.
I haven't used it in a long time and it seems the use of interface proxies has disappeared in favour of presumably bytecode instrumenting or something else that doesn't show up in my debugger.
I can't see how the standard entity dirty tracking is effected, and so can't propose where to even start resolving this.

I don't expect you to fix it, but sadly I'm unable to sorry.

Feel free to close this.
My workaround is inelegant but gets me over the line at present.
Thanks for the library in any case.

Rob

@vladmihalcea
Copy link
Owner

No worries. In the meanwhile, you could switch to a jsonb column and persist the Map attribute to it.

@rhuitl
Copy link

rhuitl commented Jun 9, 2023

FYI I saw exactly the same happen for a JSONB column, so this might not be a problem in this project but rather in Hibernate itself.

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

3 participants