Skip to content
Open
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 @@ -71,7 +71,7 @@ public static <T> BasicValue.Resolution<T> from(
final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration();
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();

final JavaType<T> 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 @@ -113,7 +113,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 @@ -153,7 +153,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 @@ -279,7 +279,11 @@ private static <T> BasicType<?> registeredType(
: pluralJavaType.resolveType(
typeConfiguration,
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 @@ -785,7 +785,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 @@ -795,9 +795,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,10 +5,13 @@
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;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.Internal;
import org.hibernate.MappingException;
Expand Down Expand Up @@ -47,6 +50,7 @@

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 @@ -251,14 +255,28 @@
if ( registeredTypeMatches( javaType, jdbcType, registeredType ) ) {
return registeredType;
}
else {
final BasicType<J> 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 BasicType<J> 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 @@ -342,7 +360,7 @@
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 @@ -394,7 +412,7 @@

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

// explicit registration keys
Expand Down Expand Up @@ -458,19 +476,30 @@
// 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 );

Check notice

Code scanning / CodeQL

Unread local variable Note

Variable 'BasicTypeReference<?> old' is never read.
// 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 @@ -13,9 +13,11 @@
import org.hibernate.boot.internal.MetadataBuilderImpl;
import org.hibernate.boot.internal.NamedProcedureCallDefinitionImpl;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.FunctionContributor;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.NamedEntityGraphDefinition;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.convert.spi.ConverterAutoApplyHandler;
Expand Down Expand Up @@ -97,6 +99,7 @@
import org.hibernate.type.descriptor.java.StringJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.internal.BasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.usertype.CompositeUserType;
Expand All @@ -105,6 +108,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
Expand Down Expand Up @@ -1075,6 +1079,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 IsJtds implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return dialect instanceof SybaseDialect && ( (SybaseDialect) dialect ).getDriverKind() == SybaseDriverKind.JTDS;
Expand Down Expand Up @@ -1140,7 +1150,7 @@ public boolean apply(Dialect dialect) {
}
}

private static final HashMap<Dialect, SqmFunctionRegistry> FUNCTION_REGISTRIES = new HashMap<>();
private static final HashMap<Dialect, FakeFunctionContributions> FUNCTION_CONTRIBUTIONS = new HashMap<>();

public static boolean definesFunction(Dialect dialect, String functionName) {
return getSqmFunctionRegistry( dialect ).findFunctionDescriptor( functionName ) != null;
Expand All @@ -1150,6 +1160,11 @@ public static boolean definesSetReturningFunction(Dialect dialect, String functi
return getSqmFunctionRegistry( dialect ).findSetReturningFunctionDescriptor( functionName ) != null;
}

public static boolean definesDdlType(Dialect dialect, int typeCode) {
final DdlTypeRegistry ddlTypeRegistry = getFunctionContributions( dialect ).typeConfiguration.getDdlTypeRegistry();
return ddlTypeRegistry.getDescriptor( typeCode ) != null;
}

public static class SupportsSubqueryInSelect implements DialectFeatureCheck {
@Override
public boolean apply(Dialect dialect) {
Expand All @@ -1171,24 +1186,33 @@ public boolean apply(Dialect dialect) {
}
}


private static SqmFunctionRegistry getSqmFunctionRegistry(Dialect dialect) {
SqmFunctionRegistry sqmFunctionRegistry = FUNCTION_REGISTRIES.get( dialect );
if ( sqmFunctionRegistry == null ) {
return getFunctionContributions( dialect ).functionRegistry;
}

private static FakeFunctionContributions getFunctionContributions(Dialect dialect) {
FakeFunctionContributions functionContributions = FUNCTION_CONTRIBUTIONS.get( dialect );
if ( functionContributions == null ) {
final TypeConfiguration typeConfiguration = new TypeConfiguration();
final SqmFunctionRegistry functionRegistry = new SqmFunctionRegistry();
typeConfiguration.scope( new FakeMetadataBuildingContext( typeConfiguration, functionRegistry ) );
final FakeTypeContributions typeContributions = new FakeTypeContributions( typeConfiguration );
final FakeFunctionContributions functionContributions = new FakeFunctionContributions(
functionContributions = new FakeFunctionContributions(
dialect,
typeConfiguration,
functionRegistry
);
dialect.contribute( typeContributions, typeConfiguration.getServiceRegistry() );
dialect.initializeFunctionRegistry( functionContributions );
FUNCTION_REGISTRIES.put( dialect, sqmFunctionRegistry = functionContributions.functionRegistry );
for ( TypeContributor typeContributor : ServiceLoader.load( TypeContributor.class ) ) {
typeContributor.contribute( typeContributions, typeConfiguration.getServiceRegistry() );
}
for ( FunctionContributor functionContributor : ServiceLoader.load( FunctionContributor.class ) ) {
functionContributor.contributeFunctions( functionContributions );
}
FUNCTION_CONTRIBUTIONS.put( dialect, functionContributions );
}
return sqmFunctionRegistry;
return functionContributions;
}

public static class FakeTypeContributions implements TypeContributions {
Expand Down
Loading