HHH-15802 SubQuery with 'in' results in java.lang.ClassCastException class org.hibernate.metamodel.mapping.internal.BasicEntityIdentifierMappingImpl cannot be cast to class org.hibernate.metamodel.mapping.EntityValuedModelPart

This commit is contained in:
Andrea Boriero 2022-12-19 15:04:12 +01:00 committed by Christian Beikov
parent e896656bb3
commit 8f78e4e23b
24 changed files with 513 additions and 91 deletions

View File

@ -81,7 +81,7 @@ public class InferredBasicValueResolver {
typeConfiguration
);
}
else if ( explicitJavaType instanceof TemporalJavaType ) {
else if ( explicitJavaType.isTemporalType() ) {
return fromTemporal(
(TemporalJavaType<T>) explicitJavaType,
null,
@ -133,7 +133,7 @@ public class InferredBasicValueResolver {
typeConfiguration
);
}
else if ( reflectedJtd instanceof TemporalJavaType ) {
else if ( reflectedJtd.isTemporalType() ) {
return fromTemporal(
(TemporalJavaType<T>) reflectedJtd,
null,
@ -170,7 +170,7 @@ public class InferredBasicValueResolver {
);
registeredElementType = resolution.getLegacyResolvedBasicType();
}
else if ( elementJtd instanceof TemporalJavaType ) {
else if ( elementJtd.isTemporalType() ) {
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromTemporal(
(TemporalJavaType<T>) elementJtd,
null,
@ -492,7 +492,7 @@ public class InferredBasicValueResolver {
// Case #1 - explicit JavaType
if ( explicitJavaType != null ) {
if ( !(explicitJavaType instanceof TemporalJavaType) ) {
if ( !explicitJavaType.isTemporalType() ) {
throw new MappingException(
"Explicit JavaType [" + explicitJavaType +
"] defined for temporal value must implement TemporalJavaType"

View File

@ -47,4 +47,9 @@ public interface PluralPersistentAttribute<D, C, E>
"Plural attribute [" + getPathName() + "] is not indexed (list / map)"
);
}
@Override
default boolean checkTypeComparability() {
return false;
}
}

View File

@ -28,4 +28,8 @@ public interface TupleType<J> extends SqmExpressible<J> {
SqmExpressible<?> get(int index);
SqmExpressible<?> get(String componentName);
@Override
default boolean checkTypeComparability() {
return false;
}
}

View File

@ -60,4 +60,9 @@ public class DiscriminatorSqmPathSource<D> extends AbstractSqmPathSource<D>
public Class<D> getJavaType() {
return getExpressibleJavaType().getJavaTypeClass();
}
@Override
public boolean checkTypeComparability() {
return false;
}
}

View File

@ -94,6 +94,7 @@ import org.hibernate.query.sqm.function.NamedSqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.internal.ParameterCollector;
import org.hibernate.query.sqm.internal.SqmCreationProcessingStateImpl;
import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder;
import org.hibernate.query.sqm.internal.SqmDmlCreationProcessingState;
import org.hibernate.query.sqm.internal.SqmQueryPartCreationProcessingStateStandardImpl;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
@ -2463,6 +2464,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
break;
}
}
((SqmCriteriaNodeBuilder) creationContext.getNodeBuilder()).assertComparable( left, right );
return new SqmComparisonPredicate(
left,
comparisonOperator,

View File

@ -41,7 +41,7 @@ public class BindingTypeHelper {
SessionFactoryImplementor sessionFactory) {
if ( precision != null ) {
final SqmExpressible<T> sqmExpressible = declaredParameterType.resolveExpressible( sessionFactory );
if ( !( sqmExpressible.getExpressibleJavaType() instanceof TemporalJavaType ) ) {
if ( !( sqmExpressible.getExpressibleJavaType().isTemporalType() ) ) {
throw new UnsupportedOperationException(
"Cannot treat non-temporal parameter type with temporal precision"
);
@ -64,7 +64,7 @@ public class BindingTypeHelper {
Object value,
JdbcMapping baseType,
TypeConfiguration typeConfiguration) {
if ( value == null || !( baseType.getJdbcJavaType() instanceof TemporalJavaType<?> ) ) {
if ( value == null || !baseType.getJdbcJavaType().isTemporalType() ) {
return baseType;
}

View File

@ -290,7 +290,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
}
private void setExplicitTemporalPrecision(TemporalType temporalTypePrecision) {
if ( bindType == null || determineJavaType( bindType ) instanceof TemporalJavaType<?> ) {
if ( bindType == null || determineJavaType( bindType ).isTemporalType() ) {
this.bindType = BindingTypeHelper.INSTANCE.resolveTemporalPrecision(
temporalTypePrecision,
bindType,

View File

@ -34,4 +34,8 @@ public interface SqmExpressible<J> extends BindableType<J> {
default SqmExpressible<J> resolveExpressible(SessionFactoryImplementor sessionFactory) {
return this;
}
default boolean checkTypeComparability(){
return true;
}
}

View File

@ -42,6 +42,9 @@ import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.TupleType;
import org.hibernate.metamodel.model.domain.internal.DiscriminatorSqmPathSource;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.query.BindableType;
@ -231,6 +234,42 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return sessionFactory.get();
}
public boolean areTypesComparable(SqmExpressible<?> lhsType, SqmExpressible<?> rhsType) {
if ( lhsType == null || rhsType == null || lhsType == rhsType ) {
return true;
}
if ( !lhsType.checkTypeComparability() || !rhsType.checkTypeComparability() ) {
return true;
}
final JavaType<?> lhsJavaType = lhsType.getExpressibleJavaType();
final JavaType<?> rhsJavaType = rhsType.getExpressibleJavaType();
if ( lhsJavaType.isTemporalType() || rhsJavaType.isTemporalType() ) {
return true;
}
if ( lhsJavaType.isUnknownType() || rhsJavaType.isUnknownType() ) {
return true;
}
if ( lhsJavaType == rhsJavaType
|| lhsJavaType.isWider( rhsJavaType )
|| rhsJavaType.isWider( lhsJavaType ) ) {
// Assume we can coerce one to another
return true;
}
if ( lhsJavaType.getJavaTypeClass().isAssignableFrom( rhsJavaType.getJavaTypeClass() )
|| rhsJavaType.getJavaTypeClass().isAssignableFrom( lhsJavaType.getJavaTypeClass() ) ) {
// Polymorphic entity comparison
return true;
}
return false;
}
@Override
public BasicType<Boolean> getBooleanType() {
final BasicType<Boolean> booleanType = this.booleanType;
@ -1253,6 +1292,11 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
public Class<T> getBindableJavaType() {
return javaType.getJavaTypeClass();
}
@Override
public boolean checkTypeComparability() {
return false;
}
}
@Override
@ -1996,6 +2040,8 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <Y extends Comparable<? super Y>> SqmPredicate between(Expression<? extends Y> value, Expression<? extends Y> lower, Expression<? extends Y> upper) {
assertComparable( value, lower );
assertComparable( value, upper );
//noinspection unchecked
return new SqmBetweenPredicate(
(SqmExpression<? extends Y>) value,
@ -2010,17 +2056,36 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@SuppressWarnings("unchecked")
public <Y extends Comparable<? super Y>> SqmPredicate between(Expression<? extends Y> value, Y lower, Y upper) {
final SqmExpression<? extends Y> valueExpression = (SqmExpression<? extends Y>) value;
final SqmExpression<?> lowerExpr = value( lower, valueExpression );
final SqmExpression<?> upperExpr = value( upper, valueExpression );
assertComparable( valueExpression, lowerExpr );
assertComparable( valueExpression, upperExpr );
return new SqmBetweenPredicate(
valueExpression,
value( lower, valueExpression ),
value( upper, valueExpression ),
lowerExpr,
upperExpr,
false,
this
);
}
public void assertComparable(Expression<?> x, Expression<?> y) {
final SqmExpressible<?> lhsType = ((SqmExpression<?>) x).getNodeType();
final SqmExpressible<?> rhsType = ((SqmExpression<?>) y).getNodeType();
if ( !areTypesComparable( lhsType, rhsType ) ) {
throw new IllegalArgumentException(
String.format(
"Can't compare test expression of type [%s] with element of type [%s]",
lhsType,
rhsType
)
);
}
}
@Override
public SqmPredicate equal(Expression<?> x, Expression<?> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.EQUAL,
@ -2031,16 +2096,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate equal(Expression<?> x, Object y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.EQUAL,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate notEqual(Expression<?> x, Expression<?> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.NOT_EQUAL,
@ -2051,16 +2119,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate notEqual(Expression<?> x, Object y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.NOT_EQUAL,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate distinctFrom(Expression<?> x, Expression<?> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.DISTINCT_FROM,
@ -2071,16 +2142,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate distinctFrom(Expression<?> x, Object y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.DISTINCT_FROM,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate notDistinctFrom(Expression<?> x, Expression<?> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.NOT_DISTINCT_FROM,
@ -2091,16 +2165,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate notDistinctFrom(Expression<?> x, Object y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.NOT_DISTINCT_FROM,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public <Y extends Comparable<? super Y>> SqmPredicate greaterThan(Expression<? extends Y> x, Expression<? extends Y> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN,
@ -2112,16 +2189,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <Y extends Comparable<? super Y>> SqmPredicate greaterThan(Expression<? extends Y> x, Y y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN,
value( y, (SqmExpression) x ),
yExpr,
this
);
}
@Override
public <Y extends Comparable<? super Y>> SqmPredicate greaterThanOrEqualTo(Expression<? extends Y> x, Expression<? extends Y> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN_OR_EQUAL,
@ -2133,16 +2213,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <Y extends Comparable<? super Y>> SqmPredicate greaterThanOrEqualTo(Expression<? extends Y> x, Y y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN_OR_EQUAL,
value( y, (SqmExpression) x ),
yExpr,
this
);
}
@Override
public <Y extends Comparable<? super Y>> SqmPredicate lessThan(Expression<? extends Y> x, Expression<? extends Y> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN,
@ -2153,10 +2236,12 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <Y extends Comparable<? super Y>> SqmPredicate lessThan(Expression<? extends Y> x, Y y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@ -2173,16 +2258,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <Y extends Comparable<? super Y>> SqmPredicate lessThanOrEqualTo(Expression<? extends Y> x, Y y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN_OR_EQUAL,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate gt(Expression<? extends Number> x, Expression<? extends Number> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN,
@ -2193,16 +2281,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate gt(Expression<? extends Number> x, Number y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate ge(Expression<? extends Number> x, Expression<? extends Number> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN_OR_EQUAL,
@ -2213,16 +2304,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate ge(Expression<? extends Number> x, Number y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.GREATER_THAN_OR_EQUAL,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate lt(Expression<? extends Number> x, Expression<? extends Number> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN,
@ -2233,16 +2327,19 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate lt(Expression<? extends Number> x, Number y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}
@Override
public SqmPredicate le(Expression<? extends Number> x, Expression<? extends Number> y) {
assertComparable( x, y );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN_OR_EQUAL,
@ -2253,10 +2350,12 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public SqmPredicate le(Expression<? extends Number> x, Number y) {
final SqmExpression<?> yExpr = value( y, (SqmExpression<?>) x );
assertComparable( x, yExpr );
return new SqmComparisonPredicate(
(SqmExpression<?>) x,
ComparisonOperator.LESS_THAN_OR_EQUAL,
value( y, (SqmExpression<?>) x ),
yExpr,
this
);
}

View File

@ -6061,7 +6061,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
private static Expression getActualExpression(Expression expression) {
if ( expression.getExpressionType() instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddableValuedModelPart = (EmbeddableValuedModelPart) expression.getExpressionType();
if ( embeddableValuedModelPart.getJavaType() instanceof TemporalJavaType<?> ) {
if ( embeddableValuedModelPart.getJavaType().isTemporalType() ) {
return ( (SqlTupleContainer) expression ).getSqlTuple().getExpressions().get( 0 );
}
}

View File

@ -111,7 +111,7 @@ public class SqmExpressionHelper {
public static boolean isCompositeTemporal(SqmExpression<?> expression) {
// When TimeZoneStorageStrategy.COLUMN is used, that implies using a composite user type
return expression instanceof SqmPath<?> && expression.getNodeType() instanceof EmbeddedSqmPathSource<?>
&& expression.getJavaTypeDescriptor() instanceof TemporalJavaType<?>;
&& expression.getJavaTypeDescriptor().isTemporalType();
}
public static SqmExpression<?> getActualExpression(SqmExpression<?> expression) {

View File

@ -15,6 +15,7 @@ import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
@ -134,11 +135,9 @@ public class SqmInListPredicate<T> extends AbstractNegatableSqmPredicate impleme
}
private void implyListElementType(SqmExpression<?> expression) {
nodeBuilder().assertComparable( getTestExpression(), expression );
expression.applyInferableType(
QueryHelper.highestPrecedenceType2(
getTestExpression().getNodeType(),
expression.getNodeType()
)
QueryHelper.highestPrecedenceType2( getTestExpression().getNodeType(), expression.getNodeType() )
);
}

View File

@ -27,10 +27,12 @@ import org.hibernate.query.sqm.tree.domain.SqmDerivedRoot;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.AbstractQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Selection;
import jakarta.persistence.criteria.Subquery;
import jakarta.persistence.metamodel.EntityType;
@ -373,4 +375,37 @@ public abstract class AbstractSqmSelectQuery<T>
}
sqmQueryPart.appendHqlString( sb );
}
protected Selection<? extends T> getResultSelection(Selection<?>[] selections) {
final Selection<? extends T> resultSelection;
Class<T> resultType = getResultType();
if ( resultType == null || resultType == Object.class ) {
switch ( selections.length ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
resultSelection = ( Selection<? extends T> ) selections[0];
break;
}
default: {
setResultType( (Class<T>) Object[].class );
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
}
}
}
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( selections );
}
else if ( resultType.isArray() ) {
resultSelection = nodeBuilder().array( resultType, selections );
}
else {
resultSelection = nodeBuilder().construct( resultType, selections );
}
return resultSelection;
}
}

View File

@ -277,34 +277,7 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
}
}
final Selection<? extends T> resultSelection;
Class<T> resultType = getResultType();
if ( resultType == null || resultType == Object.class ) {
switch ( selections.length ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
resultSelection = ( Selection<? extends T> ) selections[0];
break;
}
default: {
setResultType( (Class<T>) Object[].class );
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
}
}
}
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( selections );
}
else if ( resultType.isArray() ) {
resultSelection = nodeBuilder().array( resultType, selections );
}
else {
resultSelection = nodeBuilder().construct( resultType, selections );
}
final Selection<? extends T> resultSelection = getResultSelection( selections );
getQuerySpec().getSelectClause().setSelection( (SqmSelectableNode<?>) resultSelection );
return this;
}

View File

@ -14,6 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.criteria.JpaCteContainer;
import org.hibernate.query.criteria.JpaCteCriteria;
import org.hibernate.query.criteria.JpaExpression;
@ -51,6 +52,7 @@ import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmInPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.Tuple;
@ -83,6 +85,7 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
NodeBuilder builder) {
super( queryPart, resultType, builder );
this.parent = parent;
applyInferableType( resultType );
}
public SqmSubQuery(
@ -94,6 +97,7 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
super( builder, cteStatements, resultType );
this.parent = parent;
setQueryPart( queryPart );
applyInferableType( resultType );
}
public SqmSubQuery(
@ -102,6 +106,7 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
NodeBuilder builder) {
super( resultType, builder );
this.parent = parent;
applyInferableType( resultType );
}
public SqmSubQuery(
@ -197,6 +202,7 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
}
//noinspection unchecked
querySpec.setSelection( (JpaSelection<T>) expression );
// applyInferableType( (Class<T>) querySpec.getSelection().getJavaType() );
return this;
}
@ -205,34 +211,8 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
public SqmSubQuery<T> multiselect(Selection<?>... selections) {
validateComplianceMultiselect();
final Selection<? extends T> resultSelection;
Class<T> resultType = getResultType();
if ( resultType == null || resultType == Object.class ) {
switch ( selections.length ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
resultSelection = ( Selection<? extends T> ) selections[0];
break;
}
default: {
setResultType( (Class<T>) Object[].class );
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
}
}
}
else if ( Tuple.class.isAssignableFrom( resultType ) ) {
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( selections );
}
else if ( resultType.isArray() ) {
resultSelection = nodeBuilder().array( resultType, selections );
}
else {
resultSelection = nodeBuilder().construct( resultType, selections );
}
final Selection<? extends T> resultSelection = getResultSelection( selections );
final SqmQuerySpec<T> querySpec = getQuerySpec();
if ( querySpec.getSelectClause() == null ) {
querySpec.setSelectClause( new SqmSelectClause( false, 1, nodeBuilder() ) );
@ -603,6 +583,18 @@ public class SqmSubQuery<T> extends AbstractSqmSelectQuery<T> implements SqmSele
setResultType( type == null ? null : expressibleType.getExpressibleJavaType().getJavaTypeClass() );
}
private void applyInferableType(Class<T> type) {
final EntityDomainType<T> entityDescriptor = nodeBuilder().getSessionFactory().getRuntimeMetamodels()
.getJpaMetamodel()
.findEntityType( type );
if ( entityDescriptor != null ) {
this.expressibleType = entityDescriptor;
}
else {
this.expressibleType = nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( type );
}
}
@Override
public SqmExpression<Long> asLong() {
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( Long.class ) );

View File

@ -61,4 +61,13 @@ public class CurrencyJavaType extends AbstractClassJavaType<Currency> {
return 3;
}
@Override
public boolean isWider(JavaType<?> javaType) {
switch ( javaType.getJavaType().getTypeName() ) {
case "java.lang.String":
return true;
default:
return false;
}
}
}

View File

@ -227,4 +227,23 @@ public class EnumJavaType<T extends Enum<T>> extends AbstractClassJavaType<T> {
}
return Enum.valueOf( getJavaTypeClass(), relationalForm.trim() );
}
@Override
public boolean isWider(JavaType<?> javaType) {
switch ( javaType.getJavaType().getTypeName() ) {
case "byte":
case "java.lang.Byte":
case "short":
case "java.lang.Short":
case "int":
case "java.lang.Integer":
case "long":
case "java.lang.Long":
case "java.lang.String":
case "java.lang.Character":
return true;
default:
return false;
}
}
}

View File

@ -21,6 +21,7 @@ import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.spi.TypeConfiguration;
@ -323,4 +324,22 @@ public interface JavaType<T> extends Serializable {
return createJavaType( parameterizedType );
}
/**
* Return true if the implementation is an instance of {@link TemporalJavaType}
*
* @return true if it is an instance of {@link TemporalJavaType}; false otherwise
*/
default boolean isTemporalType() {
return false;
}
/**
* Return true if the implementation is an instance of {@link org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType}
*
* @return true if it is an instance of {@link UnknownBasicJavaType}; false otherwise
*/
default boolean isUnknownType(){
return false;
}
}

View File

@ -45,4 +45,9 @@ public interface TemporalJavaType<T> extends BasicJavaType<T> {
<X> TemporalJavaType<X> resolveTypeForPrecision(
TemporalType precision,
TypeConfiguration typeConfiguration);
@Override
default boolean isTemporalType() {
return true;
}
}

View File

@ -76,4 +76,9 @@ public class UnknownBasicJavaType<T> extends AbstractJavaType<T> {
public String toString() {
return "BasicJavaType(" + getJavaType().getTypeName() + ")";
}
@Override
public boolean isUnknownType() {
return true;
}
}

View File

@ -27,7 +27,6 @@ import org.hibernate.type.format.FormatMapper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
@ -450,7 +449,7 @@ public final class JaxbXmlFormatMapper implements FormatMapper {
.getTypeConfiguration()
.getJavaTypeRegistry()
.findDescriptor( elementClass );
if ( exampleElement == null && ( elementJavaType == null || elementJavaType instanceof UnknownBasicJavaType<?> ) ) {
if ( exampleElement == null && ( elementJavaType == null || elementJavaType.isUnknownType() ) ) {
try {
final Constructor<?> declaredConstructor = elementClass.getDeclaredConstructor();
exampleElement = declaredConstructor.newInstance();
@ -480,7 +479,7 @@ public final class JaxbXmlFormatMapper implements FormatMapper {
Object exampleElement,
JAXBIntrospector introspector,
WrapperOptions wrapperOptions) {
if ( exampleElement == null && elementJavaType instanceof UnknownBasicJavaType<?> ) {
if ( exampleElement == null && elementJavaType.isUnknownType() ) {
try {
final Constructor<?> declaredConstructor = elementJavaType.getJavaTypeClass().getDeclaredConstructor();
exampleElement = declaredConstructor.newInstance();

View File

@ -378,8 +378,7 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<Department> criteria = criteriaBuilder.createQuery( Department.class );
criteria.where( criteriaBuilder.in( criteria.from( Department.class ).get( "id" ) )
.value( subquery ) );
criteria.where( criteriaBuilder.in( criteria.from( Department.class ) ).value( subquery ) );
Query<Department> departmentsQuery = session.createQuery( criteria );
List<Department> departments = departmentsQuery.list();
@ -440,7 +439,7 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
session.enableFilter( "region" ).setParameter( "region", "APAC" );
session.enableFilter( "effectiveDate" ).setParameter( "asOfDate", testData.lastMonth.getTime() );
Subquery<Product> productSubquery = detachedCriteriaBuilder.createQuery().subquery( Product.class );
Subquery<Long> productSubquery = detachedCriteriaBuilder.createQuery().subquery( Long.class );
Root<Product> productRoot = productSubquery.from( Product.class );
productSubquery.select( productRoot.get( "id" ) );
productSubquery.where( detachedCriteriaBuilder.equal(
@ -549,7 +548,7 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
session.enableFilter( "region" ).setParameter( "region", "APAC" );
List orders = session.createQuery(
"select o from Order as o where exists (select li.id from LineItem li, Product as p where p.id = li.product and li.quantity >= ?1 and p.name = ?2) and o.buyer = ?3" )
"select o from Order as o where exists (select li.id from LineItem li, Product as p where p.id = li.product.id and li.quantity >= ?1 and p.name = ?2) and o.buyer = ?3" )
.setParameter( 1, 1L ).setParameter( 2, "Acme Hair Gel" ).setParameter( 3, "gavin" ).list();
assertEquals( "Incorrect orders count", 1, orders.size() );

View File

@ -0,0 +1,250 @@
package org.hibernate.orm.test.jpa.criteria;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.query.SemanticException;
import org.hibernate.type.descriptor.java.CoercionException;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
@Jpa(
annotatedClasses = {
QueryPredicateAndParameterComparableTest.Participation.class,
QueryPredicateAndParameterComparableTest.Submission.class
}
)
@TestForIssue(jiraKey = "HHH-15802")
public class QueryPredicateAndParameterComparableTest {
@Test
public void testWrongInPredicateType(EntityManagerFactoryScope scope) {
Assertions.assertThrows(
IllegalArgumentException.class, () -> scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Participation> criteria = builder.createQuery( Participation.class );
Root<Participation> root = criteria.from( Participation.class );
criteria.select( root );
Subquery<Participation> subQuery = criteria.subquery( Participation.class );
Root<Submission> rootSubQuery = subQuery.from( Submission.class );
subQuery.select( rootSubQuery.join( "submitters" ) );
criteria.where( root.get( "id" ).in( subQuery ) );
entityManager.createQuery( criteria ).getResultList();
}
)
);
}
@Test
public void testCorrectInPredicateType(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Participation> criteria = builder.createQuery( Participation.class );
Root<Participation> root = criteria.from( Participation.class );
criteria.select( root );
Subquery<Participation> subQuery = criteria.subquery( Participation.class );
Root<Participation> rootSubQuery = subQuery.from( Participation.class );
subQuery.select( rootSubQuery );
criteria.where( root.in( subQuery ) );
entityManager.createQuery( criteria ).getResultList();
}
);
}
@Test
public void testWrongInPredicateType2(EntityManagerFactoryScope scope) {
Assertions.assertThrows(
IllegalArgumentException.class, () -> scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Participation> criteria = builder.createQuery( Participation.class );
Root<Participation> root = criteria.from( Participation.class );
criteria.select( root );
Subquery<Participation> subQuery = criteria.subquery( Participation.class );
Root<Submission> rootSubQuery = subQuery.from( Submission.class );
subQuery.select( rootSubQuery.get( "participation" ) );
criteria.where( root.get( "id" ).in( subQuery ) );
entityManager.createQuery( criteria ).getResultList();
}
)
);
}
@Test
public void testWrongTypeEqualPredicate(EntityManagerFactoryScope scope) {
Assertions.assertThrows(
CoercionException.class, () -> scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Participation> criteria = builder.createQuery( Participation.class );
Root<Participation> root = criteria.from( Participation.class );
criteria.select( root );
Subquery<Participation> subQuery = criteria.subquery( Participation.class );
Root<Submission> rootSubQuery = subQuery.from( Submission.class );
subQuery.select( rootSubQuery.get( "participation" ) );
criteria.where( builder.equal( root.get( "id" ), new Participation() ) );
entityManager.createQuery( criteria ).getResultList();
}
)
);
}
@Test
public void testWrongTypeEqualPredicate2(EntityManagerFactoryScope scope) {
Assertions.assertThrows(
IllegalArgumentException.class, () -> scope.inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Participation> criteria = builder.createQuery( Participation.class );
Root<Participation> root = criteria.from( Participation.class );
criteria.select( root );
Subquery<Participation> subQuery = criteria.subquery( Participation.class );
Root<Submission> rootSubQuery = subQuery.from( Submission.class );
subQuery.select( rootSubQuery.get( "participation" ) );
criteria.where( builder.equal( root, 1 ) );
entityManager.createQuery( criteria ).getResultList();
}
)
);
}
@Test
public void test3(EntityManagerFactoryScope scope) {
Assertions.assertThrows(
IllegalArgumentException.class, () -> scope.inTransaction(
entityManager -> {
entityManager.createQuery( "select p from participations p where p = 1" ).getResultList();
}
)
);
}
@Test
public void testWrongTypeParameter(EntityManagerFactoryScope scope) {
Assertions.assertThrows(
IllegalArgumentException.class, () -> scope.inTransaction(
entityManager -> {
entityManager.createQuery( "select p from participations p where p = :id" ).setParameter( "id", 1 ).getResultList();
}
)
);
}
@Entity(name = "participations")
public static class Participation {
private int id;
private String description;
private Set<Submission> submissions;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "submissions_participations", joinColumns = @JoinColumn(name = "submitters_id"), inverseJoinColumns = @JoinColumn(name = "submissions_submissionid"))
public Set<Submission> getSubmissions() {
return submissions;
}
public void setSubmissions(Set<Submission> submissions) {
this.submissions = submissions;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@Entity(name = "submissions")
public class Submission {
private int submissionid;
private String description;
private Set<Participation> submitters = new HashSet<>();
private Participation participation;
@ManyToMany
@JoinTable(name = "submissions_participations", inverseJoinColumns = @JoinColumn(name = "submitters_id"), joinColumns = @JoinColumn(name = "submissions_submissionid"))
public Set<Participation> getSubmitters() {
return submitters;
}
public void setSubmitters(Set<Participation> submitters) {
this.submitters = submitters;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public int getSubmissionid() {
return submissionid;
}
public void setSubmissionid(int submissionid) {
this.submissionid = submissionid;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@ManyToOne
public Participation getParticipation() {
return participation;
}
public void setParticipation(Participation participation) {
this.participation = participation;
}
}
}

View File

@ -25,7 +25,6 @@ public class IdClassQueryRefTests {
public void testHqlVirtualIdReferences(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createQuery( "from Order o where o.orderNumber = 123" ).list();
session.createQuery( "from Order o where o.customer = 1" ).list();
session.createQuery( "from Order o where o.customer.id = 1" ).list();
} );
}