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 @@ -11,6 +11,7 @@
import java.util.function.Function;
import java.util.function.Supplier;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
Expand Down Expand Up @@ -45,6 +46,7 @@

import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.descriptor.java.JavaTypeHelper.isTemporal;
import static org.hibernate.type.descriptor.java.TemporalJavaType.resolveJdbcTypeCode;

/**
* BasicValue.Resolution resolver for cases where no explicit
Expand Down Expand Up @@ -156,23 +158,8 @@ else if ( explicitJdbcType != null ) {
// there was not a "legacy" BasicType registration,
// so use `JavaType#getRecommendedJdbcType`, if one,
// to create a mapping
final JdbcType recommendedJdbcType;
try {
recommendedJdbcType = reflectedJtd.getRecommendedJdbcType( stdIndicators );
}
catch (JdbcTypeRecommendationException jtre) {
if ( buildingContext.getMetadataCollector()
.getEntityBindingMap().values().stream()
.anyMatch( pc -> pc.getMappedClass().equals(resolvedJavaType) ) ) {
throw new MappingException( "Incorrect use of entity type '"
+ resolvedJavaType.getTypeName()
+ "' (possibly due to missing association mapping annotation)",
jtre );
}
else {
throw jtre;
}
}
final JdbcType recommendedJdbcType =
recommendedJdbcType( resolvedJavaType, stdIndicators, buildingContext, reflectedJtd );
if ( recommendedJdbcType != null ) {
jdbcMapping = resolveSqlTypeIndicators(
stdIndicators,
Expand Down Expand Up @@ -254,6 +241,27 @@ else if ( column.getLength() != null ) {
);
}

private static <T> JdbcType recommendedJdbcType(
Type resolvedJavaType,
JdbcTypeIndicators stdIndicators,
MetadataBuildingContext buildingContext,
JavaType<T> reflectedJtd) {
try {
return reflectedJtd.getRecommendedJdbcType( stdIndicators );
}
catch (JdbcTypeRecommendationException jtre) {
if ( buildingContext.getMetadataCollector()
.getEntityBindingMap().values().stream()
.anyMatch( pc -> pc.getMappedClass().equals( resolvedJavaType ) ) ) {
throw new MappingException( "Incorrect use of entity type '" + resolvedJavaType.getTypeName()
+ "' (possibly due to missing association mapping annotation)", jtre );
}
else {
throw jtre;
}
}
}

private static <T> BasicType<T> registeredType(
JdbcType explicitJdbcType,
Function<TypeConfiguration, MutabilityPlan<?>> explicitMutabilityPlanAccess,
Expand Down Expand Up @@ -485,43 +493,16 @@ public static <T> BasicValue.Resolution<T> fromTemporal(
JdbcType explicitJdbcType,
Function<TypeConfiguration, MutabilityPlan<?>> explicitMutabilityPlanAccess,
JdbcTypeIndicators stdIndicators) {
final var typeConfiguration = stdIndicators.getTypeConfiguration();
final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final var requestedTemporalPrecision = stdIndicators.getTemporalPrecision();

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Case #1 - explicit JavaType

if ( explicitJavaType != null ) {
if ( !isTemporal( explicitJavaType ) ) {
throw new MappingException(
"Explicit JavaType [" + explicitJavaType +
"] defined for temporal value must implement TemporalJavaType"
);
}

@SuppressWarnings("unchecked")
final var explicitTemporalJtd = (TemporalJavaType<T>) explicitJavaType;
if ( requestedTemporalPrecision != null && explicitTemporalJtd.getPrecision() != requestedTemporalPrecision ) {
throw new MappingException(
"Temporal precision (`jakarta.persistence.TemporalType`) mismatch... requested precision = " + requestedTemporalPrecision +
"; explicit JavaType (`" + explicitTemporalJtd + "`) precision = " + explicitTemporalJtd.getPrecision()

);
}

final var jdbcType =
explicitJdbcType != null
? explicitJdbcType
: explicitTemporalJtd.getRecommendedJdbcType( stdIndicators );
final var jdbcMapping = basicTypeRegistry.resolve( explicitTemporalJtd, jdbcType );
return new InferredBasicValueResolution<>(
jdbcMapping,
explicitTemporalJtd,
explicitTemporalJtd,
jdbcType,
jdbcMapping,
determineMutabilityPlan( explicitMutabilityPlanAccess, explicitTemporalJtd, typeConfiguration )
return fromTemporalExplicitJavaType(
explicitJavaType,
explicitJdbcType,
explicitMutabilityPlanAccess,
stdIndicators
);
}

Expand All @@ -532,19 +513,10 @@ public static <T> BasicValue.Resolution<T> fromTemporal(
// due to the new annotations being used

if ( explicitJdbcType != null ) {
final TemporalJavaType<T> temporalJavaType =
requestedTemporalPrecision != null
? reflectedJtd.resolveTypeForPrecision( requestedTemporalPrecision, typeConfiguration )
// Avoid using the DateJavaType and prefer the JdbcTimestampJavaType
: reflectedJtd.resolveTypeForPrecision( reflectedJtd.getPrecision(), typeConfiguration );
final BasicType<T> jdbcMapping = basicTypeRegistry.resolve( temporalJavaType, explicitJdbcType );
return new InferredBasicValueResolution<>(
jdbcMapping,
temporalJavaType,
temporalJavaType,
return fromTemporalExplicitJdbcType(
reflectedJtd,
explicitJdbcType,
jdbcMapping,
temporalJavaType.getMutabilityPlan()
stdIndicators
);
}

Expand All @@ -554,17 +526,31 @@ public static <T> BasicValue.Resolution<T> fromTemporal(
// - for the moment continue to use the legacy resolution to registered
// BasicType

return fromTemporalImplicit(
reflectedJtd,
explicitMutabilityPlanAccess,
stdIndicators
);
}

private static <T> @NonNull InferredBasicValueResolution<T, T> fromTemporalImplicit(
TemporalJavaType<T> reflectedJtd,
Function<TypeConfiguration, MutabilityPlan<?>> explicitMutabilityPlanAccess,
JdbcTypeIndicators stdIndicators) {
final var typeConfiguration = stdIndicators.getTypeConfiguration();
final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final var requestedTemporalPrecision = stdIndicators.getTemporalPrecision();

final BasicType<T> basicType;
if ( requestedTemporalPrecision != null
&& requestedTemporalPrecision != reflectedJtd.getPrecision() ) {
basicType = basicTypeRegistry.resolve(
reflectedJtd.resolveTypeForPrecision( requestedTemporalPrecision, typeConfiguration ),
TemporalJavaType.resolveJdbcTypeCode( requestedTemporalPrecision )
resolveJdbcTypeCode( requestedTemporalPrecision )
);
}
else {
basicType = basicTypeRegistry.resolve(
// Avoid using the DateJavaType and prefer the JdbcTimestampJavaType
reflectedJtd.resolveTypeForPrecision( reflectedJtd.getPrecision(), typeConfiguration ),
reflectedJtd.getRecommendedJdbcType( stdIndicators )
);
Expand All @@ -580,6 +566,69 @@ public static <T> BasicValue.Resolution<T> fromTemporal(
);
}

private static <T> @NonNull InferredBasicValueResolution<T, T> fromTemporalExplicitJdbcType(
TemporalJavaType<T> reflectedJtd,
JdbcType explicitJdbcType,
JdbcTypeIndicators stdIndicators) {
final var typeConfiguration = stdIndicators.getTypeConfiguration();
final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final var requestedTemporalPrecision = stdIndicators.getTemporalPrecision();

final var temporalJavaType =
requestedTemporalPrecision != null
? reflectedJtd.resolveTypeForPrecision( requestedTemporalPrecision, typeConfiguration )
: reflectedJtd.resolveTypeForPrecision( reflectedJtd.getPrecision(), typeConfiguration );
final var jdbcMapping = basicTypeRegistry.resolve( temporalJavaType, explicitJdbcType );
return new InferredBasicValueResolution<>(
jdbcMapping,
temporalJavaType,
temporalJavaType,
explicitJdbcType,
jdbcMapping,
temporalJavaType.getMutabilityPlan()
);
}

private static <T> @NonNull InferredBasicValueResolution<T, T> fromTemporalExplicitJavaType(
BasicJavaType<?> explicitJavaType,
JdbcType explicitJdbcType,
Function<TypeConfiguration, MutabilityPlan<?>> explicitMutabilityPlanAccess,
JdbcTypeIndicators stdIndicators) {
final var typeConfiguration = stdIndicators.getTypeConfiguration();
final var basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final var requestedTemporalPrecision = stdIndicators.getTemporalPrecision();

if ( !isTemporal( explicitJavaType ) ) {
throw new MappingException( "Explicit JavaType [" + explicitJavaType +
"] defined for temporal value must implement TemporalJavaType" );
}

@SuppressWarnings("unchecked")
final var explicitTemporalJtd = (TemporalJavaType<T>) explicitJavaType;
if ( requestedTemporalPrecision != null
&& explicitTemporalJtd.getPrecision() != requestedTemporalPrecision ) {
throw new MappingException(
"Temporal precision (TemporalType) mismatch; requested precision = %s; explicit JavaType (%s) precision = %s"
.formatted( requestedTemporalPrecision, explicitTemporalJtd, explicitTemporalJtd.getPrecision() )

);
}

final var jdbcType =
explicitJdbcType != null
? explicitJdbcType
: explicitTemporalJtd.getRecommendedJdbcType( stdIndicators );
final var jdbcMapping = basicTypeRegistry.resolve( explicitTemporalJtd, jdbcType );
return new InferredBasicValueResolution<>(
jdbcMapping,
explicitTemporalJtd,
explicitTemporalJtd,
jdbcType,
jdbcMapping,
determineMutabilityPlan( explicitMutabilityPlanAccess, explicitTemporalJtd, typeConfiguration )
);
}

private static <T> MutabilityPlan<T> determineMutabilityPlan(
Function<TypeConfiguration, MutabilityPlan<?>> explicitMutabilityPlanAccess,
JavaType<T> javaType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ public String getName() {

@Override
public Class<J> getJavaType() {
return valueType instanceof BasicTypeImpl basicType
? basicType.getJavaType()
// TODO: create a new method to abstract this logic
return valueType instanceof BasicTypeImpl<?> basicType
// handles primitives in basic types
? (Class<J>) basicType.getJavaType()
// good for everything else
: attributeJtd.getJavaTypeClass();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package org.hibernate.query.sqm.tree.select;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -23,11 +22,15 @@
import org.hibernate.query.sqm.tree.domain.SqmDomainType;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.jpa.AbstractJpaSelection;
import org.hibernate.type.descriptor.java.DateJavaType;
import org.hibernate.type.descriptor.java.JavaType;

import org.hibernate.type.descriptor.java.TemporalJavaType;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;

import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.query.sqm.DynamicInstantiationNature.CLASS;
Expand Down Expand Up @@ -157,7 +160,7 @@ public boolean checkInstantiation(TypeConfiguration typeConfiguration) {
if ( isConstructorCompatible( javaType, argTypes, typeConfiguration ) ) {
return true;
}
final List<SqmDynamicInstantiationArgument<?>> arguments = getArguments();
final var arguments = getArguments();
final List<String> aliases = new ArrayList<>( arguments.size() );
for ( var argument : arguments ) {
final String alias = argument.getAlias();
Expand All @@ -182,9 +185,18 @@ private List<Class<?>> argumentTypes() {
return getArguments().stream()
.map( arg -> {
final var expressible = arg.getExpressible();
return expressible != null && expressible.getExpressibleJavaType() != null ?
expressible.getExpressibleJavaType().getJavaTypeClass() :
Void.class;
if ( expressible != null ) {
final var expressibleJavaType = expressible.getExpressibleJavaType();
if ( expressibleJavaType != null ) {
return expressibleJavaType instanceof DateJavaType temporalJavaType
// Hack to accommodate a constructor with java.sql parameter
// types when the entity has java.util.Date as its field types.
// (This was requested in HHH-4179 and we fixed it by accident.)
? TemporalJavaType.resolveJavaTypeClass( temporalJavaType.getPrecision() )
: expressibleJavaType.getJavaTypeClass();
}
}
return Void.class;
} ).collect( toList() );
}

Expand Down Expand Up @@ -227,7 +239,7 @@ public SqmDynamicInstantiationTarget<T> getInstantiationTarget() {
}

public List<SqmDynamicInstantiationArgument<?>> getArguments() {
return arguments == null ? Collections.emptyList() : Collections.unmodifiableList( arguments );
return arguments == null ? emptyList() : unmodifiableList( arguments );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.InitializerParent;
import org.hibernate.sql.results.graph.instantiation.DynamicInstantiationResult;
import org.hibernate.type.descriptor.java.DateJavaType;
import org.hibernate.type.descriptor.java.JavaType;

import org.hibernate.type.descriptor.java.TemporalJavaType;
import org.jboss.logging.Logger;

import static java.util.stream.Collectors.toList;
Expand Down Expand Up @@ -157,7 +159,7 @@ private DomainResultAssembler<R> assembler(
final var constructor = findMatchingConstructor(
javaType.getJavaTypeClass(),
argumentReaders.stream()
.map( reader -> reader.getAssembledJavaType().getJavaTypeClass() )
.map( reader -> argumentClass( reader ) )
.collect( toList() ),
creationState.getSqlAstCreationContext()
.getMappingMetamodel()
Expand Down Expand Up @@ -194,6 +196,16 @@ private DomainResultAssembler<R> assembler(
return new DynamicInstantiationAssemblerInjectionImpl<>( javaType, argumentReaders );
}

private static Class<?> argumentClass(ArgumentReader<?> reader) {
final var assembledJavaType = reader.getAssembledJavaType();
return assembledJavaType instanceof DateJavaType temporalJavaType
// Hack to accommodate a constructor with java.sql parameter
// types when the entity has java.util.Date as its field types.
// (This was requested in HHH-4179 and we fixed it by accident.)
? TemporalJavaType.resolveJavaTypeClass( temporalJavaType.getPrecision() )
: assembledJavaType.getJavaTypeClass();
}

private List<String> signature() {
return argumentResults.stream()
.map( adt -> adt.getResultJavaType().getTypeName() )
Expand Down
Loading
Loading