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

Custom user types considered entities on Hibernate >= 5.4.22 #2421

Closed
darioseidl opened this issue Jan 20, 2022 · 6 comments
Closed

Custom user types considered entities on Hibernate >= 5.4.22 #2421

darioseidl opened this issue Jan 20, 2022 · 6 comments
Assignees

Comments

@darioseidl
Copy link

After the serialization issue was fixed (thank you!), I tried again to upgrade Spring Boot.

This time, we're having problems with the Deserialization on PATCH request of Java Money.

There are two problems:

  1. The first is apparently caused by Backend read-only properties overridden for PATCH requests [DATAREST-1383] spring-data-rest#1743, and very similar to the following reports:
    @Transient annotation won't deserialize fields on MongoRepository PATCH requests [DATAREST-1524] spring-data-rest#1881
    DATAREST-1383 causes top level properties with @JsonSetter to be missed. [DATAREST-1573] spring-data-rest#1911
    Custom Serializers cannot apply patch method [DATAREST-1566] spring-data-rest#1928

A check was introduced, that if a JsonNode doesn't map to a writable property, the JsonNode is removed and ignored.
That's a problem whenever the JSON doesn't map directly to a property in the entity, but is converted or parsed in any other way: transient fields with setters, @JsonSetter, or custom serializers.

On the original issue there was also the comment

We unfortunately need to revisit the fix for this here in the course of fixing DATAREST-1524. I looks like inspecting the persistence configuration regarding updatability is the wrong place to start with as that can be at odds with what's expected to happen at the JSON binding level. For the latter, there actually is e.g. @JsonProperty(access = Access.READ_ONLY). I.e. if you want to prevent the binding to it you actually would need to annotate it that way

but I don't think the fix has been been revisited.

  1. The second problem is caused by the same change in Hibernate that also caused the serialization issue earlier. A CompositeUserType like the Jadira PersistentMoneyAmountAndCurrency is now registered in the Hibernate metamodel as an embeddable and the DomainObjectReader check with isEntity whether a type is managed by Hibernate (IMO, isEntity is a bit of a misnomer here, it not only checks for entities, but for any managed type: entities, mapped superclasses and embeddables). So, now a Money is treated as an entity with nested properties, instead of passing it as a value object to the MonetaryAmountDeserializer. Maybe isEntity could return false CompositeUserTypes? (I'm not sure how to do that without some Hibernate-specific code and it probably needs to have no knowledge of Hibernate here.)

I have setup a demo to reproduce the issues (with some hacky workarounds).

@ghost ghost assigned odrotbohm Jan 24, 2022
@schauder
Copy link
Contributor

@odrotbohm Could you take a look at this?

@odrotbohm
Copy link
Member

Is there a chance we get a slightly simpler (read: Maven / Java sample project)? Using a very niche infrastructure arrangement makes working (as in execute, debug, tweak) with the project much harder than it'd need to be.

I'd like to avoid to put anything JPA / Hibernate specific into the metamodel inspection on SD RESTs side. If at all, this needs to go somewhere in the MappingContext implementation for JPA. I've asked back with the Hibernate team whether they're aware that they effectively have broken JPA API and could paddle back on this.

I'll continue by investigating how much insight we can get into all of this by inspecting the ManagedType implementations we get returned from Hibernate's implementation. Maybe we can really identify the ones backed by a custom user type that way.

@odrotbohm
Copy link
Member

Looks like we can't expect too much help from the Hibernate team here. After internal discussion, we should be able to tighten the guards to only merge entities (in the Spring Data sense) that expose an identifier.

That discussion also again brought to light that the notion of “entity” in the context of Spring Data mapping is more like “mapped type” (as in: type, we need to inspect and unfold to persist) and we're going to investigate to adapt the internal APIs to better reflect that.

Never mind the demo project, actually. I got it running in IDEA.

@odrotbohm
Copy link
Member

Looks like we need to move this to Spring Data JPA. The least invasive way of fixing this is in mitigating Hibernates change right in front of the JPA Metamodel API to re-establish the semantics of the infamous ….isEntity(…) effectively meaning “is a mapped type”. I have a local fix here, that adds an extra check for the JPA @Embeddable annotation for types considered embeddables by the JPA Metamodel implementation. That should allow us to separate the JPA-native embeddables from the types, which Hibernate additionally considers such.

@odrotbohm odrotbohm transferred this issue from spring-projects/spring-data-rest Jan 26, 2022
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 26, 2022
@odrotbohm odrotbohm removed the status: waiting-for-triage An issue we've not yet triaged label Jan 26, 2022
@odrotbohm odrotbohm added this to the 2.5.9 (2021.0.9) milestone Jan 26, 2022
odrotbohm added a commit that referenced this issue Jan 26, 2022
…rsions > 5.4.21.

Hibernate 5.4.22 started including types managed by custom user types in the embeddables exposed via JPA's Metamodel API. This causes those to be considered types to explicitly map (read: unfold) in Spring Data REST. We now exposed a cleaned up view on this via a tweak in JpaPersistentPropertyImpl (ultimately in JpaMetamodel) looking for @embeddable annotation on types allegedly considered embeddable by Hibernate.

Fixes #2421.
odrotbohm added a commit that referenced this issue Jan 26, 2022
…rsions > 5.4.21.

Hibernate 5.4.22 started including types managed by custom user types in the embeddables exposed via JPA's Metamodel API. This causes those to be considered types to explicitly map (read: unfold) in Spring Data REST. We now exposed a cleaned up view on this via a tweak in JpaPersistentPropertyImpl (ultimately in JpaMetamodel) looking for @embeddable annotation on types allegedly considered embeddable by Hibernate.

Fixes #2421.
odrotbohm added a commit that referenced this issue Jan 26, 2022
…rsions > 5.4.21.

Hibernate 5.4.22 started including types managed by custom user types in the embeddables exposed via JPA's Metamodel API. This causes those to be considered types to explicitly map (read: unfold) in Spring Data REST. We now exposed a cleaned up view on this via a tweak in JpaPersistentPropertyImpl (ultimately in JpaMetamodel) looking for @embeddable annotation on types allegedly considered embeddable by Hibernate.

Fixes #2421.
@odrotbohm odrotbohm changed the title Deserialization of Java Money with zalando/jackson-datatype-money fails after upgrade to Spring Boot 2.3.5 Custom user types considered entities on Hibernate >= 5.4.22 Jan 26, 2022
@odrotbohm
Copy link
Member

Fixes in place in all branches (2.5.x, 2.6.x, main, 3.0.x). The provided sample project builds fine against the 2.5.x one, but feel free to give the snapshots a try.

@darioseidl
Copy link
Author

Thank you for the quick fix! I tried with spring-data-jpa.2.5.9-SNAPSHOT and it's working.

I assume the fix for the custom user type being seen as embeddable fixed the whole thing. The other issue, of JsonNodes not mapping to writeable properties might still exist, but doesn't affect us with the Money deserialization now (because the money object is mapped to a writeable property, only the "amount" wasn't).

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

4 participants