initial working literal and parameter selection

This commit is contained in:
Steve Ebersole 2019-09-19 15:59:59 -05:00
parent 5631a702a7
commit 26b08fd35e
7 changed files with 176 additions and 16 deletions

View File

@ -44,11 +44,13 @@ import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.spi.SelectQueryPlan; 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.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.UpdateHandler; import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; 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.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
@ -199,20 +201,40 @@ public class QuerySqmImpl<R>
final SqmSelection sqmSelection = selections.get( 0 ); final SqmSelection sqmSelection = selections.get( 0 );
if ( ! resultClass.isAssignableFrom( sqmSelection.getNodeType().getExpressableJavaTypeDescriptor().getJavaType() ) ) { if ( sqmSelection.getSelectableNode() instanceof SqmParameter ) {
final String errorMessage = String.format( final SqmParameter sqmParameter = (SqmParameter) sqmSelection.getSelectableNode();
"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 ( sessionFactory.getSessionFactoryOptions().getJpaCompliance().isJpaQueryComplianceEnabled() ) { // we may not yet know a selection type
throw new IllegalArgumentException( errorMessage ); if ( sqmParameter.getNodeType() == null || sqmParameter.getNodeType().getExpressableJavaTypeDescriptor() == null ) {
} // we can't verify the result type up front
else { return;
throw new QueryTypeMismatchException( errorMessage );
} }
} }
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 );
}
} }
} }

View File

@ -220,6 +220,16 @@ public abstract class BaseSqmToSqlAstConverter
return sqlAliasBaseManager; return sqlAliasBaseManager;
} }
@Override
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
@Override
public QueryParameterBindings getDomainParameterBindings() {
return domainParameterBindings;
}
@Override @Override
public LockMode determineLockMode(String identificationVariable) { public LockMode determineLockMode(String identificationVariable) {
return queryOptions.getLockOptions().getEffectiveLockMode( identificationVariable ); return queryOptions.getLockOptions().getEffectiveLockMode( identificationVariable );
@ -653,7 +663,17 @@ public abstract class BaseSqmToSqlAstConverter
} }
protected MappingModelExpressable<?> determineValueMapping(SqmExpression<?> sqmExpression) { 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 ); MappingModelExpressable valueMapping = getCreationContext().getDomainModel().resolveMappingExpressable( nodeType );

View File

@ -9,6 +9,8 @@ package org.hibernate.query.sqm.sql;
import java.util.List; import java.util.List;
import org.hibernate.LockMode; 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.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -32,6 +34,10 @@ public interface SqlAstCreationState {
SqlAliasBaseGenerator getSqlAliasBaseGenerator(); SqlAliasBaseGenerator getSqlAliasBaseGenerator();
DomainParameterXref getDomainParameterXref();
QueryParameterBindings getDomainParameterBindings();
LockMode determineLockMode(String identificationVariable); LockMode determineLockMode(String identificationVariable);
/** /**

View File

@ -9,16 +9,24 @@ package org.hibernate.query.sqm.sql.internal;
import java.util.List; import java.util.List;
import org.hibernate.metamodel.mapping.MappingModelExpressable; 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.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.spi.SqlAstWalker; 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.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.exec.spi.JdbcParameter; 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 * @author Steve Ebersole
*/ */
public class SqmParameterInterpretation implements Expression { public class SqmParameterInterpretation implements Expression, DomainResultProducer {
private final SqmParameter sqmParameter; private final SqmParameter sqmParameter;
private final MappingModelExpressable valueMapping; private final MappingModelExpressable valueMapping;
@ -48,4 +56,40 @@ public class SqmParameterInterpretation implements Expression {
public MappingModelExpressable getExpressionType() { public MappingModelExpressable getExpressionType() {
return valueMapping; 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()
);
}
} }

View File

@ -76,7 +76,7 @@ public abstract class AbstractLiteral<T>
//noinspection unchecked //noinspection unchecked
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getJdbcResultSetIndex(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
type.getMappedTypeDescriptor().getMappedJavaTypeDescriptor() type.getMappedTypeDescriptor().getMappedJavaTypeDescriptor()
); );

View File

@ -13,13 +13,16 @@ import java.util.function.Consumer;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.SqlExpressable;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.exec.ExecutionException; import org.hibernate.sql.exec.ExecutionException;
import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameter; import org.hibernate.sql.exec.spi.JdbcParameter;
import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.sql.exec.spi.JdbcParameterBinding; import org.hibernate.sql.exec.spi.JdbcParameterBinding;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -28,7 +31,7 @@ import org.hibernate.type.spi.TypeConfiguration;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractJdbcParameter public abstract class AbstractJdbcParameter
implements JdbcParameter, JdbcParameterBinder, MappingModelExpressable { implements JdbcParameter, JdbcParameterBinder, MappingModelExpressable, SqlExpressable {
private final JdbcMapping jdbcMapping; private final JdbcMapping jdbcMapping;
@ -41,6 +44,29 @@ public abstract class AbstractJdbcParameter
return this; 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 @Override
public void bindParameterValue( public void bindParameterValue(
PreparedStatement statement, PreparedStatement statement,

View File

@ -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 // 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 @Test
public void testHqlMultipleDynamicInstantiation(SessionFactoryScope scope) { public void testHqlMultipleDynamicInstantiation(SessionFactoryScope scope) {
scope.getSessionFactory().inTransaction( scope.getSessionFactory().inTransaction(
@ -278,7 +320,7 @@ public class SmokeTests {
} }
@Test @Test
public void testBasicSetterDynamicInstantiation(SessionFactoryScope scope) { public void testHqlBasicSetterDynamicInstantiation(SessionFactoryScope scope) {
scope.getSessionFactory().inTransaction( scope.getSessionFactory().inTransaction(
session -> { session -> {
final Query<BasicSetterBasedDto> query = session.createQuery( final Query<BasicSetterBasedDto> query = session.createQuery(