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 @@ -64,7 +64,7 @@ public static <T> BasicValue.Resolution<T> from(
final BootstrapContext bootstrapContext = buildingContext.getBootstrapContext();
final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration();

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. Yes, it
Expand Down Expand Up @@ -105,7 +105,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 ) {
Expand Down Expand Up @@ -135,59 +135,20 @@ else if ( explicitJdbcType != null ) {
else {
// see if there is a registered BasicType for this JavaType and, if so, use it.
// this mimics the legacy handling
final BasicType registeredType;
if ( reflectedJtd instanceof BasicPluralJavaType<?> ) {
final BasicPluralJavaType<?> containerJtd = (BasicPluralJavaType<?>) reflectedJtd;
final JavaType<?> elementJtd = containerJtd.getElementJavaType();
final BasicType registeredElementType;
if ( elementJtd instanceof EnumJavaType ) {
final BasicValue.Resolution<?> resolution = fromEnum(
(EnumJavaType<?>) elementJtd,
final BasicType registeredType =
registeredType(
explicitJdbcType,
stdIndicators,
bootstrapContext);
registeredElementType = (BasicType<?>) resolution.getJdbcMapping();
}
else if ( isTemporal( elementJtd ) ) {
final BasicValue.Resolution<?> resolution = fromTemporal(
(TemporalJavaType<?>) elementJtd,
null,
null,
explicitMutabilityPlanAccess,
stdIndicators
stdIndicators,
selectable,
reflectedJtd,
bootstrapContext,
buildingContext.getMetadataCollector().getDatabase().getDialect()
);
registeredElementType = resolution.getLegacyResolvedBasicType();
}
else {
registeredElementType = typeConfiguration.getBasicTypeRegistry()
.getRegisteredType( elementJtd.getJavaType() );
}
final ColumnTypeInformation columnTypeInformation;
if ( selectable instanceof ColumnTypeInformation ) {
columnTypeInformation = (ColumnTypeInformation) selectable;
}
else {
columnTypeInformation = null;
}
registeredType = registeredElementType == null ? null : containerJtd.resolveType(
typeConfiguration,
dialect,
resolveSqlTypeIndicators( stdIndicators, registeredElementType, elementJtd ),
columnTypeInformation,
stdIndicators
);
if ( registeredType instanceof BasicPluralType<?, ?> ) {
typeConfiguration.getBasicTypeRegistry().register( registeredType );
}
}
else {
registeredType = typeConfiguration.getBasicTypeRegistry()
.getRegisteredType( reflectedJtd.getJavaType() );
}

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, so use `JavaType#getRecommendedJdbcType`, if
Expand Down Expand Up @@ -273,6 +234,76 @@ else if ( column.getLength() != null ) {
);
}

private static <T> BasicType<?> registeredType(
JdbcType explicitJdbcType,
Function<TypeConfiguration, MutabilityPlan> explicitMutabilityPlanAccess,
JdbcTypeIndicators stdIndicators,
Selectable selectable,
JavaType<T> reflectedJtd,
BootstrapContext bootstrapContext,
Dialect dialect) {
final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration();

if ( reflectedJtd instanceof BasicPluralJavaType<?> ) {
final BasicPluralJavaType<?> pluralJavaType = (BasicPluralJavaType<?>) reflectedJtd;
final JavaType<?> elementJavaType = pluralJavaType.getElementJavaType();
final BasicType registeredElementType = registeredElementType(
explicitJdbcType,
explicitMutabilityPlanAccess,
stdIndicators,
bootstrapContext,
elementJavaType
);
final BasicType<?> registeredType =
registeredElementType == null
? null
: pluralJavaType.resolveType(
typeConfiguration,
dialect,
resolveSqlTypeIndicators(
stdIndicators,
registeredElementType,
registeredElementType.getJavaTypeDescriptor()
),
selectable instanceof ColumnTypeInformation
? (ColumnTypeInformation) selectable
: null,
stdIndicators
);
if ( registeredType instanceof BasicPluralType<?, ?> ) {
typeConfiguration.getBasicTypeRegistry().register( registeredType );
}
return registeredType;
}
else {
return typeConfiguration.getBasicTypeRegistry().getRegisteredType( reflectedJtd.getJavaType() );
}
}

private static BasicType<?> registeredElementType(
JdbcType explicitJdbcType,
Function<TypeConfiguration, MutabilityPlan> explicitMutabilityPlanAccess,
JdbcTypeIndicators stdIndicators,
BootstrapContext context,
JavaType<?> elementJtd) {
if ( elementJtd instanceof EnumJavaType<?> ) {
return (BasicType<?>) fromEnum( (EnumJavaType<?>) elementJtd, explicitJdbcType, stdIndicators, context ).getJdbcMapping();
}
else if ( isTemporal( elementJtd ) ) {
final BasicValue.Resolution<?> resolution = fromTemporal(
(TemporalJavaType<?>) elementJtd,
null,
null,
explicitMutabilityPlanAccess,
stdIndicators
);
return resolution.getLegacyResolvedBasicType();
}
else {
return context.getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( elementJtd.getJavaType() );
}
}

public static <T> BasicType<T> resolveSqlTypeIndicators(
JdbcTypeIndicators stdIndicators,
BasicType<T> resolved,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,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 @@ -688,9 +688,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 @@ -27,16 +27,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 @@ -7,10 +7,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 @@ 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 @@ -205,14 +209,28 @@ private <J> BasicType<J> createIfUnregistered(
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 );
Comment on lines +219 to +223
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just lost a week of my life getting rid of all the dangerous unchecked typecasts in this and related classes, and in the process found bugs.

If you're going to do an unchecked cast, please check it first. Otherwise you've opened the door for the bugs to come seeping back in.

}
}
}
}
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 @@ -296,7 +314,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 @@ -348,7 +366,7 @@ public void addPrimeEntry(BasicTypeReference<?> type, String legacyTypeClassName

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

// explicit registration keys
Expand Down Expand Up @@ -417,16 +435,29 @@ private void applyRegistrationKeys(BasicTypeReference<?> type, String[] keys) {
//Incidentally this might help with map lookup efficiency too.
key = key.intern();

LOG.debugf( "Adding type registration %s -> %s", key, type );
addTypeReference( key, type );
}
}

final BasicTypeReference<?> old = typeReferencesByName.put( key, type );
if ( old != null && old != type ) {
LOG.debugf(
"Type registration key [%s] overrode previous entry : `%s`",
key,
old
);
}
private void addTypeReference(String name, BasicTypeReference<?> typeReference) {
// Incredibly verbose logging disabled
LOG.tracef( "Adding type registration %s -> %s", name, typeReference );
Comment on lines +443 to +444
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't turn this logging back on, it's so obnoxious it makes logging completely useless.


final BasicTypeReference<?> old = typeReferencesByName.put( name, typeReference );
if ( old != null && old != typeReference ) {
LOG.tracef(
"Type registration key [%s] overrode previous entry : `%s`",
name,
old
);
}

final var basicTypeReferences = typeReferencesByJavaTypeName.computeIfAbsent(
typeReference.getBindableJavaType().getTypeName(),
s -> new ArrayList<>()
);
if ( !basicTypeReferences.contains( typeReference ) ) {
basicTypeReferences.add( typeReference );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,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 @@ -18,6 +18,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 @@ -46,6 +48,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
Loading
Loading