Describe the bug
Using Jackson for JSON-Deserializiation Panache entities (w/out explicit setters) is problematic because generated getters & setters are marked "synthetic" by Quarkus and Jackson skips synthetic methods, i.e. it uses just the public fields. But that means that Hibernate's dirty tracking is bypassed and the (changed) entities are not persisted.
Expected behavior
Panache entity is persistable (aka. dirty) after deserialization with Jackson.
Actual behavior
Panache entity is not marked as dirty after deserialization with Jackson.
To Reproduce
Steps to reproduce the behavior:
Solution
As discussed with @FroMage on the chat, the synthetic flag should be removed to make this work as it is not relevant for any (known) use case right now (see getter & setter generation in PanacheEntityEnhancer).
I created a PR without this flag: https://github.com/quarkusio/quarkus/pull/6643 @sbeigel could you give it a try?
I created a PR without this flag: #6643 @sbeigel could you give it a try?
Perfect! It works, thank you!
Hold on, this doesn't actually seem related to the accessors very much. The problem is that we're creating a new entity and it isn't marked as dirty unless we call some accessors.
But the entity is not persisted, so it doesn't make sense that it's tracking state at this point.
I remember we already had a discussion around this @Sanne no? I can't find the previous issues though, but I was pretty sure this resulted in a decision to fix something in ORM, no?
My use-case is: I load an (panache) entity from the db, then I use Jackson's readerForUpdating() to bind a JSON payload directly to this entity. I know this can be problematic from a security point of view, but if you know your payload, it's quite elegant and a big time safer :)
TBH, I think it can be important for other scenarios as well that Jackson uses the accessor methods, not the (public) fields. And it definitely excludes synthetic methods (from the Jackson source):
private boolean _isIncludableMemberMethod(Method m)
{
if (Modifier.isStatic(m.getModifiers())
// Looks like generics can introduce hidden bridge and/or synthetic methods.
// I don't think we want to consider those...
|| m.isSynthetic() || m.isBridge()) {
return false;
}
[...]
I simplified my PR to only change the acessors methods. @sbeigel can you confirm it still works for you?
@sbeigel can you confirm it still works for you?
Yes, it does!
I remember we already had a discussion around this @Sanne no? I can't find the previous issues though, but I was pretty sure this resulted in a decision to fix something in ORM, no?
The other bug relates to default field initializers not being triggered by a Jackson optimisation of not invoking a setter for some fields when it thinks this is unnecessary, when combined with Hibernate ORM's self-dirty-tracking enhancement. AFAIR it was reproduced without having Panache in the picture so I guess it's a different issue.
Well, yeah, it's not related to Panache, but it's the same real underlying bug: that new unpersisted entities are not marked dirty.