Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static <T> BasicValue.Resolution<T> from(
final var typeConfiguration = bootstrapContext.getTypeConfiguration();
final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();

final var reflectedJtd = reflectedJtdResolver.get();
final JavaType<T> reflectedJtd;

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

if ( registeredType != null ) {
// so here is the legacy resolution
jdbcMapping = resolveSqlTypeIndicators( stdIndicators, registeredType, reflectedJtd );
jdbcMapping = resolveSqlTypeIndicators( stdIndicators, registeredType, registeredType.getJavaTypeDescriptor() );
}
else {
// there was not a "legacy" BasicType registration,
Expand Down Expand Up @@ -311,7 +311,11 @@ private static <T,E> BasicType<T> pluralBasicType(
pluralJavaType.resolveType(
bootstrapContext.getTypeConfiguration(),
dialect,
resolveSqlTypeIndicators( stdIndicators, registeredElementType, elementJavaType ),
resolveSqlTypeIndicators(
stdIndicators,
registeredElementType,
registeredElementType.getJavaTypeDescriptor()
),
selectable instanceof ColumnTypeInformation information ? information : null,
stdIndicators
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ public void contributeType(CompositeUserType<?> type) {
}

final int preferredSqlTypeCodeForDuration = getPreferredSqlTypeCodeForDuration( serviceRegistry );
if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) {
if ( preferredSqlTypeCodeForDuration != SqlTypes.DURATION ) {
adaptToPreferredSqlTypeCode(
typeConfiguration,
jdbcTypeRegistry,
Expand All @@ -772,9 +772,6 @@ public void contributeType(CompositeUserType<?> type) {
"org.hibernate.type.DurationType"
);
}
else {
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.DURATION );
}

addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOMETRY, SqlTypes.VARBINARY );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ default <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, Jav
indicators,
domainJtd
);
if ( resolvedJdbcType != jdbcType ) {
if ( getJavaTypeDescriptor() != domainJtd || 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() ) {
if ( getJavaTypeDescriptor() != domainJtd || resolvedJdbcTypeCode != jdbcType.getDefaultSqlTypeCode() ) {
return indicators.getTypeConfiguration().getBasicTypeRegistry()
.resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ), getName() );
.resolve( domainJtd, indicators.getJdbcType( resolvedJdbcTypeCode ) );
}
}
return (BasicType<X>) this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -49,6 +51,7 @@ public class BasicTypeRegistry implements Serializable {

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

public BasicTypeRegistry(TypeConfiguration typeConfiguration){
this.typeConfiguration = typeConfiguration;
Expand Down Expand Up @@ -256,14 +259,28 @@ private <J> BasicType<J> createIfUnregistered(
if ( registeredTypeMatches( javaType, jdbcType, registeredType ) ) {
return castNonNull( registeredType );
}
else {
final var createdType = creator.get();
register( javaType, jdbcType, createdType );
return createdType;
// Create an ad-hoc type since the java type doesn't come from the registry and is probably explicitly defined
else if ( typeConfiguration.getJavaTypeRegistry().resolveDescriptor( javaType.getJavaType() ) == javaType ) {
final var basicTypeReferences = typeReferencesByJavaTypeName.get( javaType.getTypeName() );
if ( basicTypeReferences != null && !basicTypeReferences.isEmpty() ) {
final var jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
for ( var typeReference : basicTypeReferences ) {
if ( jdbcTypeRegistry.getDescriptor( typeReference.getSqlTypeCode() ) == jdbcType ) {
final var basicType = typesByName.get( typeReference.getName() );
//noinspection unchecked
return registeredTypeMatches( javaType, jdbcType, basicType )
? (BasicType<J>) basicType
: (BasicType<J>) createBasicType( typeReference.getName(), typeReference );
}
}
}
}
final var createdType = creator.get();
register( javaType, jdbcType, createdType );
return createdType;
}

private static <J> boolean registeredTypeMatches(JavaType<J> javaType, JdbcType jdbcType, BasicType<J> registeredType) {
private static boolean registeredTypeMatches(JavaType<?> javaType, JdbcType jdbcType, @Nullable BasicType<?> registeredType) {
return registeredType != null
&& registeredType.getJdbcType() == jdbcType
&& registeredType.getMappedJavaType() == javaType;
Expand Down Expand Up @@ -334,7 +351,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 );
}
}

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

// Legacy name registration
if ( isNotEmpty( legacyTypeClassName ) ) {
typeReferencesByName.put( legacyTypeClassName, type );
addTypeReference( legacyTypeClassName, type );
}

// explicit registration keys
Expand Down Expand Up @@ -429,19 +446,31 @@ 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 );
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( key, type );
// if ( old != null && old != type ) {
// LOG.tracef(
// "Type registration key [%s] overrode previous entry : `%s`",
// key,
// 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( typeReference ) ) {
basicTypeReferences.add( typeReference );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,12 @@ private StandardBasicTypes() {
// Date / time data

/**
* The standard Hibernate type for mapping {@link Duration} to JDBC {@link org.hibernate.type.SqlTypes#INTERVAL_SECOND INTERVAL_SECOND}
* or {@link org.hibernate.type.SqlTypes#NUMERIC NUMERIC} as a fallback.
* The standard Hibernate type for mapping {@link Duration} to JDBC {@link org.hibernate.type.SqlTypes#DURATION DURATION}.
*/
public static final BasicTypeReference<Duration> DURATION = new BasicTypeReference<>(
"Duration",
Duration.class,
SqlTypes.INTERVAL_SECOND
SqlTypes.DURATION
);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
Expand Down Expand Up @@ -44,6 +46,7 @@
* 2.2.21. Duration
* By default, Hibernate maps Duration to the NUMERIC SQL type.
*/
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIntervalSecondType.class)
@ServiceRegistry(settings = @Setting(name = AvailableSettings.PREFERRED_DURATION_JDBC_TYPE, value = "INTERVAL_SECOND"))
public class DurationMappingTests {

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,12 @@ public boolean apply(Dialect dialect) {
}
}

public static class SupportsIntervalSecondType implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return definesDdlType( dialect, SqlTypes.INTERVAL_SECOND );
}
}

public static class SupportsVectorType implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return definesDdlType( dialect, SqlTypes.VECTOR );
Expand Down