HHH-15733 Change convert logic to default to value for Map collections of basic types

This commit is contained in:
Marco Belladelli 2022-11-28 19:06:14 +01:00 committed by Christian Beikov
parent 1bb6fcfe1c
commit f4e95d96c6
4 changed files with 39 additions and 24 deletions

View File

@ -2131,15 +2131,17 @@ private void handleElementCollection(XClass elementType, String hqlOrderBy) {
propertyHolder, propertyHolder,
buildingContext buildingContext
); );
holder.prepare( property );
final Class<? extends CompositeUserType<?>> compositeUserType = final Class<? extends CompositeUserType<?>> compositeUserType =
resolveCompositeUserType( property, elementClass, buildingContext ); resolveCompositeUserType( property, elementClass, buildingContext );
if ( classType == EMBEDDABLE || compositeUserType != null ) { boolean isComposite = classType == EMBEDDABLE || compositeUserType != null;
holder.prepare( property, isComposite );
if ( isComposite ) {
handleCompositeCollectionElement( hqlOrderBy, elementClass, holder, compositeUserType ); handleCompositeCollectionElement( hqlOrderBy, elementClass, holder, compositeUserType );
} }
else { else {
handleCollectionElement( elementType, hqlOrderBy, elementClass, holder ); handleCollectionElement( elementType, hqlOrderBy, elementClass, holder );
} }
} }

View File

@ -78,6 +78,7 @@ public Collection getCollectionBinding() {
private void buildAttributeConversionInfoMaps( private void buildAttributeConversionInfoMaps(
XProperty collectionProperty, XProperty collectionProperty,
boolean isComposite,
Map<String,AttributeConversionInfo> elementAttributeConversionInfoMap, Map<String,AttributeConversionInfo> elementAttributeConversionInfoMap,
Map<String,AttributeConversionInfo> keyAttributeConversionInfoMap) { Map<String,AttributeConversionInfo> keyAttributeConversionInfoMap) {
if ( collectionProperty == null ) { if ( collectionProperty == null ) {
@ -88,7 +89,13 @@ private void buildAttributeConversionInfoMaps(
{ {
final Convert convertAnnotation = collectionProperty.getAnnotation( Convert.class ); final Convert convertAnnotation = collectionProperty.getAnnotation( Convert.class );
if ( convertAnnotation != null ) { if ( convertAnnotation != null ) {
applyLocalConvert( convertAnnotation, collectionProperty, elementAttributeConversionInfoMap, keyAttributeConversionInfoMap ); applyLocalConvert(
convertAnnotation,
collectionProperty,
isComposite,
elementAttributeConversionInfoMap,
keyAttributeConversionInfoMap
);
} }
} }
@ -99,6 +106,7 @@ private void buildAttributeConversionInfoMaps(
applyLocalConvert( applyLocalConvert(
convertAnnotation, convertAnnotation,
collectionProperty, collectionProperty,
isComposite,
elementAttributeConversionInfoMap, elementAttributeConversionInfoMap,
keyAttributeConversionInfoMap keyAttributeConversionInfoMap
); );
@ -110,14 +118,15 @@ private void buildAttributeConversionInfoMaps(
private void applyLocalConvert( private void applyLocalConvert(
Convert convertAnnotation, Convert convertAnnotation,
XProperty collectionProperty, XProperty collectionProperty,
boolean isComposite,
Map<String,AttributeConversionInfo> elementAttributeConversionInfoMap, Map<String,AttributeConversionInfo> elementAttributeConversionInfoMap,
Map<String,AttributeConversionInfo> keyAttributeConversionInfoMap) { Map<String,AttributeConversionInfo> keyAttributeConversionInfoMap) {
// IMPL NOTE : the rules here are quite more lenient than what JPA says. For example, JPA says // IMPL NOTE : the rules here are quite more lenient than what JPA says. For example, JPA says that @Convert
// that @Convert on a Map always needs to specify attributeName of key/value (or prefixed with // on a Map of basic types should default to "value" but it should explicitly specify attributeName of "key"
// key./value. for embedded paths). However, we try to see if conversion of either is disabled // (or prefixed with "key." for embedded paths) to be applied on the key. However, we try to see if conversion
// for whatever reason. For example, if the Map is annotated with @Enumerated the elements cannot // of either is disabled for whatever reason. For example, if the Map is annotated with @Enumerated the
// be converted so any @Convert likely meant the key, so we apply it to the key // elements cannot be converted so any @Convert likely meant the key, so we apply it to the key
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, collectionProperty ); final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, collectionProperty );
if ( collection.isMap() ) { if ( collection.isMap() ) {
@ -132,10 +141,15 @@ private void applyLocalConvert(
if ( isEmpty( info.getAttributeName() ) ) { if ( isEmpty( info.getAttributeName() ) ) {
// the @Convert did not name an attribute... // the @Convert did not name an attribute...
if ( canElementBeConverted && canKeyBeConverted ) { if ( canElementBeConverted && canKeyBeConverted ) {
throw new IllegalStateException( if ( !isComposite ) {
"@Convert placed on Map attribute [" + collection.getRole() // if element is of basic type default to "value"
+ "] must define attributeName of 'key' or 'value'" elementAttributeConversionInfoMap.put( "", info );
); }
else {
throw new IllegalStateException(
"@Convert placed on Map attribute [" + collection.getRole()
+ "] of non-basic types must define attributeName of 'key' or 'value'" );
}
} }
else if ( canKeyBeConverted ) { else if ( canKeyBeConverted ) {
keyAttributeConversionInfoMap.put( "", info ); keyAttributeConversionInfoMap.put( "", info );
@ -325,7 +339,7 @@ public String toString() {
boolean prepared; boolean prepared;
public void prepare(XProperty collectionProperty) { public void prepare(XProperty collectionProperty, boolean isComposite) {
// fugly // fugly
if ( prepared ) { if ( prepared ) {
return; return;
@ -377,7 +391,12 @@ else if ( collectionProperty.isAnnotationPresent( CollectionType.class ) ) {
// Is it valid to reference a collection attribute in a @Convert attached to the owner (entity) by path? // Is it valid to reference a collection attribute in a @Convert attached to the owner (entity) by path?
// if so we should pass in 'clazzToProcess' also // if so we should pass in 'clazzToProcess' also
if ( canKeyBeConverted || canElementBeConverted ) { if ( canKeyBeConverted || canElementBeConverted ) {
buildAttributeConversionInfoMaps( collectionProperty, elementAttributeConversionInfoMap, keyAttributeConversionInfoMap ); buildAttributeConversionInfoMaps(
collectionProperty,
isComposite,
elementAttributeConversionInfoMap,
keyAttributeConversionInfoMap
);
} }
} }

View File

@ -37,6 +37,7 @@
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.BasicType;
import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserCollectionType; import org.hibernate.usertype.UserCollectionType;
@ -278,7 +279,7 @@ private CollectionPropertyHolder buildCollectionPropertyHolder(
// 'holder' is the CollectionPropertyHolder. // 'holder' is the CollectionPropertyHolder.
// 'property' is the collection XProperty // 'property' is the collection XProperty
propertyHolder.startingProperty( property ); propertyHolder.startingProperty( property );
holder.prepare(property); holder.prepare( property, !( collection.getKey().getType() instanceof BasicType ) );
return holder; return holder;
} }

View File

@ -67,16 +67,9 @@ public static class Customer {
private Integer id; private Integer id;
@ElementCollection(fetch = FetchType.EAGER) @ElementCollection(fetch = FetchType.EAGER)
@Convert(converter = MyStringConverter.class) @Convert(converter = MyStringConverter.class) // note omitted attributeName
private final Map<String, String> colors = new HashMap<>(); private final Map<String, String> colors = new HashMap<>();
public Customer() {
}
public Customer(Integer id) {
this.id = id;
}
public Map<String, String> getColors() { public Map<String, String> getColors() {
return colors; return colors;
} }