From 719e5fdd7baef0297d215e9a76854f200c11f40f Mon Sep 17 00:00:00 2001 From: Vincent Bouthinon Date: Tue, 8 Jul 2025 10:52:22 +0200 Subject: [PATCH 1/2] HHH-19589 : Test demonstrating that @Converter is ignored when @TypeRegistration is present --- ...onverterOverrideTypeRegisttrationTest.java | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java new file mode 100644 index 000000000000..4718a5007f9f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConverterOverrideTypeRegisttrationTest.java @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.mapping.converted.converter; + +import java.util.BitSet; + +import org.hibernate.annotations.TypeRegistration; +import org.hibernate.orm.test.mapping.basic.bitset.BitSetHelper; +import org.hibernate.orm.test.mapping.basic.bitset.BitSetUserType; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Converter; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + *
+ * The @Converter should take precedence over @TypeRegistration.
+ * This test shows that this is not the case.
+ *
+ * To ensure that the @Converter is taken into account without @TypeRegistration, you just need to remove the @TypeRegistration.
+ * 
+ * + * @author Vincent Bouthinon + */ +@DomainModel( + annotatedClasses = { + ConverterOverrideTypeRegisttrationTest.SimpleEntity.class + } +) +@SessionFactory +@JiraKey(value = "HHH-19589") +public class ConverterOverrideTypeRegisttrationTest { + + @Test + void test(SessionFactoryScope scope) { + scope.inTransaction( session -> { + final SimpleEntity object = new SimpleEntity( 77L ); + BitSet bitSet = new BitSet(); + bitSet.set( 0, true ); + object.setBitSet( bitSet ); + session.persist( object ); + session.flush(); + session.clear(); + SimpleEntity simpleEntity = session.find( SimpleEntity.class, object.id ); + assertThat( simpleEntity.getBitSet().get( 7 ) ).isTrue(); + } ); + } + + + @Entity(name = "SimpleEntity") + @TypeRegistration(basicClass = BitSet.class, userType = BitSetUserType.class) // Remove this annotation to test the use of @Converter + public static class SimpleEntity { + + @Id + private Long id; + @Convert(converter = BitSetConverter.class) + private BitSet bitSet; + + public SimpleEntity() { + } + + public SimpleEntity(Long id) { + this.id = id; + } + + public BitSet getBitSet() { + return bitSet; + } + + public void setBitSet(final BitSet bitSet) { + this.bitSet = bitSet; + } + } + + @Converter + public static class BitSetConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(final BitSet attribute) { + return BitSetHelper.bitSetToString( attribute ); + } + + @Override + public BitSet convertToEntityAttribute(final String dbData) { + BitSet bitSet = new BitSet(); + bitSet.set( 7, true ); + return bitSet; + } + } +} From eb4f148d125058298850932dfda6e71cfda4b865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Wed, 20 Aug 2025 09:27:39 +0200 Subject: [PATCH 2/2] HHH-19589 If property is annotated with @Convert do not check for (registered) user type --- .../boot/model/internal/BasicValueBinder.java | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) 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 86123b42ac2d..7375e17eaa68 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 @@ -332,24 +332,25 @@ public void setType( if ( converterDescriptor != null ) { applyJpaConverter( value, converterDescriptor ); } - - final Class> userTypeImpl = - kind.mappingAccess.customType( value, getSourceModelContext() ); - if ( userTypeImpl != null ) { - applyExplicitType( userTypeImpl, - kind.mappingAccess.customTypeParameters( value, getSourceModelContext() ) ); - // An explicit custom UserType has top precedence when we get to BasicValue resolution. - return; - } - else if ( modelClassDetails != null ) { - final ClassDetails rawClassDetails = modelClassDetails.determineRawClass(); - final Class basicClass = rawClassDetails.toJavaClass(); - final Class> registeredUserTypeImpl = - getMetadataCollector().findRegisteredUserType( basicClass ); - if ( registeredUserTypeImpl != null ) { - applyExplicitType( registeredUserTypeImpl, emptyMap() ); + else { + final Class> userTypeImpl = + kind.mappingAccess.customType( value, getSourceModelContext() ); + if ( userTypeImpl != null ) { + applyExplicitType( userTypeImpl, + kind.mappingAccess.customTypeParameters( value, getSourceModelContext() ) ); + // An explicit custom UserType has top precedence when we get to BasicValue resolution. return; } + else if ( modelClassDetails != null ) { + final ClassDetails rawClassDetails = modelClassDetails.determineRawClass(); + final Class basicClass = rawClassDetails.toJavaClass(); + final Class> registeredUserTypeImpl = + getMetadataCollector().findRegisteredUserType( basicClass ); + if ( registeredUserTypeImpl != null ) { + applyExplicitType( registeredUserTypeImpl, emptyMap() ); + return; + } + } } switch ( kind ) {