HHH-15133 - Use specified result-type to better infer "shape" of query results with implicit selections

This commit is contained in:
Steve Ebersole 2022-03-19 08:22:55 -05:00
parent cb4691e98e
commit cac18ae0c7
34 changed files with 430 additions and 164 deletions

View File

@ -658,6 +658,10 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
@Override
public SelectionQuery<?> createSelectionQuery(String hqlString) {
return internalCreateSelectionQuery( hqlString, null );
}
private <R> SelectionQuery<R> internalCreateSelectionQuery(String hqlString, Class<R> expectedResultType) {
checkOpen();
pulseTransactionCoordinator();
delayedAfterCompletion();
@ -667,19 +671,40 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache();
final HqlInterpretation hqlInterpretation = interpretationCache.resolveHqlInterpretation(
hqlString,
s -> queryEngine.getHqlTranslator().translate( hqlString )
expectedResultType,
(s) -> queryEngine.getHqlTranslator().translate( hqlString, expectedResultType )
);
if ( !( hqlInterpretation.getSqmStatement() instanceof SqmSelectStatement ) ) {
throw new IllegalSelectQueryException( "Expecting a selection query, but found `" + hqlString + "`", hqlString );
}
final SqmSelectionQuery<?> query = new SqmSelectionQueryImpl<>( hqlString, hqlInterpretation, this );
query.setComment( hqlString );
final SqmSelectionQueryImpl<?> query = new SqmSelectionQueryImpl<>(
hqlString,
hqlInterpretation,
expectedResultType,
this
);
if ( expectedResultType != null ) {
final Class<?> resultType = query.getResultType();
if ( ! expectedResultType.isAssignableFrom( resultType ) ) {
throw new QueryTypeMismatchException(
String.format(
Locale.ROOT,
"Query result-type error - expecting `%s`, but found `%s`",
expectedResultType.getName(),
resultType.getName()
)
);
}
}
query.setComment( hqlString );
applyQuerySettingsAndHints( query );
return query;
//noinspection unchecked
return (SelectionQuery<R>) query;
}
catch (RuntimeException e) {
markForRollbackOnly();
@ -689,32 +714,17 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
@Override
public <R> SelectionQuery<R> createSelectionQuery(String hqlString, Class<R> expectedResultType) {
final SelectionQuery<?> selectQuery = createSelectionQuery( hqlString );
//noinspection unchecked
final Class<?> resultType = ( (SqmSelectionQueryImpl<R>) selectQuery ).getResultType();
if ( resultType == null || expectedResultType.isAssignableFrom( resultType ) ) {
//noinspection unchecked
return (SelectionQuery<R>) selectQuery;
}
throw new QueryTypeMismatchException(
String.format(
Locale.ROOT,
"Query result-type error - expecting `%s`, but found `%s`",
expectedResultType.getName(),
resultType.getName()
)
);
return internalCreateSelectionQuery( hqlString, expectedResultType );
}
@Override
public <R> SelectionQuery<R> createSelectionQuery(CriteriaQuery<R> criteria) {
SqmUtil.verifyIsSelectStatement( (SqmStatement<?>) criteria, null );
return new SqmSelectionQueryImpl<>( (SqmSelectStatement<R>) criteria, this );
return new SqmSelectionQueryImpl<>( (SqmSelectStatement<R>) criteria, criteria.getResultType(), this );
}
@Override
public <T> QueryImplementor<T> createQuery(String queryString, Class<T> resultClass) {
public <T> QueryImplementor<T> createQuery(String queryString, Class<T> expectedResultType) {
checkOpen();
pulseTransactionCoordinator();
delayedAfterCompletion();
@ -727,9 +737,10 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
queryString,
interpretationCache.resolveHqlInterpretation(
queryString,
s -> queryEngine.getHqlTranslator().translate( queryString )
expectedResultType,
s -> queryEngine.getHqlTranslator().translate( queryString, expectedResultType )
),
resultClass,
expectedResultType,
this
);

View File

@ -34,7 +34,7 @@ import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
/**
@ -171,7 +171,7 @@ public class CollectionElementLoaderByIndex implements Loader {
}
},
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);

View File

@ -35,7 +35,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.jboss.logging.Logger;
@ -233,7 +233,7 @@ public class CollectionLoaderBatchKey implements CollectionLoader {
}
},
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);

View File

@ -33,7 +33,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
/**
@ -158,7 +158,7 @@ public class CollectionLoaderSingleKey implements CollectionLoader {
}
},
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);

View File

@ -34,7 +34,7 @@ import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.ResultsHelper;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
/**
@ -159,7 +159,7 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader {
}
},
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);

View File

@ -47,7 +47,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.jboss.logging.Logger;
@ -331,7 +331,7 @@ public class MultiIdLoaderStandard<T> implements MultiIdEntityLoader<T> {
}
}
},
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);
}

View File

@ -33,7 +33,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
/**
@ -197,7 +197,7 @@ public class MultiNaturalIdLoadingBatcher {
}
}
},
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);
}

View File

@ -32,7 +32,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.jboss.logging.Logger;
@ -151,7 +151,7 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
session,
subSelectFetchableKeysHandler
),
RowTransformerPassThruImpl.instance(),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
);

View File

@ -29,7 +29,7 @@ import org.hibernate.sql.exec.spi.Callback;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.sql.results.spi.RowTransformer;
@ -91,7 +91,7 @@ public class SingleIdLoadPlan<T> implements SingleEntityLoadPlan {
}
protected RowTransformer<T> getRowTransformer() {
return RowTransformerPassThruImpl.instance();
return RowTransformerStandardImpl.instance();
}
public T load(Object restrictedValue, SharedSessionContractImplementor session) {

View File

@ -25,10 +25,11 @@ public interface HqlTranslator {
* Performs the interpretation of a HQL/JPQL query string to SQM.
*
* @param hql The HQL/JPQL query string to interpret
* @param expectedResultType The type specified when creating the query
*
* @return The semantic representation of the incoming query.
*/
<R> SqmStatement<R> translate(String hql);
<R> SqmStatement<R> translate(String hql, Class<R> expectedResultType);
/**
* Give the translator a chance to "shut down" if it needs to

View File

@ -127,7 +127,7 @@ public class NamedHqlQueryMementoImpl extends AbstractNamedQueryMemento implemen
@Override
public void validate(QueryEngine queryEngine) {
queryEngine.getHqlTranslator().translate( hqlString );
queryEngine.getHqlTranslator().translate( hqlString, null );
}
@Override

View File

@ -31,6 +31,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.metamodel.Bindable;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.QueryException;
@ -193,9 +196,6 @@ import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.jboss.logging.Logger;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.metamodel.Bindable;
import jakarta.persistence.metamodel.SingularAttribute;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
@ -269,16 +269,18 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
/**
* Main entry point into analysis of HQL/JPQL parse tree - producing a semantic model of the
* query.
* Main entry point into analysis of HQL/JPQL parse tree - producing
* a semantic model of the query.
*/
public static <R> SqmStatement<R> buildSemanticModel(
HqlParser.StatementContext hqlParseTree,
Class<R> expectedResultType,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext) {
return new SemanticQueryBuilder<R>( creationOptions, creationContext ).visitStatement( hqlParseTree );
return new SemanticQueryBuilder<R>( expectedResultType, creationOptions, creationContext ).visitStatement( hqlParseTree );
}
private final Class<R> expectedResultType;
private final SqmCreationOptions creationOptions;
private final SqmCreationContext creationContext;
@ -294,7 +296,11 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
private ParameterCollector parameterCollector;
private ParameterStyle parameterStyle;
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
public SemanticQueryBuilder(
Class<R> expectedResultType,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext) {
this.expectedResultType = expectedResultType;
this.creationOptions = creationOptions;
this.creationContext = creationContext;
this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) );
@ -868,20 +874,42 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
// for now, this is slightly different than the legacy behavior where
// the root and each non-fetched-join was selected. For now, here, we simply
// select the root
final SqmSelectClause selectClause = new SqmSelectClause(
false,
fromClause.getNumberOfRoots(),
creationContext.getNodeBuilder()
);
final SqmSelectClause selectClause;
final boolean expectingArray = expectedResultType != null && expectedResultType.isArray();
if ( expectingArray ) {
// triggers legacy interpretation of returning all roots
// and non-fetched joins
selectClause = new SqmSelectClause(
false,
creationContext.getNodeBuilder()
);
}
else {
selectClause = new SqmSelectClause(
false,
fromClause.getNumberOfRoots(),
creationContext.getNodeBuilder()
);
}
fromClause.visitRoots( (sqmRoot) -> {
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), creationContext.getNodeBuilder() ) );
if ( expectingArray ) {
applyJoinsToInferredSelectClause( sqmRoot, selectClause );
}
} );
fromClause.visitRoots(
sqmRoot -> selectClause.addSelection(
new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), creationContext.getNodeBuilder() )
)
);
return selectClause;
}
private void applyJoinsToInferredSelectClause(SqmFrom<?,?> sqm, SqmSelectClause selectClause) {
sqm.visitSqmJoins( (sqmJoin) -> {
selectClause.addSelection( new SqmSelection<>( sqmJoin, sqmJoin.getAlias(), creationContext.getNodeBuilder() ) );
applyJoinsToInferredSelectClause( sqmJoin, selectClause );
} );
}
@Override
public SqmSelectClause visitSelectClause(HqlParser.SelectClauseContext ctx) {
// todo (6.0) : primer a select-clause-specific SemanticPathPart into the stack

View File

@ -13,12 +13,12 @@ import org.hibernate.grammars.hql.HqlLexer;
import org.hibernate.grammars.hql.HqlParser;
import org.hibernate.query.SemanticException;
import org.hibernate.query.hql.HqlLogging;
import org.hibernate.query.sqm.InterpretationException;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.hql.spi.SqmCreationOptions;
import org.hibernate.query.sqm.InterpretationException;
import org.hibernate.query.sqm.ParsingException;
import org.hibernate.query.sqm.internal.SqmTreePrinter;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.hql.spi.SqmCreationOptions;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.antlr.v4.runtime.ANTLRErrorListener;
@ -71,7 +71,7 @@ public class StandardHqlTranslator implements HqlTranslator {
}
@Override
public <R> SqmStatement<R> translate(String query) {
public <R> SqmStatement<R> translate(String query, Class<R> expectedResultType) {
HqlLogging.QUERY_LOGGER.debugf( "HQL : " + query );
final HqlParser.StatementContext hqlParseTree = parseHql( query );
@ -80,6 +80,7 @@ public class StandardHqlTranslator implements HqlTranslator {
try {
final SqmStatement<R> sqmStatement = SemanticQueryBuilder.buildSemanticModel(
hqlParseTree,
expectedResultType,
sqmCreationOptions,
sqmCreationContext
);

View File

@ -18,7 +18,6 @@ import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.named.NamedObjectRepository;
import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.named.NamedResultSetMappingMemento;
@ -212,9 +211,7 @@ public class NamedObjectRepositoryImpl implements NamedObjectRepository {
public Map<String, HibernateException> checkNamedQueries(QueryEngine queryEngine) {
Map<String,HibernateException> errors = new HashMap<>();
final HqlTranslator sqmProducer = queryEngine.getHqlTranslator();
final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache();
final boolean cachingEnabled = interpretationCache.isEnabled();
// Check named HQL queries
log.debugf( "Checking %s named HQL queries", sqmMementoMap.size() );
@ -224,7 +221,8 @@ public class NamedObjectRepositoryImpl implements NamedObjectRepository {
String queryString = hqlMemento.getHqlString();
interpretationCache.resolveHqlInterpretation(
queryString,
s -> queryEngine.getHqlTranslator().translate( queryString )
null,
s -> queryEngine.getHqlTranslator().translate( queryString, null )
);
}
catch ( HibernateException e ) {

View File

@ -60,7 +60,7 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
}
@Override
public HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator) {
public HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, Function<String, SqmStatement<?>> creator) {
final StatisticsImplementor statistics = statisticsSupplier.get();
final boolean stats = statistics.isStatisticsEnabled();
final long startTime = ( stats ) ? System.nanoTime() : 0L;

View File

@ -101,14 +101,23 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
@Override
public HqlInterpretation resolveHqlInterpretation(
String queryString,
Class<?> expectedResultType,
Function<String, SqmStatement<?>> creator) {
log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString );
final StatisticsImplementor statistics = statisticsSupplier.get();
final boolean stats = statistics.isStatisticsEnabled();
final long startTime = ( stats ) ? System.nanoTime() : 0L;
final String cacheKey;
if ( expectedResultType != null && expectedResultType.isArray() ) {
cacheKey = queryString + "_array";
}
else {
cacheKey = queryString;
}
final DomainParameterXref domainParameterXref;
HqlInterpretation hqlInterpretation = hqlInterpretationCache.get( queryString );
HqlInterpretation hqlInterpretation = hqlInterpretationCache.get( cacheKey );
if ( hqlInterpretation == null ) {
log.debugf( "Creating and caching HqlInterpretation - %s", queryString );
final SqmStatement<?> sqmStatement = creator.apply( queryString );
@ -124,7 +133,7 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
}
hqlInterpretation = new SimpleHqlInterpretationImpl( sqmStatement, parameterMetadata, domainParameterXref );
hqlInterpretationCache.put( queryString, hqlInterpretation );
hqlInterpretationCache.put( cacheKey, hqlInterpretation );
if ( stats ) {
final long endTime = System.nanoTime();

View File

@ -35,7 +35,7 @@ public interface QueryInterpretationCache {
int getNumberOfCachedHqlInterpretations();
int getNumberOfCachedQueryPlans();
HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator);
HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, Function<String, SqmStatement<?>> creator);
<R> SelectQueryPlan<R> resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan<R>> creator);

View File

@ -11,7 +11,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hibernate.query.Query;
import org.hibernate.ScrollMode;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
@ -23,6 +22,7 @@ import org.hibernate.internal.EmptyScrollableResults;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.IllegalQueryOperationException;
import org.hibernate.query.Query;
import org.hibernate.query.TupleTransformer;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.QueryEngine;
@ -47,9 +47,10 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter;
import org.hibernate.sql.results.internal.TupleMetadata;
import org.hibernate.sql.results.spi.ListResultsConsumer;
@ -66,7 +67,6 @@ import static org.hibernate.query.sqm.internal.QuerySqmImpl.CRITERIA_HQL_STRING;
*/
public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
private final SqmSelectStatement<?> sqm;
private final String hql;
private final DomainParameterXref domainParameterXref;
private final RowTransformer<R> rowTransformer;
private final SqmInterpreter<List<R>, Void> listInterpreter;
@ -82,7 +82,6 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
TupleMetadata tupleMetadata,
QueryOptions queryOptions) {
this.sqm = sqm;
this.hql = hql;
this.domainParameterXref = domainParameterXref;
this.rowTransformer = determineRowTransformer( sqm, resultType, tupleMetadata, queryOptions );
@ -175,16 +174,21 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
Class<R> resultType,
TupleMetadata tupleMetadata,
QueryOptions queryOptions) {
if ( resultType == null || resultType.isArray() ) {
if ( queryOptions.getTupleTransformer() != null ) {
return makeRowTransformerTupleTransformerAdapter( sqm, queryOptions );
}
else {
return RowTransformerPassThruImpl.instance();
}
if ( queryOptions.getTupleTransformer() != null ) {
return makeRowTransformerTupleTransformerAdapter( sqm, queryOptions );
}
// NOTE : if we get here, a result-type of some kind (other than Object[].class) was specified
if ( resultType == null ) {
return RowTransformerStandardImpl.instance();
}
if ( resultType.isArray() ) {
return (RowTransformer<R>) RowTransformerArrayImpl.instance();
}
// NOTE : if we get here :
// 1) there is no TupleTransformer specified
// 2) an explicit result-type, other than an array, was specified
final List<SqmSelection<?>> selections = sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections();
if ( tupleMetadata != null ) {

View File

@ -142,19 +142,20 @@ public class QuerySqmImpl<R>
*/
public QuerySqmImpl(
NamedHqlQueryMementoImpl memento,
Class<R> resultType,
Class<R> expectedResultType,
SharedSessionContractImplementor session) {
super( session );
this.hql = memento.getHqlString();
this.resultType = resultType;
this.resultType = expectedResultType;
final SessionFactoryImplementor factory = session.getFactory();
final QueryEngine queryEngine = factory.getQueryEngine();
final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache();
final HqlInterpretation hqlInterpretation = interpretationCache.resolveHqlInterpretation(
hql,
(s) -> queryEngine.getHqlTranslator().translate( hql )
expectedResultType,
(s) -> queryEngine.getHqlTranslator().translate( hql, expectedResultType )
);
this.sqm = hqlInterpretation.getSqmStatement();

View File

@ -93,24 +93,39 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
public SqmSelectionQueryImpl(
String hql,
HqlInterpretation hqlInterpretation,
Class<R> expectedResultType,
SharedSessionContractImplementor session) {
super( session );
this.hql = hql;
//noinspection unchecked
this.sqm = (SqmSelectStatement<R>) hqlInterpretation.getSqmStatement();
this.parameterMetadata = hqlInterpretation.getParameterMetadata();
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
visitQueryReturnType( sqm.getQueryPart(), null, getSessionFactory() );
this.resultType = null;
visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
this.resultType = determineResultType( sqm, expectedResultType );
setComment( hql );
this.tupleMetadata = null;
}
private static <T> Class<T> determineResultType(SqmSelectStatement<?> sqm, Class<?> expectedResultType) {
if ( expectedResultType == null || ! expectedResultType.isArray() ) {
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
if ( selections.size() == 1 ) {
final SqmSelection<?> sqmSelection = selections.get( 0 );
//noinspection unchecked
return (Class<T>) sqmSelection.getNodeJavaType().getJavaTypeClass();
}
}
//noinspection unchecked
return (Class<T>) Object[].class;
}
public SqmSelectionQueryImpl(
NamedHqlQueryMementoImpl memento,
Class<R> resultType,
@ -124,7 +139,8 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache();
final HqlInterpretation hqlInterpretation = interpretationCache.resolveHqlInterpretation(
hql,
(s) -> queryEngine.getHqlTranslator().translate( hql )
resultType,
(s) -> queryEngine.getHqlTranslator().translate( hql, resultType )
);
SqmUtil.verifyIsSelectStatement( hqlInterpretation.getSqmStatement(), hql );
@ -147,12 +163,14 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
NamedCriteriaQueryMementoImpl memento,
Class<R> resultType,
SharedSessionContractImplementor session) {
this( (SqmSelectStatement<R>) memento.getSqmStatement(), session);
//noinspection unchecked
this( (SqmSelectStatement<R>) memento.getSqmStatement(), resultType, session);
applyOptions( memento );
}
public SqmSelectionQueryImpl(
SqmSelectStatement<R> criteria,
Class<R> expectedResultType,
SharedSessionContractImplementor session) {
super( session );
this.hql = CRITERIA_HQL_STRING;
@ -181,29 +199,19 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
// We don't set a null value, unless the type is also null which is the case when using HibernateCriteriaBuilder.value
if ( value != null || jpaCriteriaParameter.getNodeType() == null ) {
// Use the anticipated type for binding the value if possible
getQueryParameterBindings().getBinding( jpaCriteriaParameter )
getQueryParameterBindings()
.getBinding( jpaCriteriaParameter )
.setBindValue( value, jpaCriteriaParameter.getAnticipatedType() );
}
}
}
this.resultType = determineResultType( sqm );
this.resultType = determineResultType( sqm, expectedResultType );
visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
visitQueryReturnType( sqm.getQueryPart(), resultType, getSessionFactory() );
setComment( hql );
this.tupleMetadata = buildTupleMetadata( sqm, resultType );
}
private static <T> Class<T> determineResultType(SqmSelectStatement<?> sqm) {
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
if ( selections.size() == 1 ) {
final SqmSelection<?> sqmSelection = selections.get( 0 );
//noinspection unchecked
return (Class<T>) sqmSelection.getNodeJavaType().getJavaTypeClass();
}
//noinspection unchecked
return (Class<T>) Object[].class;
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
}
@SuppressWarnings("rawtypes")

View File

@ -7,7 +7,6 @@
package org.hibernate.query.sqm.spi;
import org.hibernate.Incubating;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.NodeBuilder;

View File

@ -253,18 +253,10 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
checkSelectionIsJpaCompliant( selection );
}
}
final Selection<? extends T> resultSelection;
Class<T> resultType = getResultType();
if ( resultType == Object.class ) {
setResultType( resultType = (Class<T>) Object[].class );
}
if ( Tuple.class.isAssignableFrom( resultType ) ) {
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( selections );
}
else if ( resultType.isArray() ) {
resultSelection = nodeBuilder().array( resultType, selections );
}
else if ( Object.class.equals( resultType ) ) {
if ( resultType == null || resultType == Object.class ) {
switch ( selections.length ) {
case 0: {
throw new IllegalArgumentException(
@ -276,10 +268,17 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
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 );
}
@ -295,38 +294,39 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
checkSelectionIsJpaCompliant( selection );
}
}
final Selection<? extends T> resultSelection;
Class<T> resultType = getResultType();
if ( resultType == null ) {
setResultType( resultType = (Class<T>) Object[].class );
}
final List<? extends JpaSelection<?>> jpaSelectionList = (List<? extends JpaSelection<?>>) (List<?>) selectionList;
if ( Tuple.class.isAssignableFrom( resultType ) ) {
resultSelection = ( Selection<? extends T> ) nodeBuilder().tuple( jpaSelectionList );
}
else if ( resultType.isArray() ) {
resultSelection = nodeBuilder().array( resultType, jpaSelectionList );
}
else if ( Object.class.equals( resultType ) ) {
switch ( selectionList.size() ) {
final Class<T> resultType = getResultType();
final List<? extends JpaSelection<?>> selections = (List<? extends JpaSelection<?>>) (List<?>) selectionList;
if ( resultType == null || resultType == Object.class ) {
switch ( selections.size() ) {
case 0: {
throw new IllegalArgumentException(
"empty selections passed to criteria query typed as Object"
);
}
case 1: {
resultSelection = ( Selection<? extends T> ) selectionList.get( 0 );
resultSelection = ( Selection<? extends T> ) selections.get( 0 );
break;
}
default: {
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( jpaSelectionList );
setResultType( (Class<T>) Object[].class );
resultSelection = ( Selection<? extends T> ) nodeBuilder().array( selections );
}
}
}
else {
resultSelection = nodeBuilder().construct( resultType, jpaSelectionList );
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 );
}
getQuerySpec().getSelectClause().setSelection( (SqmSelectableNode<?>) resultSelection );
return this;
}

View File

@ -38,7 +38,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.results.NoMoreOutputsException;
import org.hibernate.sql.results.internal.ResultsHelper;
import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.jdbc.internal.DirectResultSetAccess;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
@ -229,7 +229,7 @@ public class OutputsImpl implements Outputs {
final RowReader<Object[]> rowReader = (RowReader<Object[]>) ResultsHelper.createRowReader(
executionContext,
null,
RowTransformerPassThruImpl.INSTANCE,
RowTransformerStandardImpl.INSTANCE,
jdbcValues
);

View File

@ -46,7 +46,7 @@ import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.internal.ResultsHelper;
import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter;
import org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesCacheHit;
@ -346,10 +346,9 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
if ( rowTransformer == null ) {
@SuppressWarnings("unchecked")
final TupleTransformer<R> tupleTransformer = (TupleTransformer<R>)
executionContext.getQueryOptions().getTupleTransformer();
final TupleTransformer<R> tupleTransformer = (TupleTransformer<R>) executionContext.getQueryOptions().getTupleTransformer();
if ( tupleTransformer == null ) {
rowTransformer = RowTransformerPassThruImpl.instance();
rowTransformer = RowTransformerStandardImpl.instance();
}
else {
final List<DomainResult<?>> domainResults = jdbcValues.getValuesMapping().getDomainResults();

View File

@ -0,0 +1,30 @@
/*
* 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.sql.results.internal;
import org.hibernate.sql.results.spi.RowTransformer;
/**
* RowTransformer used when an array is explicitly specified as the return type
*
* @author Steve Ebersole
*/
public class RowTransformerArrayImpl implements RowTransformer<Object[]> {
/**
* Singleton access
*/
public static final RowTransformerArrayImpl INSTANCE = new RowTransformerArrayImpl();
public static RowTransformerArrayImpl instance() {
return INSTANCE;
}
@Override
public Object[] transformRow(Object[] row) {
return row;
}
}

View File

@ -10,23 +10,27 @@ import org.hibernate.Incubating;
import org.hibernate.sql.results.spi.RowTransformer;
/**
* Essentially a no-op transformer - simply passes the result through
* The standard RowTransformer - <ol>
* <li>if the row array has just a single dimension, the value from that dimension (index zero) is returned</li>
* <li>otherwise, the array itself is returned</li>
* </ol>
*
* @author Steve Ebersole
*/
@Incubating
public class RowTransformerPassThruImpl<T> implements RowTransformer<T> {
public class RowTransformerStandardImpl<T> implements RowTransformer<T> {
/**
* Singleton access
*/
public static final RowTransformerPassThruImpl INSTANCE = new RowTransformerPassThruImpl();
@SuppressWarnings("rawtypes")
public static final RowTransformerStandardImpl INSTANCE = new RowTransformerStandardImpl();
@SuppressWarnings("unchecked")
public static <T> RowTransformerPassThruImpl<T> instance() {
public static <T> RowTransformerStandardImpl<T> instance() {
return INSTANCE;
}
private RowTransformerPassThruImpl() {
private RowTransformerStandardImpl() {
}
@Override

View File

@ -807,16 +807,7 @@ public class DynamicFilterTest extends BaseNonConfigCoreFunctionalTestCase {
criteria.where( criteriaBuilder.equal( root.get( "id" ), testData.prod1Id ) );
Product prod = session.createQuery( criteria )
.setResultTransformer(new ResultTransformer<Product>() {
@Override
public Product transformTuple(Object[] tuple, String[] aliases) {
return (Product) tuple[0];
}
@Override
public List<Product> transformList(List<Product> resultList) {
return ResultTransformer.super.transformList(resultList);
}
})
.setTupleTransformer( (tuple, aliases) -> (Product) tuple[0] )
.uniqueResult();
assertNotNull( prod );

View File

@ -184,7 +184,7 @@ public class EntityJoinTest {
final HqlTranslator hqlTranslator = queryEngine.getHqlTranslator();
final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory();
final SqmStatement<Object> sqm = hqlTranslator.translate( qry );
final SqmStatement<Object> sqm = hqlTranslator.translate( qry, null );
final SqmTranslator<SelectStatement> selectTranslator = sqmTranslatorFactory.createSelectTranslator(
(SqmSelectStatement<?>) sqm,

View File

@ -35,7 +35,7 @@ public class MutationTests {
final SqmDeleteStatement<?> sqmDelete = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete BasicEntity" );
.translate( "delete BasicEntity", null );
assertThat( sqmDelete, notNullValue() );
assertThat( sqmDelete.getTarget().getEntityName(), is( BasicEntity.class.getName() ) );
@ -47,7 +47,7 @@ public class MutationTests {
final SqmDeleteStatement<?> sqmDelete = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete BasicEntity where data = 'abc'" );
.translate( "delete BasicEntity where data = 'abc'", null );
assertThat( sqmDelete, notNullValue() );
assertThat( sqmDelete.getTarget().getEntityName(), is( BasicEntity.class.getName() ) );

View File

@ -0,0 +1,176 @@
/*
* 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.orm.test.query.results;
import java.util.List;
import java.util.UUID;
import org.hibernate.ScrollableResults;
import org.hibernate.query.SelectionQuery;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.retail.Product;
import org.hibernate.testing.orm.domain.retail.Vendor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel( standardModels = StandardDomainModel.RETAIL )
@SessionFactory
public class ImplicitSelectWithJoinTests {
private static final String HQL = "from Product p join p.vendor v where v.name like '%Steve%'";
private static final String HQL2 = "select p " + HQL;
@Test
public void testNoExpectedType(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final SelectionQuery<?> query = session.createSelectionQuery( HQL );
{
final List<?> results = query.list();
assertThat( results ).hasSize( 1 );
final Object result = results.get( 0 );
assertThat( result ).isInstanceOf( Product.class );
}
{
final ScrollableResults<?> results = query.scroll();
assertThat( results.next() ).isTrue();
final Object result = results.get();
assertThat( result ).isInstanceOf( Product.class );
assertThat( results.next() ).isFalse();
}
} );
}
@Test
public void testProductResult(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final SelectionQuery<Product> query = session.createSelectionQuery( HQL, Product.class );
{
final List<Product> results = query.list();
assertThat( results ).hasSize( 1 );
final Product result = results.get( 0 );
assertThat( result ).isNotNull();
}
{
final ScrollableResults<Product> results = query.scroll();
assertThat( results.next() ).isTrue();
final Product result = results.get();
assertThat( result ).isNotNull();
assertThat( results.next() ).isFalse();
}
} );
}
@Test
public void testArrayResult(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final SelectionQuery<Object[]> query = session.createSelectionQuery( HQL, Object[].class );
{
final List<Object[]> results = query.list();
assertThat( results ).hasSize( 1 );
final Object[] result = results.get( 0 );
assertThat( result ).isNotNull();
assertThat( result ).hasSize( 2 );
assertThat( result[ 0 ] ).isNotNull();
assertThat( result[ 1 ] ).isNotNull();
}
{
final ScrollableResults<Object[]> results = query.scroll();
assertThat( results.next() ).isTrue();
final Object[] result = results.get();
assertThat( results.next() ).isFalse();
assertThat( result ).isNotNull();
assertThat( result ).hasSize( 2 );
assertThat( result[ 0 ] ).isNotNull();
assertThat( result[ 1 ] ).isNotNull();
}
} );
}
@Test
public void testExplicitSingleSelectionArrayResult(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final SelectionQuery<Object[]> query = session.createSelectionQuery( HQL2, Object[].class );
{
final List<Object[]> results = query.list();
assertThat( results ).hasSize( 1 );
final Object[] result = results.get( 0 );
assertThat( result ).isNotNull();
assertThat( result ).hasSize( 1 );
assertThat( result[ 0 ] ).isNotNull();
assertThat( result[ 0 ] ).isInstanceOf( Product.class );
}
{
final ScrollableResults<Object[]> results = query.scroll();
assertThat( results.next() ).isTrue();
final Object[] result = results.get();
assertThat( results.next() ).isFalse();
assertThat( result ).isNotNull();
assertThat( result ).hasSize( 1 );
assertThat( result[ 0 ] ).isNotNull();
assertThat( result[ 0 ] ).isInstanceOf( Product.class );
}
} );
}
@Test
public void testExplicitSingleSelectionProductResult(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final SelectionQuery<Product> query = session.createSelectionQuery( HQL2, Product.class );
{
final List<Product> results = query.list();
assertThat( results ).hasSize( 1 );
final Product result = results.get( 0 );
assertThat( result ).isNotNull();
}
{
final ScrollableResults<Product> results = query.scroll();
assertThat( results.next() ).isTrue();
final Product result = results.get();
assertThat( result ).isNotNull();
assertThat( results.next() ).isFalse();
}
} );
}
@BeforeEach
public void prepareTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final Vendor vendor = new Vendor( 1, "Steve's Curios", "Acme Corp." );
final Product product = new Product( 10, UUID.randomUUID(), vendor );
session.persist( vendor );
session.persist( product );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createMutationQuery( "delete Product" ).executeUpdate();
session.createMutationQuery( "delete Vendor" ).executeUpdate();
} );
}
}

View File

@ -59,7 +59,10 @@ public abstract class BaseSqmUnitTest
}
public static SqmSelectStatement<?> interpretSelect(String hql, SessionFactoryImplementor sessionFactory) {
return (SqmSelectStatement<?>) sessionFactory.getQueryEngine().getHqlTranslator().translate( hql );
return (SqmSelectStatement<?>) sessionFactory
.getQueryEngine()
.getHqlTranslator()
.translate( hql, null );
}
@Override

View File

@ -54,7 +54,7 @@ public class IdSelectionTests {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete SimpleEntityWithSecondaryTables where name = :n" );
.translate( "delete SimpleEntityWithSecondaryTables where name = :n", null );
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
@ -79,7 +79,7 @@ public class IdSelectionTests {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete SimpleEntityWithSecondaryTables where data = :d" );
.translate( "delete SimpleEntityWithSecondaryTables where data = :d", null );
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
@ -104,7 +104,7 @@ public class IdSelectionTests {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete Customer where name = :n" );
.translate( "delete Customer where name = :n", null );
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
@ -129,7 +129,7 @@ public class IdSelectionTests {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete ForeignCustomer where name = :n" );
.translate( "delete ForeignCustomer where name = :n", null );
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
@ -154,7 +154,7 @@ public class IdSelectionTests {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory()
.getQueryEngine()
.getHqlTranslator()
.translate( "delete ForeignCustomer where vat = :v" );
.translate( "delete ForeignCustomer where vat = :v", null );
final DomainParameterXref domainParameterXref = DomainParameterXref.from( sqm );
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );

View File

@ -28,7 +28,7 @@ public class AbstractResultTests {
protected SelectStatement interpret(String hql, QueryParameterBindings parameterBindings, SessionFactoryImplementor sessionFactory) {
final QueryEngine queryEngine = sessionFactory.getQueryEngine();
final SqmSelectStatement<?> sqm = (SqmSelectStatement<?>) queryEngine.getHqlTranslator().translate( hql );
final SqmSelectStatement<?> sqm = (SqmSelectStatement<?>) queryEngine.getHqlTranslator().translate( hql, null );
final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory();
final SqmTranslator<SelectStatement> sqmConverter = sqmTranslatorFactory.createSelectTranslator(

View File

@ -20,6 +20,9 @@ import jakarta.persistence.Converter;
public class MonetaryAmountConverter implements AttributeConverter<MonetaryAmount,Double> {
@Override
public Double convertToDatabaseColumn(MonetaryAmount attribute) {
if ( attribute == null ) {
return null;
}
return attribute.getNumber().numberValueExact( Double.class );
}