HHH-17006, HHH-13016 various refactorings/cleanups before fix

- Misc cleanups and fixes to incorrect generic typing of some operations
- Try to fix bodgy type inference algorithm for case/when expressions
This commit is contained in:
Gavin King 2023-07-27 15:27:24 +02:00
parent 5daf7a0354
commit c74d6fa86f
31 changed files with 487 additions and 519 deletions

View File

@ -350,7 +350,7 @@ public class SessionFactoryDelegatingImpl implements SessionFactoryImplementor,
}
@Override @Deprecated
public <T> BindableType<? extends T> resolveParameterBindType(T bindValue) {
public <T> BindableType<? super T> resolveParameterBindType(T bindValue) {
return delegate.resolveParameterBindType( bindValue );
}

View File

@ -217,7 +217,7 @@ public interface SessionFactoryImplementor
* @deprecated Use {@link #getMappingMetamodel()}.{@link MappingMetamodelImplementor#resolveParameterBindType(Object)}
*/
@Override @Deprecated(since = "6.2", forRemoval = true)
<T> BindableType<? extends T> resolveParameterBindType(T bindValue);
<T> BindableType<? super T> resolveParameterBindType(T bindValue);
/**
* @deprecated Use {@link #getMappingMetamodel()}.{@link MappingMetamodelImplementor#resolveParameterBindType(Class)}

View File

@ -9,6 +9,10 @@ package org.hibernate.internal;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.query.BindableType;
import org.hibernate.query.spi.QueryParameterBindingTypeResolver;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.internal.BasicTypeImpl;
import java.io.Serializable;
@ -32,32 +36,52 @@ public abstract class QueryParameterBindingTypeResolverImpl implements QueryPara
return getMappingMetamodel().resolveQueryParameterType( javaType );
}
@Override
public <T> BindableType<? extends T> resolveParameterBindType(T bindValue) {
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public <T> BindableType<? super T> resolveParameterBindType(T bindValue) {
if ( bindValue == null ) {
// we can't guess
return null;
}
final LazyInitializer lazyInitializer = extractLazyInitializer( bindValue );
final Class<?> clazz = lazyInitializer != null ? lazyInitializer.getPersistentClass() : bindValue.getClass();
final Class<T> clazz = unproxiedClass( bindValue );
// Resolve superclass bindable type if necessary, as we don't register types for e.g. Inet4Address
Class<?> c = clazz;
Class<? super T> c = clazz;
do {
final BindableType<?> type = resolveParameterBindType( c );
final BindableType<? super T> type = resolveParameterBindType( c );
if ( type != null ) {
//noinspection unchecked
return (BindableType<? extends T>) type;
return type;
}
c = c.getSuperclass();
}
while ( c != Object.class );
if ( !clazz.isEnum() && Serializable.class.isAssignableFrom( clazz ) ) {
//noinspection unchecked
return (BindableType<? extends T>) resolveParameterBindType( Serializable.class );
if ( clazz.isEnum() ) {
return null;//createEnumType( (Class) clazz );
}
return null;
else if ( Serializable.class.isAssignableFrom( clazz ) ) {
return (BindableType<? super T>) resolveParameterBindType( Serializable.class );
}
else {
return null;
}
}
private <E extends Enum<E>> BasicType<E> createEnumType(Class<E> enumClass) {
final EnumJavaType<E> enumJavaType = new EnumJavaType<>( enumClass );
final JdbcType jdbcType =
// we don't know whether to map the enum as ORDINAL or STRING,
// so just accept the default from the TypeConfiguration, which
// is usually ORDINAL (the default according to JPA)
enumJavaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
return new BasicTypeImpl<>( enumJavaType, jdbcType );
}
@SuppressWarnings("unchecked")
private static <T> Class<T> unproxiedClass(T bindValue) {
final LazyInitializer lazyInitializer = extractLazyInitializer( bindValue );
final Class<?> result = lazyInitializer != null ? lazyInitializer.getPersistentClass() : bindValue.getClass();
return (Class<T>) result;
}
}

View File

@ -832,23 +832,19 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
final JavaTypeRegistry javaTypeRegistry = getTypeConfiguration().getJavaTypeRegistry();
final JavaType<T> javaType = javaTypeRegistry.findDescriptor( javaClass );
if ( javaType != null ) {
final JdbcType recommendedJdbcType = javaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
final JdbcType recommendedJdbcType =
javaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
if ( recommendedJdbcType != null ) {
return getTypeConfiguration().getBasicTypeRegistry().resolve(
javaType,
recommendedJdbcType
);
return getTypeConfiguration().getBasicTypeRegistry().resolve( javaType, recommendedJdbcType );
}
}
if ( javaClass.isArray() && javaTypeRegistry.findDescriptor( javaClass.getComponentType() ) != null ) {
final JavaType<T> resolvedJavaType = javaTypeRegistry.resolveDescriptor( javaClass );
final JdbcType recommendedJdbcType = resolvedJavaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
final JdbcType recommendedJdbcType =
resolvedJavaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
if ( recommendedJdbcType != null ) {
return getTypeConfiguration().getBasicTypeRegistry().resolve(
resolvedJavaType,
recommendedJdbcType
);
return getTypeConfiguration().getBasicTypeRegistry().resolve( resolvedJavaType, recommendedJdbcType );
}
}

View File

@ -22,6 +22,7 @@ import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
@ -32,6 +33,7 @@ import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.criteria.Expression;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
/**
* @author Steve Ebersole
@ -65,22 +67,24 @@ public class FullyQualifiedReflectivePathTerminal
@SuppressWarnings("unchecked")
private Function<SemanticQueryWalker, ?> resolveTerminalSemantic() {
return semanticQueryWalker -> {
final ClassLoaderService cls = creationState.getCreationContext().getServiceRegistry().getService( ClassLoaderService.class );
final SqmCreationContext creationContext = creationState.getCreationContext();
final ClassLoaderService cls =
creationContext.getServiceRegistry().requireService( ClassLoaderService.class );
final String fullPath = getFullPath();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// See if it is an entity-type literal
final EntityDomainType<?> entityDescriptor = creationState.getCreationContext().getJpaMetamodel().entity( fullPath );
final EntityDomainType<?> entityDescriptor = creationContext.getJpaMetamodel().entity( fullPath );
if ( entityDescriptor != null ) {
return new SqmLiteralEntityType( entityDescriptor, creationState.getCreationContext().getNodeBuilder() );
return new SqmLiteralEntityType<>( entityDescriptor, creationContext.getNodeBuilder() );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// See if it is a Class FQN
try {
final Class namedClass = cls.classForName( fullPath );
final Class<?> namedClass = cls.classForName( fullPath );
if ( namedClass != null ) {
return semanticQueryWalker.visitFullyQualifiedClass( namedClass );
}
@ -88,46 +92,47 @@ public class FullyQualifiedReflectivePathTerminal
catch (ClassLoadingException ignore) {
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check the parent path as a Class FQN, meaning the terminal is a field or
// enum-value
final String parentFullPath = getParent().getFullPath();
try {
final Class namedClass = cls.classForName( parentFullPath );
final Class<?> namedClass = cls.classForName( parentFullPath );
if ( namedClass != null ) {
if ( namedClass.isEnum() ) {
return new SqmEnumLiteral(
Enum.valueOf( namedClass, getLocalName() ),
(EnumJavaType) creationState.getCreationContext().getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor(
namedClass,
() -> new EnumJavaType( namedClass )
),
getLocalName(),
nodeBuilder()
);
}
else {
final Field field = namedClass.getField( getLocalName() );
return new SqmFieldLiteral(
field,
creationState.getCreationContext().getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor(
namedClass,
() -> new EnumJavaType( namedClass )
),
nodeBuilder()
);
}
return createEnumOrFieldLiteral( namedClass );
}
}
catch (ClassLoadingException | NoSuchFieldException ignore) {
}
throw new HqlInterpretationException( "Unsure how to handle semantic path terminal - " + fullPath );
};
}
@SuppressWarnings({"rawtypes", "unchecked"})
private SqmExpression createEnumOrFieldLiteral(Class namedClass) throws NoSuchFieldException {
if ( namedClass.isEnum() ) {
return new SqmEnumLiteral(
Enum.valueOf( namedClass, getLocalName() ),
(EnumJavaType) javaTypeRegistry()
.resolveDescriptor( namedClass, () -> new EnumJavaType( namedClass ) ),
getLocalName(),
nodeBuilder()
);
}
else {
return new SqmFieldLiteral(
namedClass.getField( getLocalName() ),
javaTypeRegistry()
.resolveDescriptor( namedClass, () -> new EnumJavaType( namedClass ) ),
nodeBuilder()
);
}
}
private JavaTypeRegistry javaTypeRegistry() {
return creationState.getCreationContext().getTypeConfiguration().getJavaTypeRegistry();
}
@Override

View File

@ -3094,7 +3094,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final int size = ctx.simpleCaseWhen().size();
final SqmCaseSimple caseExpression = new SqmCaseSimple<>(
(SqmExpression<?>) ctx.expressionOrPredicate().accept( this ),
null,
size,
creationContext.getNodeBuilder()
);
@ -3118,11 +3117,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public SqmCaseSearched<?> visitSearchedCaseList(HqlParser.SearchedCaseListContext ctx) {
final int size = ctx.searchedCaseWhen().size();
final SqmCaseSearched caseExpression = new SqmCaseSearched<>(
null,
size,
creationContext.getNodeBuilder()
);
final SqmCaseSearched caseExpression = new SqmCaseSearched<>( size, creationContext.getNodeBuilder() );
for ( int i = 0; i < size; i++ ) {
final HqlParser.SearchedCaseWhenContext searchedCaseWhenContext = ctx.searchedCaseWhen( i );
@ -3697,22 +3692,23 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final String originalText = text;
text = text.substring( 2 );
try {
final Number value;
final BasicDomainType<? extends Number> type;
if ( text.endsWith( "l" ) || text.endsWith( "L" ) ) {
text = text.substring( 0, text.length() - 1 );
value = Long.parseUnsignedLong( text, 16 );
type = resolveExpressibleTypeBasic( Long.class );
final long value = Long.parseUnsignedLong( text, 16 );
return new SqmLiteral<>(
value,
resolveExpressibleTypeBasic( Long.class ),
creationContext.getNodeBuilder()
);
}
else {
value = Integer.parseUnsignedInt( text, 16 );
type = resolveExpressibleTypeBasic( Integer.class );
final int value = Integer.parseUnsignedInt( text, 16 );
return new SqmLiteral<>(
value,
resolveExpressibleTypeBasic( Integer.class ),
creationContext.getNodeBuilder()
);
}
return new SqmLiteral<>(
value,
type,
creationContext.getNodeBuilder()
);
}
catch (NumberFormatException e) {
throw new LiteralNumberFormatException(
@ -4113,7 +4109,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
// The SQL standard says the default is three periods `...`
fillerExpression = new SqmLiteral<>(
"...",
secondArgument.getNodeType(),
resolveExpressibleTypeBasic( String.class ),
secondArgument.nodeBuilder()
);
}
@ -4130,47 +4126,31 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
public List<SqmTypedNode<?>> visitGenericFunctionArguments(HqlParser.GenericFunctionArgumentsContext ctx) {
final int size = ctx.getChildCount();
final int lastIndex = size - 1;
// Shift 1 bit instead of division by 2
final int estimateArgumentCount = size >> 1;
final List<SqmTypedNode<?>> arguments = new ArrayList<>( estimateArgumentCount );
int i = 0;
boolean distinct = false;
final ParseTree firstChild = ctx.getChild( 0 );
if ( firstChild instanceof HqlParser.DatetimeFieldContext ) {
arguments.add( toDurationUnit( (SqmExtractUnit<?>) firstChild.accept( this ) ) );
i += 2;
final List<HqlParser.ExpressionOrPredicateContext> argumentContexts = ctx.expressionOrPredicate();
int count = argumentContexts.size();
final List<SqmTypedNode<?>> arguments = new ArrayList<>( count+1 );
final HqlParser.DatetimeFieldContext datetimeFieldContext = ctx.datetimeField();
if ( datetimeFieldContext != null ) {
arguments.add( toDurationUnit( (SqmExtractUnit<?>) datetimeFieldContext.accept( this ) ) );
}
else if ( firstChild instanceof TerminalNode ) {
distinct = true;
i++;
for ( int i = 0; i < count-1; i++ ) {
final HqlParser.ExpressionOrPredicateContext argumentContext = argumentContexts.get(i);
arguments.add( (SqmTypedNode<?>) argumentContext.accept( this ) );
}
// we handle the last argument differently...
final HqlParser.ExpressionOrPredicateContext argumentContext = argumentContexts.get( count-1 );
arguments.add( visitFinalFunctionArgument( argumentContext ) );
for ( ; i < size; i += 2 ) {
// we handle the final argument differently...
if ( i == lastIndex ) {
arguments.add( visitFinalFunctionArgument( ctx.getChild( i ) ) );
}
else {
arguments.add( (SqmTypedNode<?>) ctx.getChild( i ).accept( this ) );
}
}
if ( distinct ) {
if ( ctx.DISTINCT() != null ) {
final NodeBuilder nodeBuilder = getCreationContext().getNodeBuilder();
if ( arguments.size() == 1 ) {
arguments.set( 0, new SqmDistinct<>( (SqmExpression<?>) arguments.get( 0 ), nodeBuilder ) );
}
else {
final List<SqmTypedNode<?>> newArguments = new ArrayList<>( 1 );
//noinspection unchecked
newArguments.add(
new SqmDistinct<>(
new SqmTuple<>( (List<SqmExpression<?>>) (List<?>) arguments, nodeBuilder ), nodeBuilder
)
);
@SuppressWarnings("unchecked")
final List<SqmExpression<?>> expressions = (List<SqmExpression<?>>) (List<?>) arguments;
newArguments.add( new SqmDistinct<>( new SqmTuple<>( expressions, nodeBuilder ), nodeBuilder ) );
return newArguments;
}
}

View File

@ -23,7 +23,6 @@ import org.hibernate.query.spi.SimpleHqlInterpretationImpl;
import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.jboss.logging.Logger;

View File

@ -27,7 +27,7 @@ import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType;
/**
* The standard Hibernate QueryParameterBinding implementation
* The standard implementation of {@link QueryParameterBinding}.
*
* @author Steve Ebersole
*/
@ -38,11 +38,11 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
private boolean isBound;
private boolean isMultiValued;
private BindableType<? extends T> bindType;
private BindableType<? super T> bindType;
private MappingModelExpressible<T> type;
private TemporalType explicitTemporalPrecision;
private T bindValue;
private Object bindValue;
private Collection<? extends T> bindValues;
/**
@ -67,7 +67,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
}
@Override
public BindableType<? extends T> getBindType() {
public BindableType<? super T> getBindType() {
return bindType;
}
@ -96,7 +96,8 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
throw new IllegalStateException( "Binding is multi-valued; illegal call to #getBindValue" );
}
return bindValue;
//TODO: I believe this cast is unsound due to coercion
return (T) bindValue;
}
@Override
@ -105,13 +106,17 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
return;
}
final Object coerced;
if ( ! sessionFactory.getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled() ) {
try {
if ( bindType != null ) {
value = coerce( value, bindType );
coerced = coerce( value, bindType );
}
else if ( queryParameter.getHibernateType() != null ) {
value = coerce( value, queryParameter.getHibernateType() );
coerced = coerce( value, queryParameter.getHibernateType() );
}
else {
coerced = value;
}
}
catch (CoercionException ce) {
@ -125,57 +130,64 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
);
}
}
validate( value );
if ( resolveJdbcTypeIfNecessary && bindType == null && value == null ) {
bindType = getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( "null" );
else {
coerced = value;
}
validate( coerced );
if ( value == null ) {
// needed when setting a null value to the parameter of a native SQL query
// TODO: this does not look like a very disciplined way to handle this
bindNull( resolveJdbcTypeIfNecessary );
}
else {
bindValue( coerced );
}
bindValue( value );
}
private T coerce(T value, BindableType<? extends T> parameterType) {
private void bindNull(boolean resolveJdbcTypeIfNecessary) {
isBound = true;
bindValue = null;
if ( resolveJdbcTypeIfNecessary && bindType == null ) {
bindType = getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( "null" );
}
}
private Object coerce(T value, BindableType<? super T> parameterType) {
if ( value == null ) {
return null;
}
final SqmExpressible<? extends T> sqmExpressible = parameterType.resolveExpressible( sessionFactory );
final SqmExpressible<? super T> sqmExpressible = parameterType.resolveExpressible( sessionFactory );
assert sqmExpressible != null;
return sqmExpressible.getExpressibleJavaType().coerce( value, this );
}
private boolean handleAsMultiValue(T value, BindableType<T> bindableType) {
if ( ! queryParameter.allowsMultiValuedBinding() ) {
return false;
}
if ( value == null ) {
return false;
}
if ( value instanceof Collection
&& ( bindableType != null && !bindableType.getBindableJavaType().isInstance( value )
|| ( bindableType == null && !isRegisteredAsBasicType( value.getClass() ) ) ) ) {
if ( queryParameter.allowsMultiValuedBinding()
&& value instanceof Collection
&& ( bindableType == null
? !isRegisteredAsBasicType( value.getClass() )
: !bindableType.getBindableJavaType().isInstance( value ) ) ) {
//noinspection unchecked
setBindValues( (Collection<T>) value );
return true;
}
return false;
else {
return false;
}
}
private boolean isRegisteredAsBasicType(Class<?> valueClass) {
return getTypeConfiguration().getBasicTypeForJavaType( valueClass ) != null;
}
private void bindValue(T value) {
this.isBound = true;
this.bindValue = value;
if ( bindType == null ) {
if ( value != null ) {
this.bindType = sessionFactory.getMappingMetamodel().resolveParameterBindType( value );
}
private void bindValue(Object value) {
isBound = true;
bindValue = value;
if ( bindType == null && value != null ) {
bindType = sessionFactory.getMappingMetamodel().resolveParameterBindType( value );
}
}
@ -186,19 +198,23 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
}
if ( clarifiedType != null ) {
this.bindType = clarifiedType;
bindType = clarifiedType;
}
final Object coerced;
if ( bindType != null ) {
value = coerce( value, bindType );
coerced = coerce( value, bindType );
}
else if ( queryParameter.getHibernateType() != null ) {
value = coerce( value, queryParameter.getHibernateType() );
coerced = coerce( value, queryParameter.getHibernateType() );
}
else {
coerced = value;
}
validate( value, clarifiedType );
validate( coerced, clarifiedType );
bindValue( value );
bindValue( coerced );
}
@Override
@ -211,10 +227,11 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
bindType = queryParameter.getHibernateType();
}
final Object coerced;
if ( ! sessionFactory.getSessionFactoryOptions().getJpaCompliance().isLoadByIdComplianceEnabled() ) {
if ( bindType != null ) {
try {
value = coerce( value, bindType );
coerced = coerce( value, bindType );
}
catch (CoercionException ex) {
throw new IllegalArgumentException(
@ -229,13 +246,19 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
}
}
else if ( queryParameter.getHibernateType() != null ) {
value = coerce( value, queryParameter.getHibernateType() );
coerced = coerce( value, queryParameter.getHibernateType() );
}
else {
coerced = value;
}
}
else {
coerced = value;
}
validate( value, temporalTypePrecision );
validate( coerced, temporalTypePrecision );
bindValue( value );
bindValue( coerced );
setExplicitTemporalPrecision( temporalTypePrecision );
}
@ -289,22 +312,16 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
setExplicitTemporalPrecision( temporalTypePrecision );
}
private void setExplicitTemporalPrecision(TemporalType temporalTypePrecision) {
private void setExplicitTemporalPrecision(TemporalType precision) {
explicitTemporalPrecision = precision;
if ( bindType == null || JavaTypeHelper.isTemporal( determineJavaType( bindType ) ) ) {
this.bindType = BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
temporalTypePrecision,
bindType,
sessionFactory
);
bindType = BindingTypeHelper.INSTANCE.resolveTemporalPrecision( precision, bindType, sessionFactory );
}
this.explicitTemporalPrecision = temporalTypePrecision;
}
private JavaType<? extends T> determineJavaType(BindableType<? extends T> bindType) {
final SqmExpressible<? extends T> sqmExpressible = bindType.resolveExpressible( sessionFactory );
private JavaType<? super T> determineJavaType(BindableType<? super T> bindType) {
final SqmExpressible<? super T> sqmExpressible = bindType.resolveExpressible( sessionFactory );
assert sqmExpressible != null;
return sqmExpressible.getExpressibleJavaType();
}
@ -320,14 +337,14 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
if ( bindType == null || bindType.getBindableJavaType() == Object.class || type instanceof ModelPart ) {
if ( type instanceof BindableType<?> ) {
final boolean changed = bindType != null && type != bindType;
this.bindType = (BindableType<T>) type;
bindType = (BindableType<T>) type;
return changed;
}
else if ( type instanceof BasicValuedMapping ) {
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
if ( jdbcMapping instanceof BindableType<?> ) {
final boolean changed = bindType != null && jdbcMapping != bindType;
this.bindType = (BindableType<T>) jdbcMapping;
bindType = (BindableType<T>) jdbcMapping;
return changed;
}
}
@ -335,15 +352,15 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
return false;
}
private void validate(T value) {
private void validate(Object value) {
QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, sessionFactory );
}
private void validate(T value, BindableType<?> clarifiedType) {
private void validate(Object value, BindableType<?> clarifiedType) {
QueryParameterBindingValidator.INSTANCE.validate( clarifiedType, value, sessionFactory );
}
private void validate(T value, TemporalType clarifiedTemporalType) {
private void validate(Object value, TemporalType clarifiedTemporalType) {
QueryParameterBindingValidator.INSTANCE.validate( getBindType(), value, clarifiedTemporalType, sessionFactory );
}

View File

@ -237,7 +237,7 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
if ( binding.isMultiValued() ) {
final Iterator<?> iterator = binding.getBindValues().iterator();
Object firstNonNullBindValue = null;
if ( iterator.hasNext() && firstNonNullBindValue == null ) {
if ( iterator.hasNext() ) {
firstNonNullBindValue = iterator.next();
}
if ( firstNonNullBindValue != null ) {

View File

@ -17,7 +17,7 @@ import org.hibernate.type.spi.TypeConfiguration;
/**
/**
* The value/type binding information for a particular query parameter. Supports
* both single-valued and multi-valued binds
* both single-valued and multivalued binds
*
* @author Steve Ebersole
*/
@ -30,7 +30,7 @@ public interface QueryParameterBinding<T> {
boolean isBound();
/**
* Is the binding multi-valued?
* Is the binding multivalued?
*/
boolean isMultiValued();
@ -39,7 +39,7 @@ public interface QueryParameterBinding<T> {
*
* @return The currently associated Type
*/
BindableType<? extends T> getBindType();
BindableType<? super T> getBindType();
/**
* If the parameter represents a temporal type, return the explicitly

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.spi;
import org.hibernate.Incubating;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.query.BindableType;
import org.hibernate.type.spi.TypeConfiguration;
@ -20,8 +21,9 @@ import org.hibernate.type.spi.TypeConfiguration;
*
* @author Steve Ebersole
*/
@Incubating
public interface QueryParameterBindingTypeResolver {
<T> BindableType<? extends T> resolveParameterBindType(T bindValue);
<T> BindableType<? super T> resolveParameterBindType(T bindValue);
<T> BindableType<T> resolveParameterBindType(Class<T> clazz);
TypeConfiguration getTypeConfiguration();
MappingMetamodel getMappingMetamodel();

View File

@ -118,12 +118,12 @@ public interface SqmPathSource<J> extends SqmExpressible<J>, Bindable<J>, SqmExp
@Override
default SqmExpressible<J> getExpressible() {
return (SqmExpressible<J>) getSqmPathType();
return getSqmPathType();
}
@Override
default DomainType<J> getSqmType() {
return (DomainType<J>) getSqmPathType();
return getSqmPathType();
}
/**

View File

@ -9,10 +9,9 @@ package org.hibernate.query.sqm.internal;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
/**
* todo (6.0) : how is this different from {@link org.hibernate.query.sqm.tree.jpa.ParameterCollector}?
*
* @author Steve Ebersole
*/
// todo (6.0) : how is this different from org.hibernate.query.sqm.tree.jpa.ParameterCollector?
public interface ParameterCollector {
void addParameter(SqmParameter<?> parameter);
}

View File

@ -243,8 +243,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public BasicType<Boolean> getBooleanType() {
final BasicType<Boolean> booleanType = this.booleanType;
if ( booleanType == null ) {
return this.booleanType = getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.BOOLEAN );
return this.booleanType =
getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.BOOLEAN );
}
return booleanType;
}
@ -253,7 +254,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public BasicType<Integer> getIntegerType() {
final BasicType<Integer> integerType = this.integerType;
if ( integerType == null ) {
return this.integerType = getTypeConfiguration().getBasicTypeForJavaType( Integer.class );
return this.integerType =
getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.INTEGER );
}
return integerType;
}
@ -262,7 +265,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public BasicType<Character> getCharacterType() {
final BasicType<Character> characterType = this.characterType;
if ( characterType == null ) {
return this.characterType = getTypeConfiguration().getBasicTypeForJavaType( Character.class );
return this.characterType =
getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.CHARACTER );
}
return characterType;
}
@ -297,7 +302,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
sessionFactory.get().getQueryEngine().getHqlTranslator()
.translate( hql, resultClass );
if ( statement instanceof SqmSelectStatement ) {
return new SqmSelectStatement<>((SqmSelectStatement<T>) statement);
return new SqmSelectStatement<>( (SqmSelectStatement<T>) statement );
}
else {
throw new IllegalArgumentException("Not a 'select' statement");
@ -362,13 +367,13 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@SuppressWarnings("unchecked")
private <T> JpaCriteriaQuery<T> setOperation(
SetOperator operator,
CriteriaQuery<? extends T> query1,
CriteriaQuery<? extends T> criteriaQuery,
CriteriaQuery<?>... queries) {
final Class<T> resultType = (Class<T>) query1.getResultType();
final Class<T> resultType = (Class<T>) criteriaQuery.getResultType();
final List<SqmQueryPart<T>> queryParts = new ArrayList<>( queries.length + 1 );
final Map<String, SqmCteStatement<?>> cteStatements = new LinkedHashMap<>();
final SqmSelectStatement<T> selectStatement1 = (SqmSelectStatement<T>) query1;
collectQueryPartsAndCtes( selectStatement1, queryParts, cteStatements );
final SqmSelectStatement<T> selectStatement = (SqmSelectStatement<T>) criteriaQuery;
collectQueryPartsAndCtes( selectStatement, queryParts, cteStatements );
for ( CriteriaQuery<?> query : queries ) {
if ( query.getResultType() != resultType ) {
throw new IllegalArgumentException( "Result type of all operands must match" );
@ -379,7 +384,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
new SqmQueryGroup<>( this, operator, queryParts ),
resultType,
cteStatements,
selectStatement1.getQuerySource(),
selectStatement.getQuerySource(),
this
);
}
@ -387,13 +392,13 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@SuppressWarnings("unchecked")
private <T> JpaSubQuery<T> setOperation(
SetOperator operator,
Subquery<? extends T> query1,
Subquery<? extends T> subquery,
Subquery<?>... queries) {
final Class<T> resultType = (Class<T>) query1.getResultType();
final SqmQuery<T> parent = (SqmQuery<T>) query1.getParent();
final Class<T> resultType = (Class<T>) subquery.getResultType();
final SqmQuery<T> parent = (SqmQuery<T>) subquery.getParent();
final List<SqmQueryPart<T>> queryParts = new ArrayList<>( queries.length + 1 );
final Map<String, SqmCteStatement<?>> cteStatements = new LinkedHashMap<>();
collectQueryPartsAndCtes( (SqmSelectQuery<T>) query1, queryParts, cteStatements );
collectQueryPartsAndCtes( (SqmSelectQuery<T>) subquery, queryParts, cteStatements );
for ( Subquery<?> query : queries ) {
if ( query.getResultType() != resultType ) {
throw new IllegalArgumentException( "Result type of all operands must match" );
@ -440,11 +445,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate wrap(Expression<Boolean> expression) {
if ( expression instanceof SqmPredicate ) {
return (SqmPredicate) expression;
}
return new SqmBooleanExpressionPredicate( (SqmExpression<Boolean>) expression, this );
return expression instanceof SqmPredicate
? (SqmPredicate) expression
: new SqmBooleanExpressionPredicate( (SqmExpression<Boolean>) expression, this );
}
@Override
@ -582,7 +585,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public JpaSearchOrder asc(JpaCteCriteriaAttribute x, boolean nullsFirst) {
return new SqmSearchClauseSpecification(
(SqmCteTableColumn) x,
(SqmCteTableColumn) x,
SortDirection.ASCENDING,
nullsFirst ? NullPrecedence.FIRST : NullPrecedence.LAST
);
@ -619,20 +622,13 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return tuple( tupleType, asList( expressions ) );
}
@Override
@Override @SuppressWarnings("unchecked")
public <R> SqmTuple<R> tuple(Class<R> tupleType, List<? extends SqmExpression<?>> expressions) {
final TypeConfiguration typeConfiguration = getTypeConfiguration();
@SuppressWarnings("unchecked")
final List<SqmExpression<?>> sqmExpressions = (List<SqmExpression<?>>) expressions;
final SqmExpressible<R> expressibleType;
if ( tupleType == null || tupleType == Object[].class ) {
//noinspection unchecked
expressibleType = (DomainType<R>) typeConfiguration.resolveTupleType( sqmExpressions );
}
else {
expressibleType = getDomainModel().embeddable( tupleType );
}
return tuple( expressibleType, sqmExpressions );
final SqmExpressible<R> expressibleType =
tupleType == null || tupleType == Object[].class
? (DomainType<R>) getTypeConfiguration().resolveTupleType( expressions )
: getDomainModel().embeddable( tupleType );
return tuple( expressibleType, expressions );
}
@Override
@ -646,11 +642,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
//noinspection unchecked
tupleType = (DomainType<R>) getTypeConfiguration().resolveTupleType( sqmExpressions );
}
return new SqmTuple<>(
new ArrayList<>( sqmExpressions ),
tupleType,
this
);
return new SqmTuple<>( new ArrayList<>( sqmExpressions ), tupleType, this );
}
@Override
@ -673,12 +665,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <Y> JpaCompoundSelection<Y> array(Class<Y> resultClass, List<? extends JpaSelection<?>> selections) {
checkMultiselect( selections );
final JavaType<Y> javaType = getTypeConfiguration().getJavaTypeRegistry().getDescriptor( resultClass );
//noinspection unchecked
return new SqmJpaCompoundSelection<>(
(List<SqmSelectableNode<?>>) selections,
getTypeConfiguration().getJavaTypeRegistry().getDescriptor( resultClass ),
this
);
return new SqmJpaCompoundSelection<>( (List<SqmSelectableNode<?>>) selections, javaType, this );
}
@Override
@ -704,12 +693,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
for ( Selection<?> argument : arguments ) {
final SqmSelectableNode<?> arg = (SqmSelectableNode<?>) argument;
instantiation.addArgument(
new SqmDynamicInstantiationArgument<>(
(SqmSelectableNode<?>) argument,
argument.getAlias(),
this
)
new SqmDynamicInstantiationArgument<>( arg, argument.getAlias(), this )
);
}
@ -1135,18 +1121,14 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return new SqmLiteralNull<>( this );
}
SqmExpressible<T> expressible = resolveInferredType( value, typeInferenceSource, getTypeConfiguration() );
final SqmExpressible<T> expressible = resolveInferredType( value, typeInferenceSource, getTypeConfiguration() );
if ( expressible.getExpressibleJavaType().isInstance( value ) ) {
return new SqmLiteral<>( value, expressible, this );
}
// Just like in HQL, we allow coercion of literal values to the inferred type
T coercedValue = expressible.getExpressibleJavaType().coerce( value, this::getTypeConfiguration );
if (expressible.getExpressibleJavaType().isInstance( coercedValue )) {
return new SqmLiteral<>(
coercedValue,
expressible,
this
);
final T coercedValue = expressible.getExpressibleJavaType().coerce( value, this::getTypeConfiguration );
if ( expressible.getExpressibleJavaType().isInstance( coercedValue ) ) {
return new SqmLiteral<>( coercedValue, expressible, this );
}
else {
// ignore typeInferenceSource and fallback the value type
@ -1188,13 +1170,12 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
return new SqmLiteralNull<>( this );
}
final BindableType<? extends T> valueParamType = getMappingMetamodel().resolveParameterBindType( value );
final SqmExpressible<? extends T> sqmExpressible = valueParamType == null
? null
: valueParamType.resolveExpressible( getSessionFactory() );
return new SqmLiteral<>( value, sqmExpressible, this );
else {
final BindableType<? super T> valueParamType = getParameterBindType( value );
final SqmExpressible<? super T> sqmExpressible =
valueParamType == null ? null : valueParamType.resolveExpressible( getSessionFactory() );
return new SqmLiteral<>( value, sqmExpressible, this );
}
}
private MappingMetamodelImplementor getMappingMetamodel() {
@ -1233,12 +1214,15 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
// No basic types are registered for enum java types, we have to use an untyped null literal in this case
return new SqmLiteralNull<>( this );
}
final BasicType<T> basicTypeForJavaType = getTypeConfiguration().getBasicTypeForJavaType( resultClass );
// if there's no basic type, it might be an entity type
final SqmExpressible<T> sqmExpressible = basicTypeForJavaType == null
? getDomainModel().managedType( resultClass )
: basicTypeForJavaType;
return new SqmLiteralNull<>( sqmExpressible, this );
else {
final BasicType<T> basicTypeForJavaType = getTypeConfiguration().getBasicTypeForJavaType( resultClass );
// if there's no basic type, it might be an entity type
final SqmExpressible<T> sqmExpressible =
basicTypeForJavaType == null
? getDomainModel().managedType( resultClass )
: basicTypeForJavaType;
return new SqmLiteralNull<>( sqmExpressible, this );
}
}
class MultiValueParameterType<T> implements SqmExpressible<T> {
@ -1587,10 +1571,18 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <T> SqmFunction<T> function(String name, Class<T> type, Expression<?>[] args) {
SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( name );
final BasicType<T> resultType = getTypeConfiguration().standardBasicTypeForJavaType( type );
return getFunctionTemplate( name, resultType ).generateSqmExpression(
expressionList( args ),
resultType,
getQueryEngine()
);
}
private <T> SqmFunctionDescriptor getFunctionTemplate(String name, BasicType<T> resultType) {
final SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( name );
if ( functionTemplate == null ) {
functionTemplate = new NamedSqmFunctionDescriptor(
return new NamedSqmFunctionDescriptor(
name,
true,
null,
@ -1598,12 +1590,9 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
null
);
}
return functionTemplate.generateSqmExpression(
expressionList( args ),
resultType,
getQueryEngine()
);
else {
return functionTemplate;
}
}
private static List<SqmExpression<?>> expressionList(Expression<?>[] jpaExpressions) {
@ -1659,97 +1648,69 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
* Creates an expression for the value with the given "type inference" information
*/
public <T> SqmExpression<T> value(T value, SqmExpression<? extends T> typeInferenceSource) {
if ( value instanceof SqmExpression ) {
return (SqmExpression<T>) value;
}
if ( inlineValue( value ) ) {
return literal( value, typeInferenceSource );
}
final BindableType<T> bindableType = resolveInferredParameterType(
value,
typeInferenceSource,
getTypeConfiguration()
);
if ( bindableType == null || isInstance( bindableType, value ) ) {
return new ValueBindJpaCriteriaParameter<>(
bindableType,
value,
this
);
}
final SqmExpressible<T> expressible = bindableType.resolveExpressible( getSessionFactory() );
T coercedValue = expressible.getExpressibleJavaType().coerce( value, this::getTypeConfiguration );
if ( isInstance( bindableType, coercedValue ) ) {
return new ValueBindJpaCriteriaParameter<>(
bindableType,
coercedValue,
this
);
}
else {
// ignore typeInferenceSource and fallback the value type
return new ValueBindJpaCriteriaParameter<>(
getMappingMetamodel().resolveParameterBindType( value ),
value,
this
);
}
}
private <T> boolean isInstance(BindableType<T> bindableType, T value) {
if ( bindableType instanceof SqmExpressible<?> ) {
return ( (SqmExpressible<T>) bindableType ).getExpressibleJavaType().isInstance( value );
}
if ( bindableType.getBindableJavaType().isInstance( value ) ) {
return true;
}
return bindableType.resolveExpressible( getSessionFactory() )
.getExpressibleJavaType()
.isInstance( value );
}
private static <T> BindableType<T> resolveInferredParameterType(
T value,
SqmExpression<? extends T> typeInferenceSource,
TypeConfiguration typeConfiguration) {
if ( typeInferenceSource != null ) {
if ( typeInferenceSource instanceof BindableType ) {
//noinspection unchecked
return (BindableType<T>) typeInferenceSource;
}
if ( typeInferenceSource.getNodeType() != null ) {
//noinspection unchecked
return (BindableType<T>) typeInferenceSource.getNodeType();
}
}
if ( value == null ) {
return null;
}
//noinspection unchecked
return (BasicType<T>) typeConfiguration.getBasicTypeForJavaType( value.getClass() );
return inlineValue( value ) ? literal( value, typeInferenceSource ) : valueParameter( value, typeInferenceSource );
}
@Override
public <T> SqmExpression<T> value(T value) {
if ( value instanceof SqmExpression ) {
return (SqmExpression<T>) value;
}
if ( inlineValue( value ) ) {
return literal( value );
return inlineValue( value ) ? literal( value ) : valueParameter( value );
}
private <T> boolean isInstance(BindableType<T> bindableType, T value) {
if ( bindableType instanceof SqmExpressible<?> ) {
final SqmExpressible<?> expressible = (SqmExpressible<?>) bindableType;
return expressible.getExpressibleJavaType().isInstance( value );
}
else {
return new ValueBindJpaCriteriaParameter<>(
getMappingMetamodel().resolveParameterBindType( value ),
value,
this
);
return bindableType.getBindableJavaType().isInstance( value )
|| bindableType.resolveExpressible( getSessionFactory() ).getExpressibleJavaType().isInstance( value );
}
}
@SuppressWarnings("unchecked")
private static <T> BindableType<T> resolveInferredParameterType(
T value,
SqmExpression<? extends T> typeInferenceSource,
TypeConfiguration typeConfiguration) {
if ( typeInferenceSource != null ) {
if ( typeInferenceSource instanceof BindableType ) {
return (BindableType<T>) typeInferenceSource;
}
if ( typeInferenceSource.getNodeType() != null ) {
return (BindableType<T>) typeInferenceSource.getNodeType();
}
}
return value == null ? null : (BasicType<T>) typeConfiguration.getBasicTypeForJavaType( value.getClass() );
}
private <T> ValueBindJpaCriteriaParameter<T> valueParameter(T value, SqmExpression<? extends T> typeInferenceSource) {
final BindableType<T> bindableType =
resolveInferredParameterType( value, typeInferenceSource, getTypeConfiguration() );
if ( bindableType == null || isInstance( bindableType, value) ) {
return new ValueBindJpaCriteriaParameter<>( bindableType, value, this );
}
final T coercedValue =
bindableType.resolveExpressible( getSessionFactory() ).getExpressibleJavaType()
.coerce(value, this::getTypeConfiguration );
if ( isInstance( bindableType, coercedValue ) ) {
return new ValueBindJpaCriteriaParameter<>( bindableType, coercedValue, this );
}
else {
// ignore typeInferenceSource and fall back the value type
return new ValueBindJpaCriteriaParameter<>( getParameterBindType( value ), value, this );
}
}
private <T> ValueBindJpaCriteriaParameter<T> valueParameter(T value) {
return new ValueBindJpaCriteriaParameter<>( getParameterBindType( value ), value, this );
}
private <T> BindableType<? super T> getParameterBindType(T value) {
return getMappingMetamodel().resolveParameterBindType( value );
}
private <T> boolean inlineValue(T value) {
return criteriaValueHandlingMode == ValueHandlingMode.INLINE;
// || is a literal enum mapped to a PostgreSQL named 'enum' type
@ -1772,11 +1733,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <C extends Collection<?>> SqmExpression<Integer> size(C collection) {
return new SqmLiteral<>(
collection.size(),
getIntegerType(),
this
);
return new SqmLiteral<>( collection.size(), getIntegerType(), this );
}
@Override
@ -1791,13 +1748,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
( (SqmExpression<? extends Y>) x ).getExpressible(),
( (SqmExpression<? extends Y>) y ).getExpressible()
);
return new SqmCoalesce<>(
sqmExpressible,
2,
this
)
.value(x)
.value(y);
return new SqmCoalesce<>( sqmExpressible, 2, this ).value(x).value(y);
}
@Override
@ -1817,15 +1768,14 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
}
private <Y> SqmExpression<Y> createNullifFunctionNode(SqmExpression<Y> first, SqmExpression<Y> second) {
//noinspection unchecked
final ReturnableType<Y> type = (ReturnableType<Y>) highestPrecedenceType(
first.getExpressible(),
second.getExpressible()
).getSqmType();
final DomainType<? extends Y> type =
highestPrecedenceType( first.getExpressible(), second.getExpressible() )
.getSqmType();
@SuppressWarnings("unchecked")
final ReturnableType<Y> resultType = (ReturnableType<Y>) type;
return getFunctionDescriptor("nullif").generateSqmExpression(
asList( first, second ),
type,
resultType,
getQueryEngine()
);
}
@ -1910,8 +1860,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate not(Expression<Boolean> restriction) {
final SqmPredicate predicate = wrap( restriction );
return predicate.not();
return wrap( restriction ).not();
}
@Override

View File

@ -20,7 +20,6 @@ import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.expression.AbstractSqmExpression;
@ -175,8 +174,7 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
if ( subPathSource == null ) {
return new SqmLiteral<>(
referencedPathSource.getBindableJavaType(),
(SqmExpressible<? extends Class<? extends T>>) (SqmExpressible<?>)
nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Class.class ),
nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Class.class ),
nodeBuilder()
);
}

View File

@ -23,12 +23,16 @@ import org.hibernate.query.sqm.tree.expression.SqmExpression;
public class SqmFkExpression<T> extends AbstractSqmExpression<T> {
private final SqmEntityValuedSimplePath<?> toOnePath;
@SuppressWarnings("unchecked")
public SqmFkExpression(SqmEntityValuedSimplePath<?> toOnePath, NodeBuilder criteriaBuilder) {
//noinspection unchecked
super( (SqmExpressible<? extends T>) ( (IdentifiableDomainType<?>) toOnePath.getNodeType() ).getIdType(), criteriaBuilder );
super( (SqmExpressible<? super T>) pathDomainType( toOnePath ).getIdType(), criteriaBuilder );
this.toOnePath = toOnePath;
}
private static IdentifiableDomainType<?> pathDomainType(SqmEntityValuedSimplePath<?> toOnePath) {
return (IdentifiableDomainType<?>) toOnePath.getNodeType();
}
public SqmEntityValuedSimplePath<?> getToOnePath() {
return toOnePath;
}

View File

@ -30,7 +30,7 @@ import static org.hibernate.query.internal.QueryHelper.highestPrecedenceType2;
*/
public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> implements SqmExpression<T> {
public AbstractSqmExpression(SqmExpressible<? extends T> type, NodeBuilder criteriaBuilder) {
public AbstractSqmExpression(SqmExpressible<? super T> type, NodeBuilder criteriaBuilder) {
super( type, criteriaBuilder );
}
@ -41,16 +41,6 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
@Override
public void applyInferableType(SqmExpressible<?> type) {
// if ( type == null ) {
// return;
// }
//
// final SqmExpressible<?> oldType = getNodeType();
//
// final SqmExpressible<?> newType = highestPrecedenceType( oldType, type );
// if ( newType != null && newType != oldType ) {
// internalApplyInferableType( newType );
// }
}
protected void internalApplyInferableType(SqmExpressible<?> newType) {
@ -64,44 +54,48 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
setExpressibleType( highestPrecedenceType2( newType, getExpressible() ) );
}
private <B> SqmExpression<B> castToBasicType(Class<B> javaType) {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( javaType ) );
}
@Override
public SqmExpression<Long> asLong() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Long.class ) );
return castToBasicType( Long.class );
}
@Override
public SqmExpression<Integer> asInteger() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) );
return castToBasicType( Integer.class );
}
@Override
public SqmExpression<Float> asFloat() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Float.class ) );
return castToBasicType( Float.class );
}
@Override
public SqmExpression<Double> asDouble() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Double.class ) );
return castToBasicType( Double.class );
}
@Override
public SqmExpression<BigDecimal> asBigDecimal() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( BigDecimal.class ) );
return castToBasicType( BigDecimal.class );
}
@Override
public SqmExpression<BigInteger> asBigInteger() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( BigInteger.class ) );
return castToBasicType( BigInteger.class );
}
@Override
public SqmExpression<String> asString() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( String.class ) );
return castToBasicType( String.class );
}
@Override
public <X> SqmExpression<X> as(Class<X> type) {
return nodeBuilder().cast(this, type);
return nodeBuilder().cast( this, type );
}
@Override

View File

@ -8,7 +8,6 @@ package org.hibernate.query.sqm.tree.expression;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.BindableType;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmExpressible;
@ -30,13 +29,16 @@ public abstract class AbstractSqmParameter<T> extends AbstractSqmExpression<T> i
@Override
public void applyInferableType(SqmExpressible<?> type) {
if ( type == null ) {
return;
if ( type != null ) {
if ( type instanceof PluralPersistentAttribute ) {
final PluralPersistentAttribute<?, ?, ?> pluralPersistentAttribute =
(PluralPersistentAttribute<?, ?, ?>) type;
internalApplyInferableType( pluralPersistentAttribute.getElementType() );
}
else {
internalApplyInferableType( type );
}
}
else if ( type instanceof PluralPersistentAttribute<?, ?, ?> ) {
type = ( (PluralPersistentAttribute<?, ?, ?>) type ).getElementType();
}
internalApplyInferableType( type );
}
@Override

View File

@ -36,7 +36,7 @@ public class JpaCriteriaParameter<T>
public JpaCriteriaParameter(
String name,
BindableType<T> type,
BindableType<? super T> type,
boolean allowsMultiValuedBinding,
NodeBuilder nodeBuilder) {
super( toSqmType( type, nodeBuilder ), nodeBuilder );

View File

@ -32,7 +32,7 @@ public class SqmAliasedNodeRef extends AbstractSqmExpression<Integer> {
public SqmAliasedNodeRef(
int position,
NavigablePath navigablePath,
SqmExpressible<? extends Integer> type,
SqmExpressible<Integer> type,
NodeBuilder criteriaBuilder) {
super( type, criteriaBuilder );
this.position = position;

View File

@ -25,21 +25,24 @@ import jakarta.persistence.criteria.Expression;
public class SqmCaseSearched<R>
extends AbstractSqmExpression<R>
implements JpaSearchedCase<R> {
private final List<WhenFragment<R>> whenFragments;
private SqmExpression<R> otherwise;
private final List<WhenFragment<? extends R>> whenFragments;
private SqmExpression<? extends R> otherwise;
public SqmCaseSearched(NodeBuilder nodeBuilder) {
this( null, nodeBuilder );
}
public SqmCaseSearched(SqmExpressible<R> inherentType, NodeBuilder nodeBuilder) {
super( inherentType, nodeBuilder );
this.whenFragments = new ArrayList<>();
this( inherentType, 10, nodeBuilder );
}
public SqmCaseSearched(SqmExpressible<R> inherentType, int estimateWhenSize, NodeBuilder nodeBuilder) {
public SqmCaseSearched(int estimatedWhenSize, NodeBuilder nodeBuilder) {
this( null, estimatedWhenSize, nodeBuilder );
}
private SqmCaseSearched(SqmExpressible<R> inherentType, int estimatedWhenSize, NodeBuilder nodeBuilder) {
super( inherentType, nodeBuilder );
this.whenFragments = new ArrayList<>( estimateWhenSize );
this.whenFragments = new ArrayList<>( estimatedWhenSize );
}
@Override
@ -50,13 +53,9 @@ public class SqmCaseSearched<R>
}
final SqmCaseSearched<R> caseSearched = context.registerCopy(
this,
new SqmCaseSearched<>(
getNodeType(),
whenFragments.size(),
nodeBuilder()
)
new SqmCaseSearched<>( getNodeType(), whenFragments.size(), nodeBuilder() )
);
for ( WhenFragment<R> whenFragment : whenFragments ) {
for ( WhenFragment<? extends R> whenFragment : whenFragments ) {
caseSearched.whenFragments.add(
new WhenFragment<>(
whenFragment.predicate.copy( context ),
@ -71,36 +70,33 @@ public class SqmCaseSearched<R>
return caseSearched;
}
public List<WhenFragment<R>> getWhenFragments() {
public List<WhenFragment<? extends R>> getWhenFragments() {
return whenFragments;
}
public SqmExpression<R> getOtherwise() {
public SqmExpression<? extends R> getOtherwise() {
return otherwise;
}
public SqmCaseSearched<R> when(SqmPredicate predicate, SqmExpression<R> result) {
public SqmCaseSearched<R> when(SqmPredicate predicate, SqmExpression<? extends R> result) {
whenFragments.add( new WhenFragment<>( predicate, result ) );
applyInferableResultType( result.getNodeType() );
return this;
}
public SqmCaseSearched<R> otherwise(SqmExpression<R> otherwiseExpression) {
public SqmCaseSearched<R> otherwise(SqmExpression<? extends R> otherwiseExpression) {
this.otherwise = otherwiseExpression;
applyInferableResultType( otherwiseExpression.getNodeType() );
return this;
}
private void applyInferableResultType(SqmExpressible<?> type) {
if ( type == null ) {
return;
}
final SqmExpressible<?> oldType = getExpressible();
final SqmExpressible<?> newType = QueryHelper.highestPrecedenceType2( oldType, type );
if ( newType != null && newType != oldType ) {
internalApplyInferableType( newType );
if ( type != null ) {
final SqmExpressible<?> oldType = getExpressible();
final SqmExpressible<?> newType = QueryHelper.highestPrecedenceType2( oldType, type );
if ( newType != null && newType != oldType ) {
internalApplyInferableType( newType );
}
}
}
@ -113,9 +109,7 @@ public class SqmCaseSearched<R>
}
if ( whenFragments != null ) {
whenFragments.forEach(
whenFragment -> whenFragment.getResult().applyInferableType( newType )
);
whenFragments.forEach( whenFragment -> whenFragment.getResult().applyInferableType( newType ) );
}
}
@ -150,7 +144,7 @@ public class SqmCaseSearched<R>
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "case" );
for ( WhenFragment<R> whenFragment : whenFragments ) {
for ( WhenFragment<? extends R> whenFragment : whenFragments ) {
sb.append( " when " );
whenFragment.predicate.appendHqlString( sb );
sb.append( " then " );
@ -176,8 +170,7 @@ public class SqmCaseSearched<R>
@Override
public SqmCaseSearched<R> when(Expression<Boolean> condition, Expression<? extends R> result) {
//noinspection unchecked
when( nodeBuilder().wrap( condition ), (SqmExpression) result );
when( nodeBuilder().wrap( condition ), (SqmExpression<? extends R>) result );
return this;
}
@ -189,8 +182,7 @@ public class SqmCaseSearched<R>
@Override
public SqmExpression<R> otherwise(Expression<? extends R> result) {
//noinspection unchecked
otherwise( (SqmExpression) result );
otherwise( (SqmExpression<? extends R>) result );
return this;
}

View File

@ -26,22 +26,28 @@ public class SqmCaseSimple<T, R>
extends AbstractSqmExpression<R>
implements JpaSimpleCase<T, R> {
private final SqmExpression<T> fixture;
private final List<WhenFragment<T, R>> whenFragments;
private SqmExpression<R> otherwise;
private final List<WhenFragment<? extends T, ? extends R>> whenFragments;
private SqmExpression<? extends R> otherwise;
public SqmCaseSimple(SqmExpression<T> fixture, NodeBuilder nodeBuilder) {
this( fixture, null, nodeBuilder );
this( fixture, null, 10, nodeBuilder );
}
public SqmCaseSimple(SqmExpression<T> fixture, int estimatedWhenSize, NodeBuilder nodeBuilder) {
this( fixture, null, estimatedWhenSize, nodeBuilder );
}
public SqmCaseSimple(SqmExpression<T> fixture, SqmExpressible<R> inherentType, NodeBuilder nodeBuilder) {
super( inherentType, nodeBuilder );
this.whenFragments = new ArrayList<>( );
this.fixture = fixture;
this( fixture, inherentType, 10, nodeBuilder );
}
public SqmCaseSimple(SqmExpression<T> fixture, SqmExpressible<R> inherentType, int estimateWhenSize, NodeBuilder nodeBuilder) {
private SqmCaseSimple(
SqmExpression<T> fixture,
SqmExpressible<R> inherentType,
int estimatedWhenSize,
NodeBuilder nodeBuilder) {
super( inherentType, nodeBuilder );
this.whenFragments = new ArrayList<>( estimateWhenSize );
this.whenFragments = new ArrayList<>( estimatedWhenSize );
this.fixture = fixture;
}
@ -53,14 +59,9 @@ public class SqmCaseSimple<T, R>
}
final SqmCaseSimple<T, R> caseSearched = context.registerCopy(
this,
new SqmCaseSimple<>(
fixture.copy( context ),
getNodeType(),
whenFragments.size(),
nodeBuilder()
)
new SqmCaseSimple<>( fixture.copy( context ), getNodeType(), whenFragments.size(), nodeBuilder() )
);
for ( SqmCaseSimple.WhenFragment<T, R> whenFragment : whenFragments ) {
for ( WhenFragment<? extends T, ? extends R> whenFragment : whenFragments ) {
caseSearched.whenFragments.add(
new SqmCaseSimple.WhenFragment<>(
whenFragment.checkValue.copy( context ),
@ -79,41 +80,41 @@ public class SqmCaseSimple<T, R>
return fixture;
}
public List<WhenFragment<T,R>> getWhenFragments() {
public List<WhenFragment<? extends T,? extends R>> getWhenFragments() {
return whenFragments;
}
public SqmExpression<R> getOtherwise() {
public SqmExpression<? extends R> getOtherwise() {
return otherwise;
}
public void otherwise(SqmExpression<R> otherwiseExpression) {
public void otherwise(SqmExpression<? extends R> otherwiseExpression) {
this.otherwise = otherwiseExpression;
applyInferableResultType( otherwiseExpression.getNodeType() );
}
public void when(SqmExpression<T> test, SqmExpression<R> result) {
public void when(SqmExpression<? extends T> test, SqmExpression<? extends R> result) {
whenFragments.add( new WhenFragment<>( test, result ) );
// TODO: currently does nothing, but it would be nice if it worked!
test.applyInferableType( fixture.getNodeType() );
applyInferableResultType( result.getNodeType() );
}
private void applyInferableResultType(SqmExpressible<?> type) {
if ( type == null ) {
return;
}
final SqmExpressible<?> oldType = getExpressible();
final SqmExpressible<?> newType = QueryHelper.highestPrecedenceType2( oldType, type );
if ( newType != null && newType != oldType ) {
internalApplyInferableType( newType );
if ( type != null ) {
final SqmExpressible<?> oldType = getExpressible();
final SqmExpressible<?> newType = QueryHelper.highestPrecedenceType2( oldType, type );
if ( newType != null && newType != oldType ) {
internalApplyInferableType( newType );
}
}
}
@Override
protected void internalApplyInferableType(SqmExpressible newType) {
protected void internalApplyInferableType(SqmExpressible<?> newType) {
super.internalApplyInferableType( newType );
if ( otherwise != null ) {
@ -121,9 +122,7 @@ public class SqmCaseSimple<T, R>
}
if ( whenFragments != null ) {
whenFragments.forEach(
whenFragment -> whenFragment.getResult().applyInferableType( newType )
);
whenFragments.forEach( whenFragment -> whenFragment.getResult().applyInferableType( newType ) );
}
}
@ -146,11 +145,6 @@ public class SqmCaseSimple<T, R>
this.result = result;
}
private WhenFragment(WhenFragment<T, R> original, SqmCopyContext context) {
this.checkValue = original.checkValue.copy( context );
this.result = original.result.copy( context );
}
public SqmExpression<T> getCheckValue() {
return checkValue;
}
@ -164,7 +158,7 @@ public class SqmCaseSimple<T, R>
public void appendHqlString(StringBuilder sb) {
sb.append( "case " );
fixture.appendHqlString( sb );
for ( WhenFragment<T, R> whenFragment : whenFragments ) {
for ( WhenFragment<? extends T, ? extends R> whenFragment : whenFragments ) {
sb.append( " when " );
whenFragment.checkValue.appendHqlString( sb );
sb.append( " then " );
@ -195,8 +189,7 @@ public class SqmCaseSimple<T, R>
@Override
public JpaSimpleCase<T, R> when(T condition, Expression<? extends R> result) {
//noinspection unchecked
when( nodeBuilder().value( condition, fixture ), (SqmExpression<R>) result );
when( nodeBuilder().value( condition, fixture ), (SqmExpression<? extends R>) result );
return this;
}
@ -208,8 +201,7 @@ public class SqmCaseSimple<T, R>
@Override
public JpaSimpleCase<T, R> when(Expression<? extends T> condition, Expression<? extends R> result) {
//noinspection unchecked
when( (SqmExpression<T>) condition, (SqmExpression<R>) result );
when( (SqmExpression<? extends T>) condition, (SqmExpression<? extends R>) result );
return this;
}
@ -221,8 +213,7 @@ public class SqmCaseSimple<T, R>
@Override
public JpaSimpleCase<T, R> otherwise(Expression<? extends R> result) {
//noinspection unchecked
otherwise( (SqmExpression<R>) result );
otherwise( (SqmExpression<? extends R>) result );
return this;
}

View File

@ -27,7 +27,7 @@ import org.hibernate.type.descriptor.java.JavaType;
public class SqmLiteral<T> extends AbstractSqmExpression<T> {
private final T value;
public SqmLiteral(T value, SqmExpressible<? extends T> inherentType, NodeBuilder nodeBuilder) {
public SqmLiteral(T value, SqmExpressible<? super T> inherentType, NodeBuilder nodeBuilder) {
super( inherentType, nodeBuilder );
assert value != null && ( inherentType == null || inherentType.getExpressibleJavaType().isInstance( value ) );
this.value = value;

View File

@ -19,7 +19,7 @@ public class ValueBindJpaCriteriaParameter<T> extends JpaCriteriaParameter<T>{
private final T value;
public ValueBindJpaCriteriaParameter(
BindableType<T> type,
BindableType<? super T> type,
T value,
NodeBuilder nodeBuilder) {
super( null, type, false, nodeBuilder );

View File

@ -21,8 +21,8 @@ import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
public abstract class AbstractJpaSelection<T>
extends AbstractJpaTupleElement<T>
implements SqmSelectableNode<T>, JpaSelection<T> {
protected AbstractJpaSelection(SqmExpressible<? extends T> sqmExpressible, NodeBuilder criteriaBuilder) {
super(sqmExpressible, criteriaBuilder );
protected AbstractJpaSelection(SqmExpressible<? super T> sqmExpressible, NodeBuilder criteriaBuilder) {
super( sqmExpressible, criteriaBuilder );
}
@Override

View File

@ -25,10 +25,9 @@ public abstract class AbstractJpaTupleElement<T>
private SqmExpressible<T> expressibleType;
private String alias;
protected AbstractJpaTupleElement(SqmExpressible<? extends T> expressibleType, NodeBuilder criteriaBuilder) {
protected AbstractJpaTupleElement(SqmExpressible<? super T> expressibleType, NodeBuilder criteriaBuilder) {
super( criteriaBuilder );
setExpressibleType(expressibleType);
setExpressibleType( expressibleType );
}
protected void copyTo(AbstractJpaTupleElement<T> target, SqmCopyContext context) {

View File

@ -38,10 +38,9 @@ import org.hibernate.query.sqm.tree.predicate.SqmTruthnessPredicate;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
/**
* todo (6.0) : how is this different from {@link org.hibernate.query.sqm.internal.ParameterCollector}?
*
* @author Steve Ebersole
*/
// todo (6.0) : how is this different from org.hibernate.query.sqm.internal.ParameterCollector?
public class ParameterCollector extends BaseSemanticQueryWalker {
public static Set<SqmParameter<?>> collectParameters(SqmStatement<?> statement) {
@ -76,12 +75,11 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
}
/**
* This is called while performing an inflight parameter collection of parameters
* for `CriteriaQuery#getParameters`. That method can be called multiple times and
* the parameters may have changed in between each call - therefore the parameters
* must be collected dynamically each time.
*
* This form simply returns the JpaCriteriaParameter
* This is called while performing an in-flight parameter collection of parameters
* for {@link jakarta.persistence.criteria.CriteriaQuery#getParameters}. That method
* can be called multiple times and the parameters may have changed in between each
* call - therefore the parameters must be collected dynamically each time.
* This form simply returns the {@link JpaCriteriaParameter}.
*
* @see SqmSelectStatement#resolveParameters()
*/
@ -140,7 +138,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
private SqmExpressibleAccessor<?> inferenceBasis;
private void withTypeInference(SqmExpressibleAccessor<?> inferenceBasis, Runnable action) {
private <T> void withTypeInference(SqmExpressibleAccessor<T> inferenceBasis, Runnable action) {
SqmExpressibleAccessor<?> original = this.inferenceBasis;
this.inferenceBasis = inferenceBasis;
try {
@ -159,21 +157,21 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) {
final SqmExpressible<?> resolved = whenFragment.getCheckValue().getExpressible();
if ( resolved != null ) {
return (SqmExpressible<Object>) resolved;
return (SqmExpressible<?>) resolved;
}
}
return null;
},
() -> expression.getFixture().accept( this )
);
SqmExpressibleAccessor<?> resolved = determineCurrentExpressible( expression );
SqmExpressibleAccessor<?> resolved = toExpressibleAccessor( expression );
for ( SqmCaseSimple.WhenFragment<?, ?> whenFragment : expression.getWhenFragments() ) {
withTypeInference(
expression.getFixture(),
() -> whenFragment.getCheckValue().accept( this )
);
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
getInferenceBasis( inferenceSupplier, resolved ),
() -> whenFragment.getResult().accept( this )
);
resolved = highestPrecedence( resolved, whenFragment.getResult() );
@ -181,7 +179,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
if ( expression.getOtherwise() != null ) {
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
getInferenceBasis( inferenceSupplier, resolved ),
() -> expression.getOtherwise().accept( this )
);
}
@ -192,7 +190,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
@Override
public Object visitSearchedCaseExpression(SqmCaseSearched<?> expression) {
final SqmExpressibleAccessor<?> inferenceSupplier = this.inferenceBasis;
SqmExpressibleAccessor<?> resolved = determineCurrentExpressible( expression );
SqmExpressibleAccessor<?> resolved = toExpressibleAccessor( expression );
for ( SqmCaseSearched.WhenFragment<?> whenFragment : expression.getWhenFragments() ) {
withTypeInference(
@ -200,7 +198,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
() -> whenFragment.getPredicate().accept( this )
);
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
getInferenceBasis( inferenceSupplier, resolved ),
() -> whenFragment.getResult().accept( this )
);
resolved = highestPrecedence( resolved, whenFragment.getResult() );
@ -208,7 +206,7 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
if ( expression.getOtherwise() != null ) {
withTypeInference(
resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved,
getInferenceBasis( inferenceSupplier, resolved ),
() -> expression.getOtherwise().accept( this )
);
}
@ -216,6 +214,12 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
return expression;
}
private static SqmExpressibleAccessor<?> getInferenceBasis(
SqmExpressibleAccessor<?> inferenceSupplier, SqmExpressibleAccessor<?> resolved) {
// return resolved == null && inferenceSupplier != null ? inferenceSupplier : resolved;
return inferenceSupplier == null ? resolved : inferenceSupplier;
}
private SqmExpressibleAccessor<?> highestPrecedence(SqmExpressibleAccessor<?> type1, SqmExpressibleAccessor<?> type2) {
if ( type1 == null ) {
return type2;
@ -235,11 +239,9 @@ public class ParameterCollector extends BaseSemanticQueryWalker {
return type1;
}
private SqmExpressibleAccessor<?> determineCurrentExpressible(SqmExpression<?> expression) {
if ( expression.getExpressible() != null ) {
return () -> (SqmExpressible<Object>) expression.getExpressible();
}
return null;
private <T> SqmExpressibleAccessor<T> toExpressibleAccessor(SqmExpression<T> expression) {
final SqmExpressible<T> expressible = expression.getExpressible();
return expressible == null ? null : () -> expressible;
}
@Override

View File

@ -29,6 +29,8 @@ import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.metamodel.SingularAttribute;
import static org.hibernate.query.sqm.internal.TypecheckUtil.assertAssignable;
/**
* @author Steve Ebersole
*/
@ -124,11 +126,18 @@ public class SqmUpdateStatement<T>
return this;
}
@Override
@Override @SuppressWarnings({"rawtypes", "unchecked"})
public SqmUpdateStatement<T> set(String attributeName, Object value) {
//noinspection unchecked
final SqmPath<Object> sqmPath = (SqmPath<Object>) getTarget().get( attributeName );
applyAssignment( sqmPath, (SqmExpression<?>) nodeBuilder().value( value ) );
final SqmPath sqmPath = getTarget().get(attributeName);
final SqmExpression expression;
if ( value instanceof SqmExpression ) {
expression = (SqmExpression) value;
}
else {
expression = (SqmExpression) nodeBuilder().value( value );
}
assertAssignable( null, sqmPath, expression, nodeBuilder().getSessionFactory() );
applyAssignment( sqmPath, expression );
return this;
}

View File

@ -67,13 +67,7 @@ public abstract class AbstractJdbcParameter
// - anything that is the same for each row always - parameter, literal, etc;
// the idea would be to write the value directly into the JdbcValues array
// and not generating a SQL selection in the query sent to DB
return new SqlSelectionImpl(
jdbcPosition,
valuesArrayPosition,
javaType,
this,
false
);
return new SqlSelectionImpl( jdbcPosition, valuesArrayPosition, javaType, this, false );
}
@Override
@ -93,7 +87,11 @@ public abstract class AbstractJdbcParameter
throw new ExecutionException( "JDBC parameter value not bound - " + this );
}
final Object bindValue = binding.getBindValue();
final JdbcMapping jdbcMapping = jdbcMapping( executionContext, binding );
bindParameterValue( jdbcMapping, statement, binding.getBindValue(), startPosition, executionContext );
}
private JdbcMapping jdbcMapping(ExecutionContext executionContext, JdbcParameterBinding binding) {
JdbcMapping jdbcMapping = binding.getBindType();
if ( jdbcMapping == null ) {
@ -102,10 +100,14 @@ public abstract class AbstractJdbcParameter
// If the parameter type is not known from the context i.e. null or Object, infer it from the bind value
if ( jdbcMapping == null || jdbcMapping.getMappedJavaType().getJavaTypeClass() == Object.class ) {
jdbcMapping = guessBindType( executionContext, bindValue, jdbcMapping );
jdbcMapping = guessBindType( executionContext, binding.getBindValue(), jdbcMapping );
}
bindParameterValue( jdbcMapping, statement, bindValue, startPosition, executionContext );
if ( jdbcMapping == null ) {
throw new ExecutionException( "No JDBC mapping could be inferred for parameter - " + this );
}
return jdbcMapping;
}
protected void bindParameterValue(
@ -127,14 +129,12 @@ public abstract class AbstractJdbcParameter
if ( bindValue == null && jdbcMapping != null ) {
return jdbcMapping;
}
final BindableType<?> parameterType =
executionContext.getSession().getFactory().getMappingMetamodel()
.resolveParameterBindType( bindValue );
if ( parameterType instanceof JdbcMapping ) {
return (JdbcMapping) parameterType;
else {
final BindableType<?> parameterType =
executionContext.getSession().getFactory().getMappingMetamodel()
.resolveParameterBindType( bindValue );
return parameterType instanceof JdbcMapping ? (JdbcMapping) parameterType : null;
}
return null;
}
@Override

View File

@ -8,7 +8,6 @@ package org.hibernate.sql.exec.spi;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -94,11 +93,7 @@ public interface JdbcParameterBindings {
addBinding(
params.get( selectionIndex ),
new JdbcParameterBindingImpl(
BindingTypeHelper.INSTANCE.resolveBindType(
jdbcValue,
type,
typeConfiguration
),
BindingTypeHelper.INSTANCE.resolveBindType( jdbcValue, type, typeConfiguration ),
jdbcValue
)
);

View File

@ -2031,6 +2031,17 @@ public class FunctionTests {
);
}
@Test
public void testEnumIsNull(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createSelectionQuery("from EntityOfBasics where gender is null").getResultList();
session.createSelectionQuery("from EntityOfBasics e where e.gender is null").getResultList();
// session.createSelectionQuery("from EntityOfBasics where :gender is null").setParameter("gender", EntityOfBasics.Gender.MALE).getResultList();
}
);
}
static class Pair {
int integer; double floating;
Pair(int integer, double floating) {