-
Notifications
You must be signed in to change notification settings - Fork 3.6k
HHH-14198 - Expose CompositeUserTypes through JPA Metamodel #3444
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
HHH-14198 - Expose CompositeUserTypes through JPA Metamodel #3444
Conversation
public void testMetamodel() { | ||
MetamodelImplementor metamodel = sessionFactory().getMetamodel(); | ||
PersistentAttributeDescriptor<? super Transaction, ?> value = metamodel.managedType(Transaction.class).getAttribute("value"); | ||
assertEquals(value.getPersistentAttributeType(), Attribute.PersistentAttributeType.EMBEDDED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems we might need to swap the two parameter orders for assertEquals()
. The issue only manifests itself when testing is broken, however.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After fixing this, I see nothing else blocking this PR. Any concerns @Sanne ?
hi @jwgmeligmeyling , this seems very reasonable to me. I'm wondering, do you think there's substantial drawbacks that made you choose to not enable this by default? Is it because treating composite types and embeddables should ideally be exposed differently? thanks! |
I guess backwards-compatibility is a major point here, but I agree that enabling this by default is a good idea. |
Hi @Sanne , This was indeed from a backwards compatibility point of view. Although I don't think many users will be affected by that change. To be really affected by that you'd have to be dependent on the Furthermore, its probably debatable whether a composite type should be represented as an embeddable. In the Hibernate code, the relationship is actually the other way around: an embeddable is a composite. We have to see to what extent that is an implementation choice or actual architecture. I think Composite types are relatively older than JPA's embeddables, which is probably one of the reasons embeddables were built upon composites rather than the other way around. Considering this feature is not yet available in 6.0 I think the structure still remains a bit unclear. From issues like jakartaee/persistence#105 it is however clear that there remains a wish for being able to map interfaces through any other means than Emeddables, and I think some way of composite types behaving as embeddable is a nice addition (whether we call it an composite or not). So in conclusion:
I don't think it has to, the way the code is currently is set up suggests otherwise, although future implementations may do this differently. |
thanks the additional context. FWIW, I think it would be fine to include this patch either as-is, or without the configuration knob if you're fine with having this "always on". I'm generally trying to reduce the configuration properties: I believe it would help for us take better reponsibility for features if they are "always on", rather than an obscure setup that only a few will know (and test). |
@beikov WDYT? Should we omit the configuration property? |
Since we are changing attribute types from JPA BasicType to EmbeddableType I'd say it's worth allowing the old behavior for 5.x but for 6.x we could drop it IMO. |
Hmm it seems that if it becomes the default behaviour, then we do not yet agree whether it should be configurable. @Sanne I am comfortable with removing the property personally. Should I make that change? |
yes I'd keep it simple, and assuming that we won't backport this further back than 5.4 it's ok to make minor adjustments in behaviour - when we don't expect to break existing applications. (only 5.3 has very strict backwards compatibility requirements). |
e2c20d1
to
a29e80d
Compare
I have rebased the PR. I've also tested it against my personal integration test suite and the one from Blaze-Persistence to catch any potential regressions. One issue that I've found has since been patched by fixing the if-condition. The setting is now always-on and not configurable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
8aff52a
to
a29e80d
Compare
public void testMetamodel() { | ||
MetamodelImplementor metamodel = sessionFactory().getMetamodel(); | ||
PersistentAttributeDescriptor<? super Transaction, ?> value = metamodel.managedType(Transaction.class).getAttribute("value"); | ||
assertEquals(value.getPersistentAttributeType(), Attribute.PersistentAttributeType.EMBEDDED); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After fixing this, I see nothing else blocking this PR. Any concerns @Sanne ?
Composite User Types work like regular Composite Types (like Embeddable) in HQL. However, because they cannot be represented in the JPA metamodel, libraries like [GraphQL for JPA](https://github.com/jcrygier/graphql-jpa) or [Blaze-Persistence](https://persistence.blazebit.com/) cannot fully utilize them. In order to make the composite property names available to these libraries, it would be nice to optionally expose these attributes as embedded attributes. This pull request aims to make that change and makes it configurable through a custom setting. Composite User Types are a common solution for mapping composite interfaces. A common example is for example `Money` from the Java Money API (JSR-354), for which composite user types are implemented in [Jadira](http://jadira.sourceforge.net/usertype-userguide.html). I know Composite User Types are currently not consiered in Hibernate 6.x. See also [this](https://hibernate.zulipchat.com/#narrow/stream/132094-hibernate-orm-dev/topic/CompositeUserType) Zulip thread. I am not sure if Hibernate 6.x will even have multi column types, which I presume would be a requirement to even introduce Composite User types back at some point. Usually Embeddables are a much easier, suitable mechanism for composite user types. But Embeddables are not always a viable alternative, because Embeddables require the type to be subclassed (as an interface cannot be mapped, and the type may not solely comprise fields that can be mapped to a simple basic type). To deal with this exact problem, `MonetaryAmounts` are still mapped as composite user type. There also have been suggestions to the JPA Spec to consider `AttributeConverters` for Embeddables for pracitcally the same purpose (which I think is going to be a mess of an implementation). See: jakartaee/persistence#105 Anyways, regardless of whether this gets integrated in 5.x, I don't expect it to be integrated in 6.x unless we also reintroduce Composite User Types. I am willing to contribute Composite User Types for 6.x if people see benefit in it and think it can be done in the first place.
a29e80d
to
6f91c1c
Compare
@beikov Fixed! |
nice! thanks. Will you need it in 5.4.x ? |
This would for sure be an interesting feature for 5.4 as well, but I'm not sure if we should backport features. |
it's going to take a while before we release 5.5.x versions, so while I agree we shouldn't backport significant features - we can be flexible about minor things. Up to you.. |
If the 5.5.0-Beta1 release is not planned to be released any time soon, I'd be happy to see this in 5.4. |
I’d love to see it in 5.4 as well 😃 |
Isn't this change effectively breaking the contract of
None of this is true for custom Hibernate user types, is it? I am asking because this change causes a lot of ripple effects in downstream code that uses the |
You can set the |
I am not convinced that arbitrarily reimagining a specified API and providing knobs one now explicitly has to set to actually make it work as specified is a proper way of implementing such an API. The change provided here literally breaks that specified API, i.e. all code that's been written against the originally specified semantics. There's not that much of that code, I admit, but we're getting littered with problems stemming from that change because some prominent JPA implementation decided something is "very similar". The spec doesn't say "… or similar". I can only recommend to at least turn this into an opt-in feature, so that folks who enable it know that they're switching to non-standard API behavior, and thus have to live with the downstream consequences. |
We are not "reimagining" an API and what we return here didn't "change" from a JPA supported point of view. We returned "unsupported" types from the JPA metamodel API already before when you use certain Hibernate specific features like e.g. Hibernate Envers. And even before this change, you were able to set the
The spec also doesn't say anything about custom user types, so should we remove support for that? ;) I'm obviously kidding, but you see where this "spec doesn't say" leads to. If you want Hibernate to fully behave like the spec says, you have to enable the spec compliance setting, but believe me, that's probably not something you usually want as that has other consequences as well, which is why the
If you enable JPA compliance, this is an opt-in feature. Not sure what to tell you, 1.5 years after this was merged. It wasn't an issue for anyone so far, so maybe you just had wrong expectations? Don't get me wrong, but as far as see this, you will simply have to change your code or configuration, just like other people have to adapt to changes that happen in the Spring ecosystem. |
It's pointless to discuss this way. The spec says: "returns elements that match X, Y, Z". It's allow-listing what is to be returned. It is of course not listing all elements that must not be returned as that set is unbounded. What's next? Returning entities from
I am not controlling the update cycles of the projects of our users. Some might just start to use the feature that triggers this misbehavior now.
So, you're saying that I need to explicitly enable spec compliance with a spec implementation? Interesting. Why didn't Hibernate expose this custom behavior via a custom method on its implementation class? Folks interested could then cast to that and invoke the method. But thanks for considering the feedback. |
Let's stay serious here, a
First and foremost, all implementations that implement the JPA spec usually have features that are "non-standard" which need to somehow be mapped to the JPA model or at least brought into harmony with it. I guess you wouldn't like it if we simply don't show you managed types just because they use a Hibernate specific feature, right? I hope you understand that there are multiple interests that we try to fulfill.
JPA providers are free to require some sort of configuration for the TCK and Hibernate has had a JPA spec compliance setting for a very long time.
We can of course clutter our APIs but we generally don't do that when we think that we are doing the right thing. Like I tried to explain, Hibernate returned "unsupported" types for a long time and it had a setting to disable that for a long time as well. So I don't think Hibernate is at fault here just because you didn't encounter a model yet that exposed this feature of Hibernate. |
Why are we still having this non-discussion? |
@odrotbohm Remind me which specification Spring Data implements? I'm having a senior moment and can't quite seem to recall... |
Composite User Types work like regular Composite Types (like Embeddable) in HQL. However, because they cannot be represented in the JPA metamodel, libraries like GraphQL for JPA or Blaze-Persistence cannot fully utilize them. In order to make the composite property names available to these libraries, it would be nice to optionally expose these attributes as embedded attributes. This pull request aims to make that change and makes it configurable through a custom setting.
Composite User Types are a common solution for mapping composite interfaces. A common example is for example
Money
from the Java Money API (JSR-354), for which composite user types are implemented in Jadira.I know Composite User Types are currently not consiered in Hibernate 6.x. See also this Zulip thread. I am not sure if Hibernate 6.x will even have multi column types, which I presume would be a requirement to even introduce Composite User types back at some point. Usually Embeddables are a much easier, suitable mechanism for composite user types. But Embeddables are not always a viable alternative, because Embeddables require the type to be subclassed (as an interface cannot be mapped, and the type may not solely comprise fields that can be mapped to a simple basic type). To deal with this exact problem,
MonetaryAmounts
are still mapped as composite user type. There also have been suggestions to the JPA Spec to considerAttributeConverters
for Embeddables for pracitcally the same purpose (which I think is going to be a mess of an implementation). See: jakartaee/persistence#105Anyways, regardless of whether this gets integrated in 5.x, I don't expect it to be integrated in 6.x unless we also reintroduce Composite User Types. I am willing to contribute Composite User Types for 6.x if people see benefit in it and think it can be done in the first place.
https://hibernate.atlassian.net/browse/HHH-14198