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

Allow AttributeConverter to be used for composite types by using @Embeddable #105

Open
lukasj opened this issue Mar 26, 2015 · 25 comments
Open

Comments

@lukasj
Copy link
Contributor

lukasj commented Mar 26, 2015

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:

@Embeddable
public class EmbeddableMoney {

	private BigDecimal amount;
	private Currency currency;

	// getters & setters
 }
@Converter(autoApply = true)
public class MoneyConverter implements AttributeConverter<Money, EmbeddableMoney> {

	@Override
	public EmbeddableMoney convertToDatabaseColumn(Money attribute) {
		if (attribute == null) {
			return null;
		}
		EmbeddableMoney columns = new EmbeddableMoney();
		columns.setAmount(attribute.getAmount());
		columns.setCurrency(attribute.getCurrency());
		return columns;
	}

	@Override
	public Money convertToEntityAttribute(EmbeddableMoney dbData) {
		if (dbData == null) { // check if empty? 			return null;
		}
		return new Money(dbData.getAmount(), dbData.getCurrency());
	}
}
@Converter(autoApply = true)
public class CurrencyConverter implements AttributeConverter<Currency, String> {
	...
}

What do you think?

@lukasj
Copy link
Contributor Author

lukasj commented Mar 26, 2015

@glassfishrobot Commented
Reported by kalgon

@lukasj
Copy link
Contributor Author

lukasj commented Apr 18, 2015

@glassfishrobot Commented
neilstockton said:
Whilst this may cater for some types, it would do nothing for classes that the developer has no access to. If they wanted to map some type to 2 (or more) columns they couldn't.

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).

@lukasj
Copy link
Contributor Author

lukasj commented Apr 20, 2015

@glassfishrobot Commented
kalgon said:
I don't understand how this could not be used for types the developer has no access to (as long as those types have a way to extract the necessary information to be persisted and there's a way to reconstruct objects based on that information, which btw is also needed when using Object[]).

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.

@lukasj
Copy link
Contributor Author

lukasj commented Oct 24, 2016

@glassfishrobot Commented
kalgon said:
Two more arguments to push this request:

  1. No API change is needed, just a new interpretation of the second parameter of AttributeConverter<X, Y>
  2. This is the way JAXB works too with its XmlAdapter

Example:

public class MoneyXmlAdapter extends XmlAdapter<MoneyXmlAdapter.Wrapper, Money> {

	public static class Wrapper {
		BigDecimal amount;
		Currency currency;
	}

	public Wrapper marshal(Money money) {
		if (money == null) {
			return null;
		}
		Wrapper wrapper = new Wrapper();
		wrapper.amount = money.getAmount();
		wrapper.currency = money.getCurrency();
		return wrapper;
	}

	public Money unmarshal(Wrapper wrapper) {
		if (wrapper == null || wrapper.amount == null || wrapper.currency == null) {
			return null;
		}
		return new Money(wrapper.amount, wrapper.currency);
	}
}

@lukasj
Copy link
Contributor Author

lukasj commented May 5, 2017

@glassfishrobot Commented
This issue was imported from java.net JIRA JPA_SPEC-105

@lukasj
Copy link
Contributor Author

lukasj commented Jul 22, 2018

@Erik1 Commented
Would it not be better to integrate support for javax.money.MonetaryAmount?

@lukasj
Copy link
Contributor Author

lukasj commented Jul 24, 2018

@kalgon Commented
Money was just an example, you could do the same with other composite value objects like

  • LocalDateRange(LocalDate start, LocalDate end)
  • Quantity(Unit unit, double value)
  • Color(int red, int green, int blue)
  • Address(String street, String zipCode, String cityName, int countryCode)
  • Fraction(BigInteger numerator, BigInteger denominator)
  • Complex(double real, double imaginary)
  • ...

@lukasj
Copy link
Contributor Author

lukasj commented Aug 31, 2018

@kalgon
Copy link

kalgon commented Nov 28, 2019

Another example of persistence which can use intermediate/surrogate objects is java standard serialization with readResolve() and writeReplace().

@kalgon
Copy link

kalgon commented Apr 22, 2020

With java 14 records, this feature could prove even more useful.

@jwgmeligmeyling
Copy link

jwgmeligmeyling commented Jun 27, 2020

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 javax.money.MonetaryAmount directly, because that would involve the introduction of multi-column bindings in JPA.

@beikov
Copy link

beikov commented Jun 28, 2020

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.

@kalgon
Copy link

kalgon commented Jun 29, 2020

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.

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.

@beikov
Copy link

beikov commented Jun 29, 2020

If you use an attribute converter, the model type of the attribute will be e.g. Money instead of the embeddable type MoneyEmbeddable. But what is the structure of the type Money? How would you model this in the JPA metamodel? After all, the structure of the type is essential for accessing the components. How would you write e.g. SELECT o.price.amount FROM Order o when the embeddable attribute price has a converter? Or should the JPA model type still be MoneyEmbeddable?

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 Money directly instead?

@kalgon
Copy link

kalgon commented Jun 29, 2020

I think that the EmbeddableMoney should be an internal construct as it is not directly used in the user's entity model. You wouldn't find any attribute/property of this type (only the Money type would be used in your entities)... and you would only see it being used in the converter.

Internally, the JPA provider would use the EmbeddableMoney structure and use the converter when it needs to convert it to the right type before it is set in its owning entity's property or after it reads it from the entity.

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 EntityType would be a BasicType (and not an EmbeddableType). It is already the case when you have a converted single-valued type (like VATIdentificationNumber -> String): its type is still a BasicType even if it's converted.

The advantage of this solution is that you create an indirection between your type (Money) and the structure needed by the JPA provider (EmbeddableMoney) and you have control over that indirection as you can implement it yourself in the AttributeConverter. What if you need some logic or need to call some other service to be able to deconstruct/reconstruct your value? Where would you put that logic if it's declaratively mapped with annotations/xml?

@jwgmeligmeyling
Copy link

Even if its represented as a basic type in the metamodel and the query features are limited, still AttributeConverter was never designed to work with multiple columns, because none of the basic types are. So JPA has no mechanism to declare the columns for the properties. Even if you want to allow @AttributeOverride on these attributes for that I think it will become really messy. Because @AttributeOverride is intended for Embedded properties rather than basic properties.

@beikov
Copy link

beikov commented Jun 30, 2020

I think that the EmbeddableMoney should be an internal construct as it is not directly used in the user's entity model. You wouldn't find any attribute/property of this type (only the Money type would be used in your entities)... and you would only see it being used in the converter.
Internally, the JPA provider would use the EmbeddableMoney structure and use the converter when it needs to convert it to the right type before it is set in its owning entity's property or after it reads it from the entity.
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 EntityType would be a BasicType (and not an EmbeddableType). It is already the case when you have a converted single-valued type (like VATIdentificationNumber -> String): its type is still a BasicType even if it's converted.

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?

The advantage of this solution is that you create an indirection between your type (Money) and the structure needed by the JPA provider (EmbeddableMoney) and you have control over that indirection as you can implement it yourself in the AttributeConverter. What if you need some logic or need to call some other service to be able to deconstruct/reconstruct your value? Where would you put that logic if it's declaratively mapped with annotations/xml?

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

@Entity
public class Order {
  ...
  @AttributeOverrides({
    @AttributeOverride(name = "value", column = @Column(name = "order_amount_value")),
    @AttributeOverride(name = "unit", column = @Column(name = "order_amount_unit"))
  })
  Money amount;
}

@Composite // New annotation for registering the type
public class MoneyType implements CompositeType<Money> {

  @Override
  pubilc String[] getAttributeNames() { // The attributes of the composite type which is then represented as embeddable
    return {"value", "unit"};
  }

  @Override
  pubilc Type[] getAttributeTypes() { // The java types of the attributes from which the default SQL types are inferred
    return new Type[]{ Number.class, String.class };
  }

  @Override
  pubilc boolean isMutable() {
    return false;
  }

  @Override
  pubilc Money copy(Money m) { // When it's mutable, copies must be created for dirty checking
    return m;
  }

  @Override
  public Money construct(Object[] values) {
    if (values[0] == null || values[1] == null) {
      return null;
    }
    return Money.of((Number) values[0], Monetary.getCurrency((String) values[1]));
  }

  @Override
  public Object[] deconstruct(Money m) {
    if (m == null) {
      return null;
    }
    return new Object[]{ m.getValue(), m.getUnit() };
  }
}

@kalgon
Copy link

kalgon commented Jun 30, 2020

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?

Of course you could do aggregation because you can use the properties defined in the Embeddable surrogate in your queries (which could be exactly the same as the record-ish type... or not, it's up to you to decide).

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 (EmbeddableMoney which would be inferred from the converter type signature). That embeddable has an inner structure that is known to JPA (because it would also work if your property was of that embeddable type instead of the record-ish type Money) and it knows how to use it in queries.

The converter would only be used to decorate the property/attribute accessor/getter (record->embeddable) and mutator/setter (embeddable->record) of its owning entity.

AttributeConverter and AttributesOverride(s) were never intended to be used for or for other things than Embeddables, but that's what this issue is all about: extend the way they can be used. The big advantage is that you don't need to come up with separate concepts for single-valued types and multi-valued ones. By using a surrogate embeddable object, you can also annotate its properties with all the existing annotations, so you don't have to reinvent the wheel.

Currently, having an AttributeConverter whose second parameter is not a basic single-column type won't work, so I propose to make it work when that parameter is an embeddable object.

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:

  • it does not need a default constructor
  • fields can be final
  • the table/embeddable structure can diverge from the record structure (if you have a record with multiple levels and you want to flatten it in the DB, you can do the mapping in the converter... of course, this will impact the way you will use it in queries)
  • the surrogate can declare property of the good type instead of playing with Object[] and casting

I have always had mixed feelings about Embeddables because I didn't want those to leak in my domain model with all the constraints they carry but I would like them a bit more if they could serve as an intermediate structure for the persistence of my record-ish objects :-)

Could you tell me in your example how you would do it if your Money type contained an Enum, where would you put the @Enumerated to choose between ORDINAL and STRING? Will you extend the AttributeOverride for all possible annotations? It would be easier to have an Embeddable surrogate and annotate its property.

@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.

@kalgon
Copy link

kalgon commented Jun 30, 2020

For the Enum, I guess you could declare its type to be Integer or String and convert it in your CompositeType.

@beikov
Copy link

beikov commented Jun 30, 2020

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 Money instead of MoneyEmbeddable, but how would the JPA Criteria API work with that? IMO this is a lot more confusing than providing a proper user type contract.

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 EmbeddableMoney type or the converter. You just map the attributes defined for the type to columns and be done.

@kalgon
Copy link

kalgon commented Jun 30, 2020

You're right, a @Mutable annotation or a new boolean mutable() attribute could be added to @Converter.

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 CompositeType?

The fundamental problem I have with the CompositeType is that you don't benefit from what the java class structure gives you for free (properties names and types which I prefer over String[]/Type[]) nor from JPA itself which could already do a lot for you if you can annotate your properties with @Enumerated, @Temporal, @Convert, @Column.
In CompositeType, you need to do everything by yourself and that does not sound too much user-friendly to me.

Anyway, I'm very happy to see some debate around this feature I've been waiting for quite a long time.
At this point, any solution would be a nice step forward ;-)

@beikov
Copy link

beikov commented Jun 30, 2020

You're right, a @mutable annotation or a new boolean mutable() attribute could be added to @converter.

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. Date? You wouldn't want to do deep copies all the time, that's why a separate copy contract is necessary.

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 CompositeType?

When modelling the composite type as embeddable, you not only have the actual type Money as java type in the EmbeddableType, but the attributes are accessible as well. The structure needs to be reflected in the metamodel to be usable in JPA Criteria.

The fundamental problem I have with the CompositeType is that you don't benefit from what the java class structure gives you for free (properties names and types which I prefer over String[]/Type[]) nor from JPA itself which could already do a lot for you if you can annotate your properties with @Enumerated, @TeMPOraL, @convert, @column.
In CompositeType, you need to do everything by yourself and that does not sound too much user-friendly to me.

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 CompositeType contract would only help if you have no control over the type i.e. it is a library type or interface like e.g. Money from the java-money specification.

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.

@kalgon
Copy link

kalgon commented Jun 30, 2020

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?

If you have control of the type i.e. you can change it, why would you even need such a mechanism?

I was talking about putting those annotations on the @Embeddable surrogate, not the type it helps destructuring which I have no access to.

@beikov
Copy link

beikov commented Jul 1, 2020

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?

CompositeType is no API, but an SPI for libraries to plug-in their custom types as embeddables. You as a user will still work in a declarative way, like I proposed:

@Entity
public class Order {
  ...
  @AttributeOverrides({
    @AttributeOverride(name = "value", column = @Column(name = "order_amount_value")),
    @AttributeOverride(name = "unit", column = @Column(name = "order_amount_unit"))
  })
  Money amount;
}

EclipseLink has transformation mappings: https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_transformation.htm

@Entity
public class Order {
  ...
  @Transformation(fetch=FecthType.LAZY, optional="true")
  @ReadTransformer(class=org.javamoney.jpa.MoneyReadTransformer.class)
  @WriteTranformers({
   @WriteTranformer(column=@Column(name="order_amount_value"), 
      transformerClass=org.javamoney.jpa.MoneyValueWriteTransformer.class),
   @WriteTranformer(column=@Column(name="order_amount_unit"), 
      transformerClass=org.javamoney.jpa.MoneyUnitWriteTransformer.class)
  })
  @Mutable
  Money amount;
}

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 was talking about putting those annotations on the @embeddable surrogate, not the type it helps destructuring which I have no access to.

I understand, but I don't think you need such a surrogate.

jwgmeligmeyling added a commit to jwgmeligmeyling/hibernate-orm that referenced this issue Sep 3, 2020
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.
beikov pushed a commit to hibernate/hibernate-orm that referenced this issue Sep 4, 2020
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.
Sanne pushed a commit to Sanne/hibernate-orm that referenced this issue Sep 28, 2020
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.
Sanne pushed a commit to hibernate/hibernate-orm that referenced this issue Sep 29, 2020
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.
@sebersole
Copy link
Contributor

Another aspect to consider is whether converters are tied to the embeddable or to the embedded. In other words, can I have multiple Money attributes with different converters?

  @Embedded
  @Convert( converter = FirstMoneyConverter.class )
  Money unitCost;

  @Embedded
  @Convert( converter = SecondMoneyConverter.class )
  Money retailPrice;

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

5 participants