HHH-15078 - Support for Tuple and SelectionQuery

This commit is contained in:
Steve Ebersole 2022-03-30 14:20:44 -05:00
parent 88938ac482
commit aa0c57aa5c
4 changed files with 72 additions and 11 deletions

View File

@ -86,6 +86,10 @@ import static org.hibernate.jpa.QueryHints.HINT_READONLY;
public abstract class AbstractSelectionQuery<R>
extends AbstractCommonQueryContract
implements SelectionQuery<R>, DomainQueryExecutionContext {
/**
* The value used for {@link #getQueryString} for Criteria-based queries
*/
public static final String CRITERIA_HQL_STRING = "<criteria>";
private Callback callback;
@ -189,10 +193,17 @@ public abstract class AbstractSelectionQuery<R>
}
}
protected abstract String getQueryString();
/**
* Used during handling of Criteria queries
*/
protected void visitQueryReturnType(
SqmQueryPart<R> queryPart,
Class<R> resultType,
SessionFactoryImplementor factory) {
assert getQueryString().equals( CRITERIA_HQL_STRING );
if ( queryPart instanceof SqmQuerySpec<?> ) {
final SqmQuerySpec<R> sqmQuerySpec = (SqmQuerySpec<R>) queryPart;
final List<SqmSelection<?>> sqmSelections = sqmQuerySpec.getSelectClause().getSelections();

View File

@ -119,11 +119,6 @@ import static org.hibernate.query.sqm.internal.SqmUtil.isSelect;
public class QuerySqmImpl<R>
extends AbstractSelectionQuery<R>
implements SqmQueryImplementor<R>, InterpretationsKeySource, DomainQueryExecutionContext {
/**
* The value used for {@link #getQueryString} for Criteria-based queries
*/
public static final String CRITERIA_HQL_STRING = "<criteria>";
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( QuerySqmImpl.class );
private final String hql;

View File

@ -18,6 +18,7 @@ import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.Parameter;
import jakarta.persistence.TemporalType;
import jakarta.persistence.Tuple;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
@ -78,8 +79,6 @@ import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOption
* @author Steve Ebersole
*/
public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implements SqmSelectionQuery<R>, InterpretationsKeySource {
public static final String CRITERIA_HQL_STRING = "<criteria>";
private final String hql;
private final SqmSelectStatement<R> sqm;
@ -105,14 +104,19 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
// visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
this.resultType = determineResultType( sqm, expectedResultType );
setComment( hql );
this.tupleMetadata = null;
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
}
private static <T> Class<T> determineResultType(SqmSelectStatement<?> sqm, Class<?> expectedResultType) {
if ( expectedResultType != null && expectedResultType.equals( Tuple.class ) ) {
//noinspection unchecked
return (Class<T>) Tuple.class;
}
if ( expectedResultType == null || ! expectedResultType.isArray() ) {
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
if ( selections.size() == 1 ) {
@ -152,10 +156,9 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
visitQueryReturnType( sqm.getQueryPart(), resultType, getSessionFactory() );
setComment( hql );
applyOptions( memento );
this.tupleMetadata = buildTupleMetadata( sqm, resultType );
}

View File

@ -6,11 +6,13 @@
*/
package org.hibernate.orm.test.query.sqm;
import java.util.List;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;
import jakarta.persistence.Tuple;
import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.SessionImplementor;
@ -22,8 +24,10 @@ import org.hibernate.testing.orm.domain.contacts.Contact;
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.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
/**
@ -63,6 +67,54 @@ public class BasicSelectionQueryTests {
} );
}
private void createDummyEntity(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.persist( new DummyEntity( 1, "whatever" ) );
} );
}
private final String tuple_selection_hql = "select c.id as id, c.name as name from DummyEntity c";
@Test
public void tupleSelectionTestBaseline(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
checkResults( session.createQuery( tuple_selection_hql, Tuple.class ), session );
} );
}
@Test
public void tupleSelectionTest(SessionFactoryScope scope) {
createDummyEntity( scope );
// first make sure we get back the correct results via list
scope.inTransaction( (session) -> {
final SelectionQuery<Tuple> selectionQuery = session.createSelectionQuery( tuple_selection_hql, Tuple.class );
final List<Tuple> tuples = selectionQuery.list();
assertThat( tuples ).hasSize( 1 );
assertThat( tuples.get( 0 ) ).isInstanceOf( Tuple.class );
final Tuple tuple = tuples.get( 0 );
assertThat( tuple.get( 0 ) ).isEqualTo( 1 );
assertThat( tuple.get( "id" ) ).isEqualTo( 1 );
assertThat( tuple.get( 1 ) ).isEqualTo( "whatever" );
assertThat( tuple.get( "name" ) ).isEqualTo( "whatever" );
} );
// next make sure the rest of the execution methods work
scope.inTransaction( (session) -> {
final SelectionQuery<Tuple> selectionQuery = session.createSelectionQuery( tuple_selection_hql, Tuple.class );
checkResults( selectionQuery, session );
} );
}
@AfterEach
public void dropTestData(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
session.createMutationQuery( "delete DummyEntity" ).executeUpdate();
} );
}
@Test
public void rawScalarSelectTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {