Initial working support for building and executing JdbcSelect operation from simple HQL for a converted (enum) value
This commit is contained in:
parent
dbd108e0b7
commit
5b1df3c6c9
|
@ -104,7 +104,8 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu
|
|||
sqlSelection.getValuesArrayPosition(),
|
||||
resultVariable,
|
||||
getMappedTypeDescriptor().getMappedJavaTypeDescriptor(),
|
||||
valueConverter
|
||||
valueConverter,
|
||||
navigablePath
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,4 +67,11 @@ public class BasicResultAssembler<J> implements DomainResultAssembler<J> {
|
|||
public JavaTypeDescriptor<J> getAssembledJavaTypeDescriptor() {
|
||||
return assembledJavaTypeDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposed for testing purposes
|
||||
*/
|
||||
public BasicValueConverter<J, ?> getValueConverter() {
|
||||
return valueConverter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.sql.results.internal;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.results.spi.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.sql.results.spi.Initializer;
|
||||
|
@ -22,15 +23,27 @@ public class ScalarDomainResultImpl<T> implements ScalarDomainResult<T> {
|
|||
private final String resultVariable;
|
||||
private final JavaTypeDescriptor<T> javaTypeDescriptor;
|
||||
|
||||
private final NavigablePath navigablePath;
|
||||
|
||||
private final DomainResultAssembler<T> assembler;
|
||||
|
||||
public ScalarDomainResultImpl(
|
||||
int jdbcValuesArrayPosition,
|
||||
String resultVariable,
|
||||
JavaTypeDescriptor<T> javaTypeDescriptor) {
|
||||
this( jdbcValuesArrayPosition, resultVariable, javaTypeDescriptor, (NavigablePath) null );
|
||||
}
|
||||
|
||||
public ScalarDomainResultImpl(
|
||||
int jdbcValuesArrayPosition,
|
||||
String resultVariable,
|
||||
JavaTypeDescriptor<T> javaTypeDescriptor,
|
||||
NavigablePath navigablePath) {
|
||||
this.resultVariable = resultVariable;
|
||||
this.javaTypeDescriptor = javaTypeDescriptor;
|
||||
|
||||
this.navigablePath = navigablePath;
|
||||
|
||||
this.assembler = new BasicResultAssembler<>( jdbcValuesArrayPosition, javaTypeDescriptor );
|
||||
}
|
||||
|
||||
|
@ -39,8 +52,18 @@ public class ScalarDomainResultImpl<T> implements ScalarDomainResult<T> {
|
|||
String resultVariable,
|
||||
JavaTypeDescriptor<T> javaTypeDescriptor,
|
||||
BasicValueConverter<T,?> valueConverter) {
|
||||
this( valuesArrayPosition, resultVariable, javaTypeDescriptor, valueConverter, null );
|
||||
}
|
||||
|
||||
public ScalarDomainResultImpl(
|
||||
int valuesArrayPosition,
|
||||
String resultVariable,
|
||||
JavaTypeDescriptor<T> javaTypeDescriptor,
|
||||
BasicValueConverter<T,?> valueConverter,
|
||||
NavigablePath navigablePath) {
|
||||
this.resultVariable = resultVariable;
|
||||
this.javaTypeDescriptor = javaTypeDescriptor;
|
||||
this.navigablePath = navigablePath;
|
||||
|
||||
this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaTypeDescriptor, valueConverter );
|
||||
}
|
||||
|
@ -55,6 +78,11 @@ public class ScalarDomainResultImpl<T> implements ScalarDomainResult<T> {
|
|||
return javaTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigablePath getNavigablePath() {
|
||||
return navigablePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResultAssembler<T> createResultAssembler(
|
||||
Consumer<Initializer> initializerCollector,
|
||||
|
|
|
@ -14,11 +14,9 @@ import java.sql.SQLException;
|
|||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
|
@ -30,7 +28,6 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
|
|||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
import org.hibernate.usertype.EnhancedUserType;
|
||||
import org.hibernate.usertype.LoggableUserType;
|
||||
import org.hibernate.usertype.ParameterizedType;
|
||||
import org.hibernate.usertype.Sized;
|
||||
import org.hibernate.usertype.UserType;
|
||||
import org.hibernate.usertype.UserVersionType;
|
||||
|
@ -44,7 +41,7 @@ import org.hibernate.usertype.UserVersionType;
|
|||
*/
|
||||
public class CustomType
|
||||
extends AbstractType
|
||||
implements IdentifierType, DiscriminatorType, VersionType, BasicType, StringRepresentableType, ProcedureParameterNamedBinder, ProcedureParameterExtractionAware {
|
||||
implements BasicType, IdentifierType, DiscriminatorType, VersionType, StringRepresentableType, ProcedureParameterNamedBinder, ProcedureParameterExtractionAware {
|
||||
|
||||
private final UserType userType;
|
||||
private final String[] registrationKeys;
|
||||
|
@ -90,8 +87,8 @@ public class CustomType
|
|||
}
|
||||
|
||||
@Override
|
||||
public String[] getRegistrationKeys() {
|
||||
return registrationKeys;
|
||||
public SqlTypeDescriptor getSqlTypeDescriptor() {
|
||||
return sqlTypeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -99,6 +96,11 @@ public class CustomType
|
|||
return new int[] { sqlTypeDescriptor.getSqlType() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRegistrationKeys() {
|
||||
return registrationKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size[] dictatedSizes(Mapping mapping) throws MappingException {
|
||||
return new Size[] {dictatedSize};
|
||||
|
@ -271,13 +273,13 @@ public class CustomType
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public String toString(Object value) throws HibernateException {
|
||||
if ( StringRepresentableType.class.isInstance( getUserType() ) ) {
|
||||
if ( getUserType() instanceof StringRepresentableType ) {
|
||||
return ( (StringRepresentableType) getUserType() ).toString( value );
|
||||
}
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( EnhancedUserType.class.isInstance( getUserType() ) ) {
|
||||
if ( getUserType() instanceof EnhancedUserType ) {
|
||||
//noinspection deprecation
|
||||
return ( (EnhancedUserType) getUserType() ).toXMLString( value );
|
||||
}
|
||||
|
@ -286,10 +288,10 @@ public class CustomType
|
|||
|
||||
@Override
|
||||
public Object fromStringValue(String string) throws HibernateException {
|
||||
if ( StringRepresentableType.class.isInstance( getUserType() ) ) {
|
||||
if ( getUserType() instanceof StringRepresentableType ) {
|
||||
return ( (StringRepresentableType) getUserType() ).fromStringValue( string );
|
||||
}
|
||||
if ( EnhancedUserType.class.isInstance( getUserType() ) ) {
|
||||
if ( getUserType() instanceof EnhancedUserType ) {
|
||||
//noinspection deprecation
|
||||
return ( (EnhancedUserType) getUserType() ).fromXMLString( string );
|
||||
}
|
||||
|
@ -305,7 +307,7 @@ public class CustomType
|
|||
|
||||
@Override
|
||||
public boolean canDoSetting() {
|
||||
if ( ProcedureParameterNamedBinder.class.isInstance( getUserType() ) ) {
|
||||
if ( getUserType() instanceof ProcedureParameterNamedBinder ) {
|
||||
return ((ProcedureParameterNamedBinder) getUserType() ).canDoSetting();
|
||||
}
|
||||
return false;
|
||||
|
@ -326,17 +328,12 @@ public class CustomType
|
|||
|
||||
@Override
|
||||
public boolean canDoExtraction() {
|
||||
if ( ProcedureParameterExtractionAware.class.isInstance( getUserType() ) ) {
|
||||
if ( getUserType() instanceof ProcedureParameterExtractionAware ) {
|
||||
return ((ProcedureParameterExtractionAware) getUserType() ).canDoExtraction();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlTypeDescriptor getSqlTypeDescriptor() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(CallableStatement statement, int startIndex, SharedSessionContractImplementor session) throws SQLException {
|
||||
if ( canDoExtraction() ) {
|
||||
|
|
|
@ -90,6 +90,10 @@ public class EnumType<T extends Enum>
|
|||
this.typeConfiguration = typeConfiguration;
|
||||
}
|
||||
|
||||
public EnumValueConverter getEnumValueConverter() {
|
||||
return enumValueConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameterValues(Properties parameters) {
|
||||
// IMPL NOTE: we handle 2 distinct cases here:
|
||||
|
|
|
@ -7,19 +7,34 @@
|
|||
package org.hibernate.orm.test.sql.ast;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
|
||||
import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
|
||||
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.hql.spi.HqlQueryImplementor;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.query.sqm.internal.QuerySqmImpl;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmSelectToSqlAstConverter;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.sql.ast.spi.SqlAstSelectToJdbcSelectConverter;
|
||||
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.ast.tree.from.FromClause;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||
import org.hibernate.sql.results.internal.BasicResultAssembler;
|
||||
import org.hibernate.sql.results.internal.ScalarDomainResultImpl;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.spi.DomainResult;
|
||||
import org.hibernate.sql.results.spi.DomainResultAssembler;
|
||||
import org.hibernate.type.CustomType;
|
||||
import org.hibernate.type.EnumType;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
|
@ -27,6 +42,8 @@ import org.hibernate.testing.orm.junit.SessionFactory;
|
|||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -47,7 +64,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
@SessionFactory
|
||||
public class SmokeTests {
|
||||
@Test
|
||||
public void testSimpleHql(SessionFactoryScope scope) {
|
||||
public void testSimpleHqlInterpretation(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( "select e.name from SimpleEntity e", String.class );
|
||||
|
@ -66,17 +83,32 @@ public class SmokeTests {
|
|||
final SqmSelectInterpretation sqmInterpretation = sqmConverter.interpret( sqmStatement );
|
||||
final SelectStatement sqlAst = sqmInterpretation.getSqlAst();
|
||||
|
||||
checkSqmInterpretation( sqlAst );
|
||||
final FromClause fromClause = sqlAst.getQuerySpec().getFromClause();
|
||||
assertThat( fromClause.getRoots().size(), is( 1 ) );
|
||||
|
||||
final JdbcSelect jdbcSelectOperation = SqlAstSelectToJdbcSelectConverter.interpret(
|
||||
sqlAst,
|
||||
session.getSessionFactory()
|
||||
);
|
||||
final TableGroup rootTableGroup = fromClause.getRoots().get( 0 );
|
||||
assertThat( rootTableGroup.getPrimaryTableReference(), notNullValue() );
|
||||
assertThat( rootTableGroup.getPrimaryTableReference().getTableExpression(), is( "mapping_simple_entity" ) );
|
||||
|
||||
assertThat( jdbcSelectOperation.getSql(), is( "select s1_0.name from mapping_simple_entity as s1_0" ) );
|
||||
assertThat( rootTableGroup.getTableReferenceJoins().size(), is( 0 ) );
|
||||
|
||||
assertThat( rootTableGroup.hasTableGroupJoins(), is( false ) );
|
||||
|
||||
|
||||
// `s` is the "alias stem" for `SimpleEntity` and as it is the first entity with that stem in
|
||||
// the query the base becomes `s1`. The primary table reference is always suffixed as `_0`
|
||||
assertThat( rootTableGroup.getPrimaryTableReference().getIdentificationVariable(), is( "s1_0" ) );
|
||||
|
||||
final SelectClause selectClause = sqlAst.getQuerySpec().getSelectClause();
|
||||
assertThat( selectClause.getSqlSelections().size(), is( 1 ) ) ;
|
||||
final SqlSelection sqlSelection = selectClause.getSqlSelections().get( 0 );
|
||||
assertThat( sqlSelection.getJdbcResultSetIndex(), is( 1 ) );
|
||||
assertThat( sqlSelection.getValuesArrayPosition(), is( 0 ) );
|
||||
assertThat( sqlSelection.getJdbcValueExtractor(), notNullValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleHqlExecution(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
|
@ -86,29 +118,96 @@ public class SmokeTests {
|
|||
}
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void testConvertedHqlInterpretation(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Gender> query = session.createQuery( "select e.gender from SimpleEntity e", Gender.class );
|
||||
final HqlQueryImplementor<Gender> hqlQuery = (HqlQueryImplementor<Gender>) query;
|
||||
//noinspection unchecked
|
||||
final SqmSelectStatement<Gender> sqmStatement = (SqmSelectStatement<Gender>) hqlQuery.getSqmStatement();
|
||||
|
||||
private void checkSqmInterpretation(SelectStatement sqlAst) {
|
||||
final FromClause fromClause = sqlAst.getQuerySpec().getFromClause();
|
||||
assertThat( fromClause.getRoots().size(), is( 1 ) );
|
||||
final SqmSelectToSqlAstConverter sqmConverter = new SqmSelectToSqlAstConverter(
|
||||
hqlQuery.getQueryOptions(),
|
||||
( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(),
|
||||
query.getParameterBindings(),
|
||||
session.getLoadQueryInfluencers(),
|
||||
scope.getSessionFactory()
|
||||
);
|
||||
|
||||
final TableGroup rootTableGroup = fromClause.getRoots().get( 0 );
|
||||
assertThat( rootTableGroup.getPrimaryTableReference(), notNullValue() );
|
||||
assertThat( rootTableGroup.getPrimaryTableReference().getTableExpression(), is( "mapping_simple_entity" ) );
|
||||
final SqmSelectInterpretation sqmInterpretation = sqmConverter.interpret( sqmStatement );
|
||||
final SelectStatement sqlAst = sqmInterpretation.getSqlAst();
|
||||
|
||||
assertThat( rootTableGroup.getTableReferenceJoins().size(), is( 0 ) );
|
||||
final FromClause fromClause = sqlAst.getQuerySpec().getFromClause();
|
||||
assertThat( fromClause.getRoots().size(), is( 1 ) );
|
||||
|
||||
assertThat( rootTableGroup.hasTableGroupJoins(), is( false ) );
|
||||
final TableGroup rootTableGroup = fromClause.getRoots().get( 0 );
|
||||
assertThat( rootTableGroup.getPrimaryTableReference(), notNullValue() );
|
||||
assertThat( rootTableGroup.getPrimaryTableReference().getTableExpression(), is( "mapping_simple_entity" ) );
|
||||
|
||||
assertThat( rootTableGroup.getTableReferenceJoins().size(), is( 0 ) );
|
||||
|
||||
assertThat( rootTableGroup.hasTableGroupJoins(), is( false ) );
|
||||
|
||||
|
||||
// `s` is the "alias stem" for `SimpleEntity` and as it is the first entity with that stem in
|
||||
// the query the base becomes `s1`. The primary table reference is always suffixed as `_0`
|
||||
assertThat( rootTableGroup.getPrimaryTableReference().getIdentificationVariable(), is( "s1_0" ) );
|
||||
// `s` is the "alias stem" for `SimpleEntity` and as it is the first entity with that stem in
|
||||
// the query the base becomes `s1`. The primary table reference is always suffixed as `_0`
|
||||
assertThat( rootTableGroup.getPrimaryTableReference().getIdentificationVariable(), is( "s1_0" ) );
|
||||
|
||||
final SelectClause selectClause = sqlAst.getQuerySpec().getSelectClause();
|
||||
assertThat( selectClause.getSqlSelections().size(), is( 1 ) ) ;
|
||||
final SqlSelection sqlSelection = selectClause.getSqlSelections().get( 0 );
|
||||
assertThat( sqlSelection.getJdbcResultSetIndex(), is( 1 ) );
|
||||
assertThat( sqlSelection.getValuesArrayPosition(), is( 0 ) );
|
||||
assertThat( sqlSelection.getJdbcValueExtractor(), notNullValue() );
|
||||
final SelectClause selectClause = sqlAst.getQuerySpec().getSelectClause();
|
||||
assertThat( selectClause.getSqlSelections().size(), is( 1 ) ) ;
|
||||
|
||||
final SqlSelection sqlSelection = selectClause.getSqlSelections().get( 0 );
|
||||
assertThat( sqlSelection.getJdbcResultSetIndex(), is( 1 ) );
|
||||
assertThat( sqlSelection.getValuesArrayPosition(), is( 0 ) );
|
||||
assertThat( sqlSelection.getJdbcValueExtractor(), notNullValue() );
|
||||
|
||||
assertThat( sqlSelection, instanceOf( SqlSelectionImpl.class ) );
|
||||
final Expression selectedExpression = ( (SqlSelectionImpl) sqlSelection ).getWrappedSqlExpression();
|
||||
assertThat( selectedExpression, instanceOf( ColumnReference.class ) );
|
||||
final ColumnReference columnReference = (ColumnReference) selectedExpression;
|
||||
assertThat( columnReference.getReferencedColumnExpression(), is( "gender" ) );
|
||||
assertThat( columnReference.renderSqlFragment( scope.getSessionFactory() ), is( "s1_0.gender" ) );
|
||||
|
||||
final MappingModelExpressable selectedExpressable = selectedExpression.getExpressionType();
|
||||
assertThat( selectedExpressable, instanceOf( CustomType.class ) );
|
||||
final UserType userType = ( (CustomType) selectedExpressable ).getUserType();
|
||||
assertThat( userType, instanceOf( EnumType.class ) );
|
||||
final EnumValueConverter enumValueConverter = ( (EnumType) userType ).getEnumValueConverter();
|
||||
assertThat( enumValueConverter, notNullValue() );
|
||||
assertThat( enumValueConverter.getDomainJavaDescriptor().getJavaType(), equalTo( Gender.class ) );
|
||||
|
||||
assertThat( sqlAst.getDomainResultDescriptors().size(), is( 1 ) );
|
||||
final DomainResult domainResult = sqlAst.getDomainResultDescriptors().get( 0 );
|
||||
final NavigablePath expectedSelectedPath = new NavigablePath(
|
||||
org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity.class.getName(),
|
||||
"e"
|
||||
).append( "gender" );
|
||||
assertThat( domainResult.getNavigablePath(), equalTo( expectedSelectedPath ) );
|
||||
assertThat( domainResult, instanceOf( ScalarDomainResultImpl.class ) );
|
||||
|
||||
// ScalarDomainResultImpl creates and caches the assembler at its creation.
|
||||
// this just gets access to that cached one
|
||||
final DomainResultAssembler resultAssembler = domainResult.createResultAssembler(
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
assertThat( resultAssembler, instanceOf( BasicResultAssembler.class ) );
|
||||
final BasicValueConverter valueConverter = ( (BasicResultAssembler) resultAssembler ).getValueConverter();
|
||||
assertThat( valueConverter, notNullValue() );
|
||||
assertThat( valueConverter, instanceOf( OrdinalEnumValueConverter.class ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertedHqlExecution(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Gender> query = session.createQuery( "select e.gender from SimpleEntity e", Gender.class );
|
||||
query.list();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue