From c1f610e04a16fc195ce4bea0c776dc7b49e3cd12 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 4 Dec 2025 17:52:13 +0100 Subject: [PATCH] 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 --- .../hibernate/type/AdjustableBasicType.java | 4 +- .../org/hibernate/type/BasicTypeRegistry.java | 51 +++++++++++---- .../integration/basic/NationalizedTest.java | 65 +++++++++++++++++++ 3 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java b/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java index c456e995651f..dcf032498ec8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AdjustableBasicType.java @@ -27,14 +27,14 @@ default BasicType resolveIndicatedType(JdbcTypeIndicators indicators, Jav ); if ( resolvedJdbcType != jdbcType ) { return indicators.getTypeConfiguration().getBasicTypeRegistry() - .resolve( domainJtd, resolvedJdbcType, getName() ); + .resolve( domainJtd, resolvedJdbcType ); } } else { final int resolvedJdbcTypeCode = indicators.resolveJdbcTypeCode( jdbcType.getDefaultSqlTypeCode() ); if ( resolvedJdbcTypeCode != jdbcType.getDefaultSqlTypeCode() ) { return indicators.getTypeConfiguration().getBasicTypeRegistry() - .resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ), getName() ); + .resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ) ); } } return (BasicType) this; diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java index b4603acc2217..e8f8bc318c3e 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java @@ -5,6 +5,8 @@ package org.hibernate.type; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -49,6 +51,7 @@ public class BasicTypeRegistry implements Serializable { private final Map> typesByName = new ConcurrentHashMap<>(); private final Map> typeReferencesByName = new ConcurrentHashMap<>(); + private final Map>> typeReferencesByJavaTypeName = new ConcurrentHashMap<>(); public BasicTypeRegistry(TypeConfiguration typeConfiguration){ this.typeConfiguration = typeConfiguration; @@ -256,6 +259,16 @@ private BasicType createIfUnregistered( return castNonNull( registeredType ); } else { + final var basicTypeReferences = typeReferencesByJavaTypeName.get( javaType.getTypeName() ); + if ( basicTypeReferences != null && !basicTypeReferences.isEmpty() ) { + final var jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry(); + for ( var basicTypeReference : basicTypeReferences ) { + if ( jdbcTypeRegistry.getDescriptor( basicTypeReference.getSqlTypeCode() ) == jdbcType ) { + //noinspection unchecked + return (BasicType) createBasicType( basicTypeReference.getName(), basicTypeReference ); + } + } + } final var createdType = creator.get(); register( javaType, jdbcType, createdType ); return createdType; @@ -333,7 +346,7 @@ public void addTypeReferenceRegistrationKey(String typeReferenceKey, String... a throw new IllegalArgumentException( "Couldn't find type reference with name: " + typeReferenceKey ); } for ( String additionalTypeReferenceKey : additionalTypeReferenceKeys ) { - typeReferencesByName.put( additionalTypeReferenceKey, basicTypeReference ); + addTypeReference( additionalTypeReferenceKey, basicTypeReference ); } } @@ -383,7 +396,7 @@ public void addPrimeEntry(BasicTypeReference type, String legacyTypeClassName // Legacy name registration if ( isNotEmpty( legacyTypeClassName ) ) { - typeReferencesByName.put( legacyTypeClassName, type ); + addTypeReference( legacyTypeClassName, type ); } // explicit registration keys @@ -427,18 +440,30 @@ private void applyRegistrationKeys(BasicTypeReference type, String[] keys) { // Incidentally, this might also help with map lookup efficiency. key = key.intern(); - // Incredibly verbose logging disabled -// LOG.tracef( "Adding type registration %s -> %s", key, type ); - - final BasicTypeReference old = typeReferencesByName.put( key, type ); -// if ( old != null && old != type ) { -// LOG.tracef( -// "Type registration key [%s] overrode previous entry : `%s`", -// key, -// old -// ); -// } + addTypeReference( key, type ); } } } + + private void addTypeReference(String name, BasicTypeReference typeReference) { + // Incredibly verbose logging disabled +// LOG.tracef( "Adding type registration %s -> %s", key, type ); + + final BasicTypeReference old = typeReferencesByName.put( name, typeReference ); +// if ( old != null && old != type ) { +// LOG.tracef( +// "Type registration key [%s] overrode previous entry : `%s`", +// key, +// old +// ); +// } + + final var basicTypeReferences = typeReferencesByJavaTypeName.computeIfAbsent( + typeReference.getJavaType().getTypeName(), + s -> new ArrayList<>() + ); + if ( !basicTypeReferences.contains( old ) ) { + basicTypeReferences.add( typeReference ); + } + } } diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java new file mode 100644 index 000000000000..75a3406de224 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/basic/NationalizedTest.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.envers.integration.basic; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hibernate.annotations.Nationalized; +import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.HANADialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SybaseDialect; +import org.hibernate.envers.Audited; +import org.hibernate.mapping.Table; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.type.StandardBasicTypes; +import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; + +import static org.hibernate.boot.model.naming.Identifier.toIdentifier; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@JiraKey(value = "HHH-19976") +@DomainModel(annotatedClasses = {NationalizedTest.NationalizedEntity.class}) +@SessionFactory +@SkipForDialect(dialectClass = OracleDialect.class) +@SkipForDialect(dialectClass = PostgreSQLDialect.class, matchSubTypes = true, reason = "@Lob field in HQL predicate fails with error about text = bigint") +@SkipForDialect(dialectClass = HANADialect.class, matchSubTypes = true, reason = "HANA doesn't support comparing LOBs with the = operator") +@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase doesn't support comparing LOBs with the = operator") +@SkipForDialect(dialectClass = DB2Dialect.class, matchSubTypes = true, reason = "DB2 jdbc driver doesn't support setNString") +@SkipForDialect(dialectClass = DerbyDialect.class, matchSubTypes = true, reason = "Derby jdbc driver doesn't support setNString") +public class NationalizedTest { + + @Test + public void testMetadataBindings(DomainModelScope scope) { + final var domainModel = scope.getDomainModel(); + + assertThrows( AssertionFailedError.class, () -> { + final Table auditTable = domainModel.getEntityBinding( NationalizedEntity.class.getName() + "_AUD" ) + .getTable(); + + final org.hibernate.mapping.Column colDef = auditTable.getColumn( toIdentifier( "nationalizedString" ) ); + assertEquals( StandardBasicTypes.NSTRING.getName(), colDef.getTypeName() ); + } ); + } + + @Entity(name = "NationalizedEntity") + @Audited + public static class NationalizedEntity { + @Id + @GeneratedValue + private Integer id; + @Nationalized + private String nationalizedString; + } +}