Skip to content

Commit 221f17c

Browse files
committed
HHH-19286 Ignoring auto-applied conversions on special mappings
1 parent 7b7a964 commit 221f17c

File tree

5 files changed

+127
-9
lines changed

5 files changed

+127
-9
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractPropertyHolder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
8686
protected abstract AttributeConversionInfo locateAttributeConversionInfo(String path);
8787

8888
@Override
89-
public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails attributeMember) {
89+
public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails attributeMember, boolean autoApply) {
9090
final AttributeConversionInfo info = locateAttributeConversionInfo( attributeMember );
9191
if ( info != null ) {
9292
if ( info.isConversionDisabled() ) {
@@ -102,7 +102,7 @@ public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails att
102102
}
103103
}
104104

105-
return context.getMetadataCollector()
105+
return !autoApply ? null : context.getMetadataCollector()
106106
.getConverterRegistry()
107107
.getAttributeConverterAutoApplyHandler()
108108
.findAutoApplyConverterForAttribute( attributeMember, context );

hibernate-core/src/main/java/org/hibernate/boot/model/internal/BasicValueBinder.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import jakarta.persistence.Temporal;
2222
import jakarta.persistence.TemporalType;
2323
import jakarta.persistence.Version;
24+
import org.checkerframework.checker.nullness.qual.Nullable;
2425
import org.hibernate.AnnotationException;
2526
import org.hibernate.AssertionFailure;
2627
import org.hibernate.MappingException;
@@ -301,7 +302,7 @@ public void setType(
301302
MemberDetails value,
302303
TypeDetails typeDetails,
303304
String declaringClassName,
304-
ConverterDescriptor converterDescriptor) {
305+
@Nullable ConverterDescriptor converterDescriptor) {
305306
this.memberDetails = value;
306307
final boolean isArray = value.isArray();
307308
if ( typeDetails == null && !isArray ) {
@@ -1187,10 +1188,6 @@ private void applyJpaConverter(MemberDetails attribute, ConverterDescriptor attr
11871188
}
11881189

11891190
void disallowConverter(MemberDetails attribute, Class<? extends Annotation> annotationType, boolean autoApply) {
1190-
// NOTE: A really faithful reading of the JPA spec is that we should
1191-
// just silently ignore any auto-apply converter which matches
1192-
// one of the disallowed attribute types, but for now let's be
1193-
// a bit more fussy/helpful, and see how many people complain.
11941191
if ( attribute.hasDirectAnnotationUsage( annotationType ) ) {
11951192
throw new AnnotationException( "'AttributeConverter' not allowed for attribute '" + attribute.getName()
11961193
+ "' annotated '@" + annotationType.getName() + "'"

hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import jakarta.persistence.Basic;
88
import jakarta.persistence.ElementCollection;
99
import jakarta.persistence.EmbeddedId;
10+
import jakarta.persistence.Enumerated;
1011
import jakarta.persistence.GeneratedValue;
1112
import jakarta.persistence.Id;
1213
import jakarta.persistence.Lob;
@@ -24,6 +25,7 @@
2425
import jakarta.persistence.OneToOne;
2526
import jakarta.persistence.OrderBy;
2627
import jakarta.persistence.OrderColumn;
28+
import jakarta.persistence.Temporal;
2729
import jakarta.persistence.Version;
2830
import org.hibernate.AnnotationException;
2931
import org.hibernate.AssertionFailure;
@@ -266,7 +268,7 @@ private Property makePropertyAndValue() {
266268
memberDetails,
267269
returnedClass,
268270
containerClassName,
269-
holder.resolveAttributeConverterDescriptor( memberDetails )
271+
holder.resolveAttributeConverterDescriptor( memberDetails, autoApplyConverters() )
270272
);
271273
basicValueBinder.setReferencedEntityName( referencedEntityName );
272274
basicValueBinder.setAccessType( accessType );
@@ -276,6 +278,18 @@ private Property makePropertyAndValue() {
276278
return makeProperty();
277279
}
278280

281+
private boolean autoApplyConverters() {
282+
// JPA 3.2 section 3.9 says there are exceptions where to auto-apply converters, citing:
283+
// The conversion of all basic types is supported except for the following:
284+
// Id attributes (including the attributes of embedded ids and derived identities),
285+
// version attributes, relationship attributes,
286+
// and attributes explicitly annotated as Enumerated or Temporal
287+
return !isId
288+
&& !isVersion( memberDetails )
289+
&& !memberDetails.hasDirectAnnotationUsage( Enumerated.class )
290+
&& !memberDetails.hasDirectAnnotationUsage( Temporal.class );
291+
}
292+
279293
@SuppressWarnings({"rawtypes", "unchecked"})
280294
private void callAttributeBinders(Property property, Map<String, PersistentClass> persistentClasses) {
281295
final List<? extends Annotation> metaAnnotatedTargets =

hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,5 @@ default ForeignKey getOverriddenForeignKey(String propertyName) {
108108
*
109109
* @return The ConverterDescriptor
110110
*/
111-
ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails property);
111+
ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails property, boolean autoApply);
112112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.mapping.converted.converter;
6+
7+
import jakarta.persistence.AttributeConverter;
8+
import jakarta.persistence.Converter;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.Id;
11+
import jakarta.persistence.Version;
12+
import org.hibernate.testing.orm.junit.DomainModel;
13+
import org.hibernate.testing.orm.junit.JiraKey;
14+
import org.hibernate.testing.orm.junit.SessionFactory;
15+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
16+
import org.junit.jupiter.api.AfterEach;
17+
import org.junit.jupiter.api.Test;
18+
19+
import static org.junit.Assert.assertEquals;
20+
21+
@JiraKey( value = "HHH-19286" )
22+
@DomainModel( annotatedClasses = {
23+
IgnoreAutoAppliedConverterForIdAndVersionTest.SampleEntity.class,
24+
IgnoreAutoAppliedConverterForIdAndVersionTest.NegatingIntegerConverter.class,
25+
} )
26+
@SessionFactory
27+
public class IgnoreAutoAppliedConverterForIdAndVersionTest {
28+
public final String sql = "select code from SampleEntity where id = :id";
29+
30+
@Test
31+
public void test(SessionFactoryScope scope) {
32+
scope.inTransaction(
33+
(session) -> {
34+
final SampleEntity sampleEntity = new SampleEntity();
35+
sampleEntity.setId( 1 );
36+
sampleEntity.setCode( 123 );
37+
38+
session.persist( sampleEntity );
39+
}
40+
);
41+
42+
scope.inTransaction(
43+
(session) -> {
44+
final Integer code = session.createNativeQuery( sql, Integer.class )
45+
.setParameter( "id", 1L )
46+
.getSingleResult();
47+
assertEquals( -123, (int) code );
48+
49+
final SampleEntity sampleEntity = session.find( SampleEntity.class, 1 );
50+
51+
assertEquals( 0, (int) sampleEntity.getVersion() );
52+
assertEquals( 123, (int) sampleEntity.getCode() );
53+
}
54+
);
55+
}
56+
57+
@AfterEach
58+
public void dropTestData(SessionFactoryScope scope) {
59+
scope.inTransaction( (session) -> session.createQuery( "delete SampleEntity" ).executeUpdate() );
60+
}
61+
62+
@Entity(name = "SampleEntity")
63+
public static class SampleEntity {
64+
@Id
65+
private Integer id;
66+
@Version
67+
private Integer version;
68+
private Integer code;
69+
70+
public Integer getId() {
71+
return id;
72+
}
73+
74+
public void setId(Integer id) {
75+
this.id = id;
76+
}
77+
78+
public Integer getVersion() {
79+
return version;
80+
}
81+
82+
public void setVersion(Integer version) {
83+
this.version = version;
84+
}
85+
86+
public Integer getCode() {
87+
return code;
88+
}
89+
90+
public void setCode(Integer code) {
91+
this.code = code;
92+
}
93+
}
94+
95+
@Converter( autoApply = true )
96+
static class NegatingIntegerConverter implements AttributeConverter<Integer,Integer> {
97+
@Override
98+
public Integer convertToDatabaseColumn(Integer attribute) {
99+
return attribute == null ? null : -attribute;
100+
}
101+
102+
@Override
103+
public Integer convertToEntityAttribute(Integer dbData) {
104+
return dbData == null ? null : -dbData;
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)