improve the error message and add comments

This commit is contained in:
Gavin King 2024-12-31 12:50:59 +01:00
parent 9ff21ad449
commit abf55433a2
5 changed files with 44 additions and 47 deletions

View File

@ -24,6 +24,11 @@ public class AutoApplicableConverterDescriptorBypassedImpl implements AutoApplic
private AutoApplicableConverterDescriptorBypassedImpl() { private AutoApplicableConverterDescriptorBypassedImpl() {
} }
@Override
public boolean isAutoApplicable() {
return false;
}
@Override @Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute( public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(
MemberDetails memberDetails, MemberDetails memberDetails,

View File

@ -33,6 +33,11 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
this.linkedConverterDescriptor = linkedConverterDescriptor; this.linkedConverterDescriptor = linkedConverterDescriptor;
} }
@Override
public boolean isAutoApplicable() {
return true;
}
@Override @Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute( public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(
MemberDetails memberDetails, MemberDetails memberDetails,
@ -51,16 +56,16 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
final ResolvedMember<?> collectionMember = resolveMember( memberDetails, context ); final ResolvedMember<?> collectionMember = resolveMember( memberDetails, context );
final ResolvedType elementType; final ResolvedType elementType;
Class<?> erasedType = collectionMember.getType().getErasedType(); final Class<?> erasedType = collectionMember.getType().getErasedType();
if ( Map.class.isAssignableFrom( erasedType ) ) { if ( Map.class.isAssignableFrom( erasedType ) ) {
List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Map.class); final List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Map.class);
if ( typeArguments.size() < 2 ) { if ( typeArguments.size() < 2 ) {
return null; return null;
} }
elementType = typeArguments.get( 1 ); elementType = typeArguments.get( 1 );
} }
else if ( Collection.class.isAssignableFrom( erasedType ) ) { else if ( Collection.class.isAssignableFrom( erasedType ) ) {
List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Collection.class); final List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Collection.class);
if ( typeArguments.isEmpty() ) { if ( typeArguments.isEmpty() ) {
return null; return null;
} }
@ -87,7 +92,7 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
final ResolvedType keyType; final ResolvedType keyType;
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) { if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Map.class); final List<ResolvedType> typeArguments = collectionMember.getType().typeParametersFor(Map.class);
if ( typeArguments.isEmpty() ) { if ( typeArguments.isEmpty() ) {
return null; return null;
} }

View File

@ -13,6 +13,7 @@ import org.hibernate.models.spi.MemberDetails;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface AutoApplicableConverterDescriptor { public interface AutoApplicableConverterDescriptor {
boolean isAutoApplicable();
ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(MemberDetails memberDetails, MetadataBuildingContext context); ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(MemberDetails memberDetails, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(MemberDetails memberDetails, MetadataBuildingContext context); ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(MemberDetails memberDetails, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(MemberDetails memberDetails, MetadataBuildingContext context); ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(MemberDetails memberDetails, MetadataBuildingContext context);

View File

@ -87,7 +87,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
@Override @Override
public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails attributeMember) { public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails attributeMember) {
AttributeConversionInfo info = locateAttributeConversionInfo( attributeMember ); final AttributeConversionInfo info = locateAttributeConversionInfo( attributeMember );
if ( info != null ) { if ( info != null ) {
if ( info.isConversionDisabled() ) { if ( info.isConversionDisabled() ) {
return null; return null;

View File

@ -12,6 +12,7 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import jakarta.persistence.Embedded; import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
@ -20,35 +21,7 @@ import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.TimeZoneStorageStrategy;
import org.hibernate.annotations.AnyDiscriminator; import org.hibernate.annotations.*;
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.boot.internal.AnyKeyType; import org.hibernate.boot.internal.AnyKeyType;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor; import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.AccessType; import org.hibernate.boot.spi.AccessType;
@ -1188,30 +1161,43 @@ public class BasicValueBinder implements JdbcTypeIndicators {
} }
private void applyJpaConverter(MemberDetails attribute, ConverterDescriptor attributeConverterDescriptor) { private void applyJpaConverter(MemberDetails attribute, ConverterDescriptor attributeConverterDescriptor) {
disallowConverter( attribute, Id.class ); final boolean autoApply = attributeConverterDescriptor.getAutoApplyDescriptor().isAutoApplicable();
disallowConverter( attribute, Version.class ); disallowConverter( attribute, Id.class, autoApply );
disallowConverter( attribute, Version.class, autoApply );
if ( kind == Kind.MAP_KEY ) { if ( kind == Kind.MAP_KEY ) {
//noinspection deprecation //noinspection deprecation
disallowConverter( attribute, MapKeyTemporal.class ); disallowConverter( attribute, MapKeyTemporal.class, autoApply );
disallowConverter( attribute, MapKeyEnumerated.class ); disallowConverter( attribute, MapKeyEnumerated.class, autoApply );
} }
else { else {
//noinspection deprecation //noinspection deprecation
disallowConverter( attribute, Temporal.class ); disallowConverter( attribute, Temporal.class, autoApply );
disallowConverter( attribute, Enumerated.class ); disallowConverter( attribute, Enumerated.class, autoApply );
disallowConverter( attribute, Embedded.class ); disallowConverter( attribute, ManyToOne.class, autoApply );
disallowConverter( attribute, ManyToOne.class ); disallowConverter( attribute, OneToOne.class, autoApply );
disallowConverter( attribute, OneToOne.class ); disallowConverter( attribute, OneToMany.class, autoApply );
disallowConverter( attribute, OneToMany.class ); disallowConverter( attribute, ManyToMany.class, autoApply );
disallowConverter( attribute, ManyToMany.class ); // 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; this.converterDescriptor = attributeConverterDescriptor;
} }
void disallowConverter(MemberDetails attribute, Class<? extends Annotation> annotationType) { void disallowConverter(MemberDetails attribute, Class<? extends Annotation> 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 ) ) { if ( attribute.hasDirectAnnotationUsage( annotationType ) ) {
throw new AnnotationException( "'AttributeConverter' not allowed for attribute '" + attribute.getName() 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)" : "" ) );
} }
} }