diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorBypassedImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorBypassedImpl.java index 976baa93ee..7c2c015a77 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorBypassedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorBypassedImpl.java @@ -24,6 +24,11 @@ public class AutoApplicableConverterDescriptorBypassedImpl implements AutoApplic private AutoApplicableConverterDescriptorBypassedImpl() { } + @Override + public boolean isAutoApplicable() { + return false; + } + @Override public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute( MemberDetails memberDetails, diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorStandardImpl.java index 3fcf5df3ff..99ceb10b4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorStandardImpl.java @@ -33,6 +33,11 @@ public AutoApplicableConverterDescriptorStandardImpl(ConverterDescriptor linkedC this.linkedConverterDescriptor = linkedConverterDescriptor; } + @Override + public boolean isAutoApplicable() { + return true; + } + @Override public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute( MemberDetails memberDetails, @@ -51,16 +56,16 @@ public ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement final ResolvedMember collectionMember = resolveMember( memberDetails, context ); final ResolvedType elementType; - Class erasedType = collectionMember.getType().getErasedType(); + final Class erasedType = collectionMember.getType().getErasedType(); if ( Map.class.isAssignableFrom( erasedType ) ) { - List typeArguments = collectionMember.getType().typeParametersFor(Map.class); + final List typeArguments = collectionMember.getType().typeParametersFor(Map.class); if ( typeArguments.size() < 2 ) { return null; } elementType = typeArguments.get( 1 ); } else if ( Collection.class.isAssignableFrom( erasedType ) ) { - List typeArguments = collectionMember.getType().typeParametersFor(Collection.class); + final List typeArguments = collectionMember.getType().typeParametersFor(Collection.class); if ( typeArguments.isEmpty() ) { return null; } @@ -87,7 +92,7 @@ public ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey( final ResolvedType keyType; if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) { - List typeArguments = collectionMember.getType().typeParametersFor(Map.class); + final List typeArguments = collectionMember.getType().typeParametersFor(Map.class); if ( typeArguments.isEmpty() ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/spi/AutoApplicableConverterDescriptor.java b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/spi/AutoApplicableConverterDescriptor.java index bec8787486..07d899c0a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/spi/AutoApplicableConverterDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/spi/AutoApplicableConverterDescriptor.java @@ -13,6 +13,7 @@ * @author Steve Ebersole */ public interface AutoApplicableConverterDescriptor { + boolean isAutoApplicable(); ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(MemberDetails memberDetails, MetadataBuildingContext context); ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(MemberDetails memberDetails, MetadataBuildingContext context); ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(MemberDetails memberDetails, MetadataBuildingContext context); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractPropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractPropertyHolder.java index 6d25700958..1fe123f1f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractPropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractPropertyHolder.java @@ -87,7 +87,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder { @Override public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails attributeMember) { - AttributeConversionInfo info = locateAttributeConversionInfo( attributeMember ); + final AttributeConversionInfo info = locateAttributeConversionInfo( attributeMember ); if ( info != null ) { if ( info.isConversionDisabled() ) { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java index 9dd5af917e..953f5342c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java @@ -12,6 +12,7 @@ import java.util.function.Function; import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; @@ -20,35 +21,7 @@ import org.hibernate.AssertionFailure; import org.hibernate.MappingException; import org.hibernate.TimeZoneStorageStrategy; -import org.hibernate.annotations.AnyDiscriminator; -import org.hibernate.annotations.AnyKeyJavaClass; -import org.hibernate.annotations.AnyKeyJavaType; -import org.hibernate.annotations.AnyKeyJdbcType; -import org.hibernate.annotations.AnyKeyJdbcTypeCode; -import org.hibernate.annotations.CollectionId; -import org.hibernate.annotations.CollectionIdJavaType; -import org.hibernate.annotations.CollectionIdJdbcType; -import org.hibernate.annotations.CollectionIdJdbcTypeCode; -import org.hibernate.annotations.CollectionIdMutability; -import org.hibernate.annotations.CollectionIdType; -import org.hibernate.annotations.Immutable; -import org.hibernate.annotations.JdbcTypeCode; -import org.hibernate.annotations.ListIndexJavaType; -import org.hibernate.annotations.ListIndexJdbcType; -import org.hibernate.annotations.ListIndexJdbcTypeCode; -import org.hibernate.annotations.MapKeyJavaType; -import org.hibernate.annotations.MapKeyJdbcType; -import org.hibernate.annotations.MapKeyJdbcTypeCode; -import org.hibernate.annotations.MapKeyMutability; -import org.hibernate.annotations.MapKeyType; -import org.hibernate.annotations.Mutability; -import org.hibernate.annotations.Nationalized; -import org.hibernate.annotations.PartitionKey; -import org.hibernate.annotations.Target; -import org.hibernate.annotations.TimeZoneColumn; -import org.hibernate.annotations.TimeZoneStorage; -import org.hibernate.annotations.TimeZoneStorageType; -import org.hibernate.annotations.Type; +import org.hibernate.annotations.*; import org.hibernate.boot.internal.AnyKeyType; import org.hibernate.boot.model.convert.spi.ConverterDescriptor; import org.hibernate.boot.spi.AccessType; @@ -1188,30 +1161,43 @@ public Dialect getDialect() { } private void applyJpaConverter(MemberDetails attribute, ConverterDescriptor attributeConverterDescriptor) { - disallowConverter( attribute, Id.class ); - disallowConverter( attribute, Version.class ); + final boolean autoApply = attributeConverterDescriptor.getAutoApplyDescriptor().isAutoApplicable(); + disallowConverter( attribute, Id.class, autoApply ); + disallowConverter( attribute, Version.class, autoApply ); if ( kind == Kind.MAP_KEY ) { //noinspection deprecation - disallowConverter( attribute, MapKeyTemporal.class ); - disallowConverter( attribute, MapKeyEnumerated.class ); + disallowConverter( attribute, MapKeyTemporal.class, autoApply ); + disallowConverter( attribute, MapKeyEnumerated.class, autoApply ); } else { //noinspection deprecation - disallowConverter( attribute, Temporal.class ); - disallowConverter( attribute, Enumerated.class ); - disallowConverter( attribute, Embedded.class ); - disallowConverter( attribute, ManyToOne.class ); - disallowConverter( attribute, OneToOne.class ); - disallowConverter( attribute, OneToMany.class ); - disallowConverter( attribute, ManyToMany.class ); + disallowConverter( attribute, Temporal.class, autoApply ); + disallowConverter( attribute, Enumerated.class, autoApply ); + disallowConverter( attribute, ManyToOne.class, autoApply ); + disallowConverter( attribute, OneToOne.class, autoApply ); + disallowConverter( attribute, OneToMany.class, autoApply ); + disallowConverter( attribute, ManyToMany.class, autoApply ); + // Note that @Convert is only allowed in conjunction with + // @Embedded if it specifies a field using attributeName + disallowConverter( attribute, Embedded.class, autoApply ); + disallowConverter( attribute, EmbeddedId.class, autoApply ); } + // I assume that these do not work with converters (no tests) + disallowConverter( attribute, Struct.class, autoApply ); + disallowConverter( attribute, Array.class, autoApply ); + disallowConverter( attribute, Any.class, autoApply ); this.converterDescriptor = attributeConverterDescriptor; } - void disallowConverter(MemberDetails attribute, Class annotationType) { + void disallowConverter(MemberDetails attribute, Class annotationType, boolean autoApply) { + // NOTE: A really faithful reading of the JPA spec is that we should + // just silently ignore any auto-apply converter which matches + // one of the disallowed attribute types, but for now let's be + // a bit more fussy/helpful, and see how many people complain. if ( attribute.hasDirectAnnotationUsage( annotationType ) ) { throw new AnnotationException( "'AttributeConverter' not allowed for attribute '" + attribute.getName() - + "' annotated '@" + annotationType.getName() + "'" ); + + "' annotated '@" + annotationType.getName() + "'" + + ( autoApply ? " (use '@Convert(disableConversion=true)' to suppress this error)" : "" ) ); } }