Skip to content

Commit 90df2d7

Browse files
committed
HHH-19976 Don't adopt AdjustableBasicType name to create derived type
Also, store BasicTypeReferences by java type name and try finding a JavaType/JdbcType match when resolving a BasicType
1 parent f144b9f commit 90df2d7

File tree

6 files changed

+127
-33
lines changed

6 files changed

+127
-33
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public static <T> BasicValue.Resolution<T> from(
6868
final var typeConfiguration = bootstrapContext.getTypeConfiguration();
6969
final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
7070

71-
final var reflectedJtd = reflectedJtdResolver.get();
71+
final JavaType<T> reflectedJtd;
7272

7373
// NOTE: the distinction that is made below wrt `explicitJavaType` and `reflectedJtd`
7474
// is needed temporarily to trigger "legacy resolution" versus "ORM6 resolution.
@@ -110,7 +110,7 @@ else if ( explicitJdbcType != null ) {
110110
}
111111
}
112112
}
113-
else if ( reflectedJtd != null ) {
113+
else if ( ( reflectedJtd = reflectedJtdResolver.get() ) != null ) {
114114
// we were able to determine the "reflected java-type"
115115
// Use JTD if we know it to apply any specialized resolutions
116116
if ( reflectedJtd instanceof EnumJavaType enumJavaType ) {
@@ -150,7 +150,7 @@ else if ( explicitJdbcType != null ) {
150150

151151
if ( registeredType != null ) {
152152
// so here is the legacy resolution
153-
jdbcMapping = resolveSqlTypeIndicators( stdIndicators, registeredType, reflectedJtd );
153+
jdbcMapping = resolveSqlTypeIndicators( stdIndicators, registeredType, registeredType.getJavaTypeDescriptor() );
154154
}
155155
else {
156156
// there was not a "legacy" BasicType registration,
@@ -311,7 +311,11 @@ private static <T,E> BasicType<T> pluralBasicType(
311311
pluralJavaType.resolveType(
312312
bootstrapContext.getTypeConfiguration(),
313313
dialect,
314-
resolveSqlTypeIndicators( stdIndicators, registeredElementType, elementJavaType ),
314+
resolveSqlTypeIndicators(
315+
stdIndicators,
316+
registeredElementType,
317+
registeredElementType.getJavaTypeDescriptor()
318+
),
315319
selectable instanceof ColumnTypeInformation information ? information : null,
316320
stdIndicators
317321
);

hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ public void contributeType(CompositeUserType<?> type) {
762762
}
763763

764764
final int preferredSqlTypeCodeForDuration = getPreferredSqlTypeCodeForDuration( serviceRegistry );
765-
if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) {
765+
if ( preferredSqlTypeCodeForDuration != SqlTypes.DURATION ) {
766766
adaptToPreferredSqlTypeCode(
767767
typeConfiguration,
768768
jdbcTypeRegistry,
@@ -772,9 +772,6 @@ public void contributeType(CompositeUserType<?> type) {
772772
"org.hibernate.type.DurationType"
773773
);
774774
}
775-
else {
776-
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.DURATION );
777-
}
778775

779776
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
780777
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOMETRY, SqlTypes.VARBINARY );

hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ default <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, Jav
2525
indicators,
2626
domainJtd
2727
);
28-
if ( resolvedJdbcType != jdbcType ) {
28+
if ( getJavaTypeDescriptor() != domainJtd || resolvedJdbcType != jdbcType ) {
2929
return indicators.getTypeConfiguration().getBasicTypeRegistry()
30-
.resolve( domainJtd, resolvedJdbcType, getName() );
30+
.resolve( domainJtd, resolvedJdbcType );
3131
}
3232
}
3333
else {
3434
final int resolvedJdbcTypeCode = indicators.resolveJdbcTypeCode( jdbcType.getDefaultSqlTypeCode() );
35-
if ( resolvedJdbcTypeCode != jdbcType.getDefaultSqlTypeCode() ) {
35+
if ( getJavaTypeDescriptor() != domainJtd || resolvedJdbcTypeCode != jdbcType.getDefaultSqlTypeCode() ) {
3636
return indicators.getTypeConfiguration().getBasicTypeRegistry()
37-
.resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ), getName() );
37+
.resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ) );
3838
}
3939
}
4040
return (BasicType<X>) this;

hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package org.hibernate.type;
66

77
import java.io.Serializable;
8+
import java.util.ArrayList;
9+
import java.util.List;
810
import java.util.Map;
911
import java.util.concurrent.ConcurrentHashMap;
1012
import java.util.function.Supplier;
@@ -49,6 +51,7 @@ public class BasicTypeRegistry implements Serializable {
4951

5052
private final Map<String, BasicType<?>> typesByName = new ConcurrentHashMap<>();
5153
private final Map<String, BasicTypeReference<?>> typeReferencesByName = new ConcurrentHashMap<>();
54+
private final Map<String, List<BasicTypeReference<?>>> typeReferencesByJavaTypeName = new ConcurrentHashMap<>();
5255

5356
public BasicTypeRegistry(TypeConfiguration typeConfiguration){
5457
this.typeConfiguration = typeConfiguration;
@@ -256,14 +259,28 @@ private <J> BasicType<J> createIfUnregistered(
256259
if ( registeredTypeMatches( javaType, jdbcType, registeredType ) ) {
257260
return castNonNull( registeredType );
258261
}
259-
else {
260-
final var createdType = creator.get();
261-
register( javaType, jdbcType, createdType );
262-
return createdType;
262+
// Create an ad-hoc type since the java type doesn't come from the registry and is probably explicitly defined
263+
else if ( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( javaType.getJavaType() ) == javaType ) {
264+
final var basicTypeReferences = typeReferencesByJavaTypeName.get( javaType.getTypeName() );
265+
if ( basicTypeReferences != null && !basicTypeReferences.isEmpty() ) {
266+
final var jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
267+
for ( var typeReference : basicTypeReferences ) {
268+
if ( jdbcTypeRegistry.getDescriptor( typeReference.getSqlTypeCode() ) == jdbcType ) {
269+
final var basicType = typesByName.get( typeReference.getName() );
270+
//noinspection unchecked
271+
return registeredTypeMatches( javaType, jdbcType, basicType )
272+
? (BasicType<J>) basicType
273+
: (BasicType<J>) createBasicType( typeReference.getName(), typeReference );
274+
}
275+
}
276+
}
263277
}
278+
final var createdType = creator.get();
279+
register( javaType, jdbcType, createdType );
280+
return createdType;
264281
}
265282

266-
private static <J> boolean registeredTypeMatches(JavaType<J> javaType, JdbcType jdbcType, BasicType<J> registeredType) {
283+
private static boolean registeredTypeMatches(JavaType<?> javaType, JdbcType jdbcType, @Nullable BasicType<?> registeredType) {
267284
return registeredType != null
268285
&& registeredType.getJdbcType() == jdbcType
269286
&& registeredType.getMappedJavaType() == javaType;
@@ -334,7 +351,7 @@ public void addTypeReferenceRegistrationKey(String typeReferenceKey, String... a
334351
throw new IllegalArgumentException( "Couldn't find type reference with name: " + typeReferenceKey );
335352
}
336353
for ( String additionalTypeReferenceKey : additionalTypeReferenceKeys ) {
337-
typeReferencesByName.put( additionalTypeReferenceKey, basicTypeReference );
354+
addTypeReference( additionalTypeReferenceKey, basicTypeReference );
338355
}
339356
}
340357

@@ -384,7 +401,7 @@ public void addPrimeEntry(BasicTypeReference<?> type, String legacyTypeClassName
384401

385402
// Legacy name registration
386403
if ( isNotEmpty( legacyTypeClassName ) ) {
387-
typeReferencesByName.put( legacyTypeClassName, type );
404+
addTypeReference( legacyTypeClassName, type );
388405
}
389406

390407
// explicit registration keys
@@ -429,19 +446,31 @@ private void applyRegistrationKeys(BasicTypeReference<?> type, String[] keys) {
429446
// Incidentally, this might also help with map lookup efficiency.
430447
key = key.intern();
431448

432-
// Incredibly verbose logging disabled
433-
// LOG.tracef( "Adding type registration %s -> %s", key, type );
449+
addTypeReference( key, type );
450+
}
451+
}
452+
}
453+
454+
private void addTypeReference(String name, BasicTypeReference<?> typeReference) {
455+
// Incredibly verbose logging disabled
456+
// LOG.tracef( "Adding type registration %s -> %s", key, type );
434457

435458
// final BasicTypeReference<?> old =
436-
typeReferencesByName.put( key, type );
437-
// if ( old != null && old != type ) {
438-
// LOG.tracef(
439-
// "Type registration key [%s] overrode previous entry : `%s`",
440-
// key,
441-
// old
442-
// );
443-
// }
444-
}
459+
typeReferencesByName.put( name, typeReference );
460+
// if ( old != null && old != type ) {
461+
// LOG.tracef(
462+
// "Type registration key [%s] overrode previous entry : `%s`",
463+
// key,
464+
// old
465+
// );
466+
// }
467+
468+
final var basicTypeReferences = typeReferencesByJavaTypeName.computeIfAbsent(
469+
typeReference.getJavaType().getTypeName(),
470+
s -> new ArrayList<>()
471+
);
472+
if ( !basicTypeReferences.contains( typeReference ) ) {
473+
basicTypeReferences.add( typeReference );
445474
}
446475
}
447476
}

hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,13 +345,12 @@ private StandardBasicTypes() {
345345
// Date / time data
346346

347347
/**
348-
* The standard Hibernate type for mapping {@link Duration} to JDBC {@link org.hibernate.type.SqlTypes#INTERVAL_SECOND INTERVAL_SECOND}
349-
* or {@link org.hibernate.type.SqlTypes#NUMERIC NUMERIC} as a fallback.
348+
* The standard Hibernate type for mapping {@link Duration} to JDBC {@link org.hibernate.type.SqlTypes#DURATION DURATION}.
350349
*/
351350
public static final BasicTypeReference<Duration> DURATION = new BasicTypeReference<>(
352351
"Duration",
353352
Duration.class,
354-
SqlTypes.INTERVAL_SECOND
353+
SqlTypes.DURATION
355354
);
356355

357356
/**
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.envers.integration.basic;
6+
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.GeneratedValue;
9+
import jakarta.persistence.Id;
10+
import org.hibernate.annotations.Nationalized;
11+
import org.hibernate.community.dialect.DerbyDialect;
12+
import org.hibernate.dialect.DB2Dialect;
13+
import org.hibernate.dialect.HANADialect;
14+
import org.hibernate.dialect.OracleDialect;
15+
import org.hibernate.dialect.PostgreSQLDialect;
16+
import org.hibernate.dialect.SybaseDialect;
17+
import org.hibernate.envers.Audited;
18+
import org.hibernate.mapping.Table;
19+
import org.hibernate.testing.orm.junit.DomainModel;
20+
import org.hibernate.testing.orm.junit.DomainModelScope;
21+
import org.hibernate.testing.orm.junit.JiraKey;
22+
import org.hibernate.testing.orm.junit.SessionFactory;
23+
import org.hibernate.testing.orm.junit.SkipForDialect;
24+
import org.hibernate.type.StandardBasicTypes;
25+
import org.junit.jupiter.api.Test;
26+
import org.opentest4j.AssertionFailedError;
27+
28+
import static org.hibernate.boot.model.naming.Identifier.toIdentifier;
29+
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.junit.jupiter.api.Assertions.assertThrows;
31+
32+
@JiraKey(value = "HHH-19976")
33+
@DomainModel(annotatedClasses = {NationalizedTest.NationalizedEntity.class})
34+
@SessionFactory
35+
@SkipForDialect(dialectClass = OracleDialect.class)
36+
@SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "@Lob field in HQL predicate fails with error about text = bigint")
37+
@SkipForDialect(dialectClass = HANADialect.class, matchSubTypes = true, reason = "HANA doesn't support comparing LOBs with the = operator")
38+
@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase doesn't support comparing LOBs with the = operator")
39+
@SkipForDialect(dialectClass = DB2Dialect.class, matchSubTypes = true, reason = "DB2 jdbc driver doesn't support setNString")
40+
@SkipForDialect(dialectClass = DerbyDialect.class, matchSubTypes = true, reason = "Derby jdbc driver doesn't support setNString")
41+
public class NationalizedTest {
42+
43+
@Test
44+
public void testMetadataBindings(DomainModelScope scope) {
45+
final var domainModel = scope.getDomainModel();
46+
47+
assertThrows( AssertionFailedError.class, () -> {
48+
final Table auditTable = domainModel.getEntityBinding( NationalizedEntity.class.getName() + "_AUD" )
49+
.getTable();
50+
51+
final org.hibernate.mapping.Column colDef = auditTable.getColumn( toIdentifier( "nationalizedString" ) );
52+
assertEquals( StandardBasicTypes.NSTRING.getName(), colDef.getTypeName() );
53+
} );
54+
}
55+
56+
@Entity(name = "NationalizedEntity")
57+
@Audited
58+
public static class NationalizedEntity {
59+
@Id
60+
@GeneratedValue
61+
private Integer id;
62+
@Nationalized
63+
private String nationalizedString;
64+
}
65+
}

0 commit comments

Comments
 (0)