HHH-15725 Criteria API Expression.as adds cast even when the cast type is equal to the expression type
This commit is contained in:
parent
aece493697
commit
6a1581cf4a
|
@ -301,4 +301,9 @@ public class SingularAttributeImpl<D,J>
|
|||
public SqmPath<J> createSqmPath(SqmPath<?> lhs, SqmPathSource<?> intermediatePathSource) {
|
||||
return sqmPathSource.createSqmPath( lhs, intermediatePathSource );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<?> getRelationalJavaType() {
|
||||
return sqmPathSource.getRelationalJavaType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,4 +56,6 @@ public interface JpaExpression<T> extends JpaSelection<T>, Expression<T> {
|
|||
JpaPredicate equalTo(Expression<T> that);
|
||||
|
||||
JpaPredicate equalTo(T that);
|
||||
|
||||
<X> JpaExpression<X> cast(Class<X> type);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
|||
import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||
import org.hibernate.query.sqm.tree.expression.AsWrapperSqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAny;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
|
||||
|
@ -417,4 +418,6 @@ public interface SemanticQueryWalker<T> {
|
|||
T visitMapEntryFunction(SqmMapEntryReference<?, ?> function);
|
||||
|
||||
T visitFullyQualifiedClass(Class<?> namedClass);
|
||||
|
||||
T visitAsWrapperExpression(AsWrapperSqmExpression<?> expression);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.query.sqm.tree.domain.SqmIndexAggregateFunction;
|
|||
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||
import org.hibernate.query.sqm.tree.expression.AsWrapperSqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAny;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAnyDiscriminatorValue;
|
||||
|
@ -1194,6 +1195,11 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitAsWrapperExpression(AsWrapperSqmExpression expression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitModifiedSubQueryExpression(SqmModifiedSubQueryExpression expression) {
|
||||
return null;
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
|
|||
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||
import org.hibernate.query.sqm.tree.expression.AsWrapperSqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAggregateFunction;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAny;
|
||||
|
@ -981,4 +982,9 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
|
|||
return sqmFieldLiteral;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitAsWrapperExpression(AsWrapperSqmExpression<?> expression) {
|
||||
expression.getExpression().accept( this );
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper;
|
|||
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
||||
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.sql.internal.AnyDiscriminatorPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.AsWrappedExpression;
|
||||
import org.hibernate.query.sqm.sql.internal.BasicValuedPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.DiscriminatedAssociationPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.DiscriminatorPathInterpretation;
|
||||
|
@ -174,6 +175,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
|
|||
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
|
||||
import org.hibernate.query.sqm.tree.expression.AsWrapperSqmExpression;
|
||||
import org.hibernate.query.sqm.tree.expression.Conversion;
|
||||
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
|
||||
|
@ -8268,6 +8270,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
// .getOrMakeJavaDescriptor( namedClass );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitAsWrapperExpression(AsWrapperSqmExpression<?> sqmExpression) {
|
||||
return new AsWrappedExpression<>(
|
||||
(Expression) sqmExpression.getExpression().accept( this ),
|
||||
sqmExpression.getNodeType()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
|
||||
final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer()
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.query.sqm.sql.internal;
|
||||
|
||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
public class AsWrappedExpression<B> implements Expression, DomainResultProducer<B> {
|
||||
private final Expression wrappedExpression;
|
||||
private final BasicType<B> expressionType;
|
||||
|
||||
public AsWrappedExpression(Expression wrappedExpression, BasicType<B> expressionType) {
|
||||
assert wrappedExpression instanceof DomainResultProducer : "AsWrappedExpression expected to be an instance of DomainResultProducer";
|
||||
this.wrappedExpression = wrappedExpression;
|
||||
this.expressionType = expressionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMappingContainer getExpressionType() {
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnReference getColumnReference() {
|
||||
return wrappedExpression.getColumnReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSelection createSqlSelection(
|
||||
int jdbcPosition,
|
||||
int valuesArrayPosition,
|
||||
JavaType javaType,
|
||||
boolean virtual,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
return wrappedExpression.createSqlSelection(
|
||||
jdbcPosition,
|
||||
valuesArrayPosition,
|
||||
javaType,
|
||||
virtual,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSelection createDomainResultSqlSelection(
|
||||
int jdbcPosition,
|
||||
int valuesArrayPosition,
|
||||
JavaType javaType,
|
||||
boolean virtual,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
return wrappedExpression.createDomainResultSqlSelection(
|
||||
jdbcPosition,
|
||||
valuesArrayPosition,
|
||||
javaType,
|
||||
virtual,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker sqlTreeWalker) {
|
||||
wrappedExpression.accept( sqlTreeWalker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<B> createDomainResult(String resultVariable, DomainResultCreationState creationState) {
|
||||
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
|
||||
final SqlSelection sqlSelection = sqlAstCreationState.getSqlExpressionResolver()
|
||||
.resolveSqlSelection(
|
||||
wrappedExpression,
|
||||
wrappedExpression.getExpressionType().getSingleJdbcMapping().getJdbcJavaType(),
|
||||
null,
|
||||
sqlAstCreationState.getCreationContext()
|
||||
.getMappingMetamodel().getTypeConfiguration()
|
||||
);
|
||||
return new BasicResult<>(
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
resultVariable,
|
||||
expressionType.getExpressibleJavaType(),
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySqlSelections(DomainResultCreationState creationState) {
|
||||
//noinspection unchecked
|
||||
( (DomainResultProducer<B>) wrappedExpression ).applySqlSelections( creationState );
|
||||
}
|
||||
}
|
|
@ -197,4 +197,9 @@ public class SqmBasicValuedSimplePath<T>
|
|||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||
return walker.visitBasicValuedPath( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<?> getRelationalJavaType() {
|
||||
return super.getExpressible().getRelationalJavaType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,4 +121,9 @@ public class SqmEmbeddedValuedSimplePath<T>
|
|||
public Class<T> getBindableJavaType() {
|
||||
return getJavaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<?> getRelationalJavaType() {
|
||||
return super.getExpressible().getRelationalJavaType();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ import java.math.BigDecimal;
|
|||
import java.math.BigInteger;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hibernate.query.criteria.JpaSelection;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
import org.hibernate.query.sqm.SqmTreeCreationLogger;
|
||||
import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder;
|
||||
import org.hibernate.query.sqm.tree.jpa.AbstractJpaSelection;
|
||||
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
import jakarta.persistence.criteria.Expression;
|
||||
|
@ -94,7 +94,11 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
|
|||
|
||||
@Override
|
||||
public <X> SqmExpression<X> as(Class<X> type) {
|
||||
return nodeBuilder().cast( this, type );
|
||||
final BasicType<X> basicTypeForJavaType = nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( type );
|
||||
if ( basicTypeForJavaType == null ) {
|
||||
throw new IllegalArgumentException( "Can't cast expression to unknown type: " + type.getCanonicalName() );
|
||||
}
|
||||
return new AsWrapperSqmExpression<>( basicTypeForJavaType, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.query.sqm.tree.expression;
|
||||
|
||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||
import org.hibernate.type.BasicType;
|
||||
|
||||
public class AsWrapperSqmExpression<T> extends AbstractSqmExpression<T> {
|
||||
private final SqmExpression<?> expression;
|
||||
|
||||
AsWrapperSqmExpression(SqmExpressible<T> type, SqmExpression<?> expression) {
|
||||
super( type, expression.nodeBuilder() );
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X accept(SemanticQueryWalker<X> walker) {
|
||||
return walker.visitAsWrapperExpression( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHqlString(StringBuilder sb) {
|
||||
sb.append( "wrap(" );
|
||||
expression.appendHqlString( sb );
|
||||
sb.append( " as " );
|
||||
sb.append( getNodeType().getReturnedClassName() );
|
||||
sb.append( ")" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> SqmExpression<X> as(Class<X> type) {
|
||||
return expression.as( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmExpression<T> copy(SqmCopyContext context) {
|
||||
return new AsWrapperSqmExpression<>( getExpressible(), expression.copy( context ) );
|
||||
}
|
||||
|
||||
public SqmExpression<?> getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicType<T> getNodeType() {
|
||||
return (BasicType<T>) super.getNodeType();
|
||||
}
|
||||
}
|
|
@ -125,4 +125,9 @@ public interface SqmExpression<T> extends SqmSelectableNode<T>, JpaExpression<T>
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <X> SqmExpression<X> cast(Class<X> type) {
|
||||
return castAs( nodeBuilder().getTypeConfiguration().getBasicTypeForJavaType( type ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ import org.hibernate.testing.orm.junit.Jpa;
|
|||
Thing.class,
|
||||
ThingWithQuantity.class,
|
||||
VersionedEntity.class
|
||||
}
|
||||
)
|
||||
})
|
||||
public abstract class AbstractCriteriaTest {
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.hibernate.dialect.CockroachDialect;
|
|||
import org.hibernate.dialect.DerbyDialect;
|
||||
import org.hibernate.dialect.PostgreSQLDialect;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaExpression;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
|
||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||
|
@ -237,14 +238,14 @@ public class CriteriaBuilderNonStandardFunctionsTest {
|
|||
Expression<String> theString = from.get( "theString" );
|
||||
query.multiselect(
|
||||
cb.overlay( theString, "33", 6 ),
|
||||
// cb.overlay( theString, from.get( "theInt" ).as( String.class ), 6 ),
|
||||
cb.overlay( theString, ( (JpaExpression) from.get( "theInt" ) ).cast( String.class ), 6 ),
|
||||
cb.overlay( theString, "1234", from.get( "theInteger" ), 2 )
|
||||
).where( cb.equal( from.get( "id" ), 4 ) );
|
||||
|
||||
Tuple result = session.createQuery( query ).getSingleResult();
|
||||
assertEquals( "thirt33n", result.get( 0 ) );
|
||||
// assertEquals( "thirt13n", result.get( 1 ) );
|
||||
assertEquals( "thi1234een", result.get( 1 ) );
|
||||
assertEquals( "thirt13n", result.get( 1 ) );
|
||||
assertEquals( "thi1234een", result.get( 2 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -300,12 +301,12 @@ public class CriteriaBuilderNonStandardFunctionsTest {
|
|||
Expression<String> theString = from.get( "theString" );
|
||||
query.multiselect(
|
||||
cb.replace( theString, "thi", "12345" ),
|
||||
cb.replace( theString, "t", from.get( "theString" ) )
|
||||
cb.replace( theString, "t", ( (JpaExpression) from.get( "theInteger" ) ).cast( String.class ) )
|
||||
).where( cb.equal( from.get( "id" ), 4 ) );
|
||||
|
||||
Tuple result = session.createQuery( query ).getSingleResult();
|
||||
assertEquals( "12345rteen", result.get( 0 ) );
|
||||
assertEquals( "thirteenhirthirteeneen", result.get( 1 ) );
|
||||
assertEquals( "4hir4een", result.get( 1 ) );
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -94,3 +94,17 @@ You can find more details about embeddable inheritance in the dedicated link:{us
|
|||
== H2 database and bulk mutation strategy
|
||||
|
||||
With ORM 6.6 when a bulk mutation involves multiple tables, H2 dialect will make use of global temporary tables instead of local ones.
|
||||
|
||||
[[criteria-query]]
|
||||
== Criteria: `jakarta.persistence.criteria.Expression#as(Class)`
|
||||
|
||||
The behaviour of `jakarta.persistence.criteria.Expression#as(Class)` has been changed to conform to the Jakarta Persistence specification.
|
||||
|
||||
`Expression.as()` doesn’t do anymore a real type conversions, it’s just an unsafe typecast on the Expression object itself.
|
||||
|
||||
In order to perform an actual typecast, `org.hibernate.query.criteria.JpaExpression#cast(Class)` can be used.
|
||||
|
||||
E.g.
|
||||
```
|
||||
( (JpaExpression) from.get( "theInt" ) ).cast( String.class )
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue