initial working literal and parameter selection
This commit is contained in:
parent
5631a702a7
commit
26b08fd35e
|
@ -44,11 +44,13 @@ import org.hibernate.query.spi.QueryParameterBindings;
|
|||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||
import org.hibernate.query.spi.SelectQueryPlan;
|
||||
import org.hibernate.query.sqm.SqmExpressable;
|
||||
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
|
||||
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
|
||||
import org.hibernate.query.sqm.tree.SqmDmlStatement;
|
||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||
|
@ -199,20 +201,40 @@ public class QuerySqmImpl<R>
|
|||
|
||||
final SqmSelection sqmSelection = selections.get( 0 );
|
||||
|
||||
if ( ! resultClass.isAssignableFrom( sqmSelection.getNodeType().getExpressableJavaTypeDescriptor().getJavaType() ) ) {
|
||||
final String errorMessage = String.format(
|
||||
"Specified result type [%s] did not match Query selection type [%s] - multiple selections: use Tuple or array",
|
||||
resultClass.getName(),
|
||||
sqmSelection.getNodeType().getExpressableJavaTypeDescriptor().getJavaType().getName()
|
||||
);
|
||||
if ( sqmSelection.getSelectableNode() instanceof SqmParameter ) {
|
||||
final SqmParameter sqmParameter = (SqmParameter) sqmSelection.getSelectableNode();
|
||||
|
||||
if ( sessionFactory.getSessionFactoryOptions().getJpaCompliance().isJpaQueryComplianceEnabled() ) {
|
||||
throw new IllegalArgumentException( errorMessage );
|
||||
}
|
||||
else {
|
||||
throw new QueryTypeMismatchException( errorMessage );
|
||||
// we may not yet know a selection type
|
||||
if ( sqmParameter.getNodeType() == null || sqmParameter.getNodeType().getExpressableJavaTypeDescriptor() == null ) {
|
||||
// we can't verify the result type up front
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
verifyResultType( resultClass, sqmSelection.getNodeType(), sessionFactory );
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void verifyResultType(
|
||||
Class<T> resultClass,
|
||||
SqmExpressable<?> sqmExpressable,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
assert sqmExpressable != null;
|
||||
assert sqmExpressable.getExpressableJavaTypeDescriptor() != null;
|
||||
|
||||
if ( ! resultClass.isAssignableFrom( sqmExpressable.getExpressableJavaTypeDescriptor().getJavaType() ) ) {
|
||||
final String errorMessage = String.format(
|
||||
"Specified result type [%s] did not match Query selection type [%s] - multiple selections: use Tuple or array",
|
||||
resultClass.getName(),
|
||||
sqmExpressable.getExpressableJavaTypeDescriptor().getJavaType().getName()
|
||||
);
|
||||
|
||||
if ( sessionFactory.getSessionFactoryOptions().getJpaCompliance().isJpaQueryComplianceEnabled() ) {
|
||||
throw new IllegalArgumentException( errorMessage );
|
||||
}
|
||||
else {
|
||||
throw new QueryTypeMismatchException( errorMessage );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -220,6 +220,16 @@ public abstract class BaseSqmToSqlAstConverter
|
|||
return sqlAliasBaseManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainParameterXref getDomainParameterXref() {
|
||||
return domainParameterXref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getDomainParameterBindings() {
|
||||
return domainParameterBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LockMode determineLockMode(String identificationVariable) {
|
||||
return queryOptions.getLockOptions().getEffectiveLockMode( identificationVariable );
|
||||
|
@ -653,7 +663,17 @@ public abstract class BaseSqmToSqlAstConverter
|
|||
}
|
||||
|
||||
protected MappingModelExpressable<?> determineValueMapping(SqmExpression<?> sqmExpression) {
|
||||
final SqmExpressable<?> nodeType = sqmExpression.getNodeType();
|
||||
SqmExpressable<?> nodeType = sqmExpression.getNodeType();
|
||||
|
||||
if ( nodeType == null ) {
|
||||
if ( sqmExpression instanceof SqmParameter ) {
|
||||
final SqmParameter sqmParameter = (SqmParameter) sqmExpression;
|
||||
final QueryParameterBinding<?> binding = domainParameterBindings.getBinding( domainParameterXref.getQueryParameter( sqmParameter ) );
|
||||
assert binding != null;
|
||||
|
||||
nodeType = binding.getBindType();
|
||||
}
|
||||
}
|
||||
|
||||
MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable( nodeType );
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.query.sqm.sql;
|
|||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
|
@ -32,6 +34,10 @@ public interface SqlAstCreationState {
|
|||
|
||||
SqlAliasBaseGenerator getSqlAliasBaseGenerator();
|
||||
|
||||
DomainParameterXref getDomainParameterXref();
|
||||
|
||||
QueryParameterBindings getDomainParameterBindings();
|
||||
|
||||
LockMode determineLockMode(String identificationVariable);
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,16 +9,24 @@ package org.hibernate.query.sqm.sql.internal;
|
|||
import java.util.List;
|
||||
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.spi.QueryParameterBinding;
|
||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.sql.ast.spi.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqmParameterInterpretation implements Expression {
|
||||
public class SqmParameterInterpretation implements Expression, DomainResultProducer {
|
||||
private final SqmParameter sqmParameter;
|
||||
private final MappingModelExpressable valueMapping;
|
||||
|
||||
|
@ -48,4 +56,40 @@ public class SqmParameterInterpretation implements Expression {
|
|||
public MappingModelExpressable getExpressionType() {
|
||||
return valueMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult createDomainResult(
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
if ( resolvedExpression instanceof SqlTuple ) {
|
||||
throw new SemanticException( "Composite query parameter cannot be used in select" );
|
||||
}
|
||||
|
||||
AllowableParameterType nodeType = sqmParameter.getNodeType();
|
||||
if ( nodeType == null ) {
|
||||
final QueryParameterImplementor<?> queryParameter = creationState.getSqlAstCreationState()
|
||||
.getDomainParameterXref()
|
||||
.getQueryParameter( sqmParameter );
|
||||
final QueryParameterBinding<?> binding = creationState.getSqlAstCreationState()
|
||||
.getDomainParameterBindings()
|
||||
.getBinding( queryParameter );
|
||||
nodeType = binding.getBindType();
|
||||
}
|
||||
|
||||
final SqlSelection sqlSelection = creationState.getSqlAstCreationState().getSqlExpressionResolver().resolveSqlSelection(
|
||||
resolvedExpression,
|
||||
nodeType.getExpressableJavaTypeDescriptor(),
|
||||
creationState.getSqlAstCreationState()
|
||||
.getCreationContext()
|
||||
.getSessionFactory()
|
||||
.getTypeConfiguration()
|
||||
);
|
||||
|
||||
//noinspection unchecked
|
||||
return new BasicResult(
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
resultVariable,
|
||||
nodeType.getExpressableJavaTypeDescriptor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ public abstract class AbstractLiteral<T>
|
|||
|
||||
//noinspection unchecked
|
||||
return new BasicResult<>(
|
||||
sqlSelection.getJdbcResultSetIndex(),
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
resultVariable,
|
||||
type.getMappedTypeDescriptor().getMappedJavaTypeDescriptor()
|
||||
);
|
||||
|
|
|
@ -13,13 +13,16 @@ import java.util.function.Consumer;
|
|||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.mapping.SqlExpressable;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.exec.ExecutionException;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
@ -28,7 +31,7 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractJdbcParameter
|
||||
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressable {
|
||||
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressable, SqlExpressable {
|
||||
|
||||
private final JdbcMapping jdbcMapping;
|
||||
|
||||
|
@ -41,6 +44,29 @@ public abstract class AbstractJdbcParameter
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcMapping getJdbcMapping() {
|
||||
return jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlSelection createSqlSelection(
|
||||
int jdbcPosition,
|
||||
int valuesArrayPosition,
|
||||
JavaTypeDescriptor javaTypeDescriptor,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
// todo (6.0) : investigate "virtual" or "static" selections
|
||||
// - 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,
|
||||
this,
|
||||
jdbcMapping
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindParameterValue(
|
||||
PreparedStatement statement,
|
||||
|
|
|
@ -160,6 +160,28 @@ public class SmokeTests {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlSelectLiteral(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( "select 'items' from SimpleEntity e", String.class );
|
||||
final String attribute1 = query.uniqueResult();
|
||||
assertThat( attribute1, is( "items" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlSelectParameter(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( "select :param from SimpleEntity e", String.class );
|
||||
final String attribute1 = query.setParameter( "param", "items" ).uniqueResult();
|
||||
assertThat( attribute1, is( "items" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Dynamic instantiations
|
||||
|
@ -254,6 +276,26 @@ public class SmokeTests {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlNestedDynamicInstantiationWithLiteral(SessionFactoryScope scope) {
|
||||
scope.getSessionFactory().inTransaction(
|
||||
session -> {
|
||||
final Query<CategorizedListItemDto> query = session.createQuery(
|
||||
"select new CategorizedListItemDto( new ListItemDto( 'items', e.component.attribute1 ), e.component.attribute2, e.name ) from SimpleEntity e",
|
||||
CategorizedListItemDto.class
|
||||
);
|
||||
|
||||
final CategorizedListItemDto dto = query.getSingleResult();
|
||||
assertThat( dto, notNullValue() );
|
||||
assertThat( dto.category, notNullValue() );
|
||||
assertThat( dto.category.code, is( "items") );
|
||||
assertThat( dto.category.value, is( "a1") );
|
||||
assertThat( dto.code, is( "a2" ) );
|
||||
assertThat( dto.value, is( "Fab" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHqlMultipleDynamicInstantiation(SessionFactoryScope scope) {
|
||||
scope.getSessionFactory().inTransaction(
|
||||
|
@ -278,7 +320,7 @@ public class SmokeTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBasicSetterDynamicInstantiation(SessionFactoryScope scope) {
|
||||
public void testHqlBasicSetterDynamicInstantiation(SessionFactoryScope scope) {
|
||||
scope.getSessionFactory().inTransaction(
|
||||
session -> {
|
||||
final Query<BasicSetterBasedDto> query = session.createQuery(
|
||||
|
|
Loading…
Reference in New Issue