-
Notifications
You must be signed in to change notification settings - Fork 63
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
Allow AttributeConverter to be used for composite types by using @Embeddable #105
Comments
@glassfishrobot Commented |
@glassfishrobot Commented What could be done is to take the existing AttributeConverter and use the second generic type (the column type) as Object[], so then the values of the column(s) can be obtained. An implementation would likely need to know the individual column types (since not obtainable from Object[]), hence add a new method Class[] getDatabaseColumnTypes(); (or provide a new interface MultiColumnAttributeConverter (with this method above) that AttributeConverters implement should they want to persist to multiple columns). |
@glassfishrobot Commented In my example, EmbeddableMoney is used to (de)structure the Money type which is final and coming from an external library. Another advantage of using an @embeddable instead of Object[] is that you can annotate the properties with @column and you can use those properties names in your query. |
@glassfishrobot Commented
Example:
|
@glassfishrobot Commented |
@Erik1 Commented |
@kalgon Commented
|
|
Another example of persistence which can use intermediate/surrogate objects is java standard serialization with |
With java 14 records, this feature could prove even more useful. |
I agree. Embeddables are not always a viable option if you want to program against an interface from an external module (i.e. Java Money). A solution that is commonly used for this is composite user types (or multi column types). These however are going to be removed in Hibernate, so that option will disappear. I agree that being able to use attribute converters on embedded attributes would be a neat solution to this problem. A problem that has to be tackled here, is that these types probably would have to be represented as Embeddable in the JPA metamodel. Otherwise the embedded properties will not be usable. This will lead to new issues, because what is then the desired behaviour in JPQL? Should it behave like its embeddable type? Or its converted type? Or both? And I don't think it will be possible to add support for |
I don't think conversion would make much sense. JPA has no idea what the model structure of the converted type is, so you'd have no way of accessing individual properties in a JPQL query. IMO, there should be a way in JPA to define the structure of types programmatically or through XML mapping and a mechanism that allows to control the construction of objects of that type. |
Could you elaborate a bit on that point? The goal here is to create an @embeddable class which somehow mirrors the structure of your record-like class. That embeddable class can then be annotated and its properties discovered by JPA and used in JPQL. The AttributeConverter is used to convert from one to another. |
If you use an attribute converter, the model type of the attribute will be e.g. I just don't think it is a good idea to do something like this. Wouldn't it be better for you as well to be able to model |
I think that the Internally, the JPA provider would use the Therefore, internally, the JPA would use the embeddable type but it would not expose it, this means that the type of the attribute returned by the metamodel The advantage of this solution is that you create an indirection between your type ( |
Even if its represented as a basic type in the metamodel and the query features are limited, still |
So you are saying that it should be impossible to access the individual properties of the composite type? That would really suck. How would you do aggregations on something like that?
As I wrote, the construction should still be controllable. Deconstruction could be considered as well, but why not rely on accessors? I didn't think it through, but maybe it could look something like this
|
Of course you could do aggregation because you can use the properties defined in the TBH, I don't see why it should be that complicated to do. All that matters for the JPA provider is that it should treat that property/attribute like it was of the type of the embeddable object ( The converter would only be used to decorate the property/attribute accessor/getter (record->embeddable) and mutator/setter (embeddable->record) of its owning entity.
Currently, having an Conceptually, this solution is really simple from a user point-of-view but I admit it could turn complicated at the JPA provider level (the multipass metadata/mapping-model process of hibernate for example can be hard to understand). This solution also gives great flexibility because it alleviates the constraints on your record type:
I have always had mixed feelings about Could you tell me in your example how you would do it if your @Embeddable
public class EmbeddableMoney {
@Basic
private Number value; // the value property can be used in jpql
@Enumerated(STRING)
private Currency unit; // the unit property can be used in jpql
// getter + setters
}
@Converter
public class MoneyAttributeConverter implements AttributeConverter<Money, EmbeddableMoney> {
@Override
public EmbeddableMoney convertToDatabaseColumn(Money attribute) {
// add null checks
EmbeddableMoney columns = new EmbeddableMoney();
columns.setValue(attribute.getValue());
columns.setUnit(attribute.getUnit());
return columns;
}
@Override
public Money convertToEntityAttribute(EmbeddableMoney dbData) {
// add null checks
return Money.of(dbData.getValue(), dbData.getUnit());
}
// String[] getAttributeNames() is not needed, it is inferred from the embeddable properties names
// Type[] getAttributeTypes() is not needed, it is inferred from the embeddable properties types
// boolean isMutable() is it really needed, why doesn't AttributeConverter have such a method for single-valued types?
// Money copy(Money m) same as above
} Your way seems more programmatic (and I don't mind/care if something like that is used at the provider level) but from a user perspective, I think it's easier to write classes whose properties names and types will automatically be discovered and a tiny bit of logic for the mapping than reimplement hibernate's usertypes. |
For the |
I think your view is a bit simplistic. In order to do proper dirty checking you need the information about mutability etc. so something like the Hibernate UserTypes really is necessary to model this. You are saying that the embeddable type is "just a surrogate" and that in the metamodel it should be a basic type returning Apart from that, you usually don't have to implement these contracts yourself as that will usually be done by an integration for whatever library type you want to use. If you are in control of the class, why not just use an embeddable then? From a user perspective, my suggested approach would be even easier to handle, because you don't need the |
You're right, a TBH, I didn't think a lot about the Criteria API which I find way too cumbersome to use and is not very safe about class structure/properties anyway. But, nevertheless, that's a good point. Care to explain how that would work better with a The fundamental problem I have with the Anyway, I'm very happy to see some debate around this feature I've been waiting for quite a long time. |
This is not just about the fact that something is mutable, but it also needs to be copied somehow. This could be done through using construct again, but what if the components are mutable as well e.g.
When modelling the composite type as embeddable, you not only have the actual type
You are mixing things up here. If you have control of the type i.e. you can change it, why would you even need such a mechanism? Just write your embeddable type and use that. Such a This is way more user friendlier if the user does not have to write this stuff himself. The library maintainers can provide that so you don't have to care about the details and be able to just use the type as if it were an embeddable. |
You may be right but this kind of API seems to be really at odd with the rest of JPA where most of the mapping is done in a declarative way rather than imperative. It looks more like low-level code for the provider (nowhere in JPA do you need to implement an interface to return properties names or types, that kind of information is generally retrieved via reflection/annotations/xml). So I am sure there should be a better way to do it which conforms more to the JPA principles. I have no problem seeing that kind of code in Hibernate but not in JPA... maybe other JPA providers have found alternative solutions to that problem?
I was talking about putting those annotations on the |
EclipseLink has transformation mappings: https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_transformation.htm
You need 3 additional classes for the respective transformers and also can't really handle deep copying when the type is non-serializable but mutable. It's apparently also not possible to access components: https://stackoverflow.com/questions/16269002/how-to-query-properties-of-an-entity-attribute-mapped-using-eclipselink-readtr So I'd say an approach similar to Hibernate is pretty compact and declarative for developers.
I understand, but I don't think you need such a surrogate. |
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.
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.
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.
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.
Another aspect to consider is whether converters are tied to the embeddable or to the embedded. In other words, can I have multiple
|
Right now, AttributeConverters can only be used for single-value types. They could be extended to use an @embeddable type as a surrogate column type for composite types.
Imagine the immutable type Money(final BigDecimal amount, final Currency currency) and the following @embeddable type:
What do you think?
The text was updated successfully, but these errors were encountered: