Query#scroll support
+ HHH-14308: Add generic type parameter to ScrollableResults
This commit is contained in:
parent
1b7017ff71
commit
abeb6373c7
|
@ -59,6 +59,13 @@ public interface ScrollableResults<R> extends AutoCloseable, Closeable {
|
|||
*/
|
||||
boolean scroll(int positions);
|
||||
|
||||
/**
|
||||
* Moves the result cursor to the specified position.
|
||||
*
|
||||
* @return {@code true} if there is a result at the new location
|
||||
*/
|
||||
boolean position(int position);
|
||||
|
||||
/**
|
||||
* Go to the last result.
|
||||
*
|
||||
|
|
|
@ -16,7 +16,8 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
|||
import org.hibernate.sql.results.spi.RowReader;
|
||||
|
||||
/**
|
||||
* Base implementation of the ScrollableResults interface.
|
||||
* Base implementation of the ScrollableResults interface intended for sharing between
|
||||
* {@link ScrollableResultsImpl} and {@link FetchingScrollableResultsImpl}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -91,7 +92,9 @@ public abstract class AbstractScrollableResults<R> implements ScrollableResultsI
|
|||
return;
|
||||
}
|
||||
|
||||
getJdbcValues().finishUp( persistenceContext );
|
||||
rowReader.finishUp( jdbcValuesSourceProcessingState );
|
||||
jdbcValues.finishUp( persistenceContext );
|
||||
|
||||
getPersistenceContext().getJdbcCoordinator().afterStatementExecution();
|
||||
|
||||
this.closed = true;
|
||||
|
|
|
@ -45,6 +45,11 @@ public class EmptyScrollableResults implements ScrollableResultsImplementor {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean position(int position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean last() {
|
||||
return true;
|
||||
|
|
|
@ -155,6 +155,11 @@ public class FetchingScrollableResultsImpl<R> extends AbstractScrollableResults<
|
|||
return more;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean position(int position) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean last() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
|
|
|
@ -6,14 +6,11 @@
|
|||
*/
|
||||
package org.hibernate.internal;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.spi.RowReader;
|
||||
|
@ -49,44 +46,45 @@ public class ScrollableResultsImpl<R> extends AbstractScrollableResults<R> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean scroll(int i) {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
|
||||
// todo (6.0) : need these scrollable ResultSet "re-positioning"-style methods on the JdbcValues stuff
|
||||
|
||||
// try {
|
||||
// final boolean result = getResultSet().relative( i );
|
||||
// prepareCurrentRow( result );
|
||||
// return result;
|
||||
// }
|
||||
// catch (SQLException sqle) {
|
||||
// throw convert( sqle, "could not advance using scroll()" );
|
||||
// }
|
||||
public boolean next() {
|
||||
final boolean result = getRowProcessingState().next();
|
||||
prepareCurrentRow( result );
|
||||
return result;
|
||||
}
|
||||
|
||||
protected JDBCException convert(SQLException sqle, String message) {
|
||||
return getPersistenceContext().getJdbcServices().getSqlExceptionHelper().convert( sqle, message );
|
||||
@Override
|
||||
public boolean previous() {
|
||||
final boolean result = getRowProcessingState().previous();
|
||||
prepareCurrentRow( result );
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean scroll(int i) {
|
||||
final boolean hasResult = getRowProcessingState().scroll( i );
|
||||
prepareCurrentRow( hasResult );
|
||||
return hasResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean position(int position) {
|
||||
final boolean hasResult = getRowProcessingState().position( position );
|
||||
prepareCurrentRow( hasResult );
|
||||
return hasResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean first() {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
|
||||
// todo (6.0) : need these scrollable ResultSet "re-positioning"-style methods on the JdbcValues stuff
|
||||
|
||||
// try {
|
||||
// final boolean result = getResultSet().first();
|
||||
// prepareCurrentRow( result );
|
||||
// return result;
|
||||
// }
|
||||
// catch (SQLException sqle) {
|
||||
// throw convert( sqle, "could not advance using first()" );
|
||||
// }
|
||||
final boolean hasResult = getRowProcessingState().first();
|
||||
prepareCurrentRow( hasResult );
|
||||
return hasResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean last() {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
final boolean hasResult = getRowProcessingState().last();
|
||||
prepareCurrentRow( hasResult );
|
||||
return hasResult;
|
||||
|
||||
// todo (6.0) : need these scrollable ResultSet "re-positioning"-style methods on the JdbcValues stuff
|
||||
|
||||
|
@ -100,34 +98,6 @@ public class ScrollableResultsImpl<R> extends AbstractScrollableResults<R> {
|
|||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
try {
|
||||
final boolean result = getJdbcValues().next( getRowProcessingState() );
|
||||
prepareCurrentRow( result );
|
||||
return result;
|
||||
}
|
||||
catch (SQLException sqle) {
|
||||
throw convert( sqle, "could not advance using next()" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean previous() {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
|
||||
// todo (6.0) : need these scrollable ResultSet "re-positioning"-style methods on the JdbcValues stuff
|
||||
|
||||
// try {
|
||||
// final boolean result = getResultSet().previous();
|
||||
// prepareCurrentRow( result );
|
||||
// return result;
|
||||
// }
|
||||
// catch (SQLException sqle) {
|
||||
// throw convert( sqle, "could not advance using previous()" );
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterLast() {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
|
@ -186,36 +156,12 @@ public class ScrollableResultsImpl<R> extends AbstractScrollableResults<R> {
|
|||
|
||||
@Override
|
||||
public int getRowNumber() throws HibernateException {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
|
||||
// todo (6.0) : need these scrollable ResultSet "re-positioning"-style methods on the JdbcValues stuff
|
||||
|
||||
// try {
|
||||
// return getResultSet().getRow() - 1;
|
||||
// }
|
||||
// catch (SQLException sqle) {
|
||||
// throw convert( sqle, "exception calling getRow()" );
|
||||
// }
|
||||
return getRowProcessingState().getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setRowNumber(int rowNumber) throws HibernateException {
|
||||
throw new NotYetImplementedFor6Exception();
|
||||
|
||||
// todo (6.0) : need these scrollable ResultSet "re-positioning"-style methods on the JdbcValues stuff
|
||||
|
||||
// if ( rowNumber >= 0 ) {
|
||||
// rowNumber++;
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// final boolean result = getResultSet().absolute( rowNumber );
|
||||
// prepareCurrentRow( result );
|
||||
// return result;
|
||||
// }
|
||||
// catch (SQLException sqle) {
|
||||
// throw convert( sqle, "could not advance using absolute()" );
|
||||
// }
|
||||
return position( rowNumber );
|
||||
}
|
||||
|
||||
private void prepareCurrentRow(boolean underlyingScrollSuccessful) {
|
||||
|
@ -224,15 +170,10 @@ public class ScrollableResultsImpl<R> extends AbstractScrollableResults<R> {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentRow = getRowReader().readRow(
|
||||
getRowProcessingState(),
|
||||
getProcessingOptions()
|
||||
);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw convert( e, "Unable to read row as part of ScrollableResult handling" );
|
||||
}
|
||||
currentRow = getRowReader().readRow(
|
||||
getRowProcessingState(),
|
||||
getProcessingOptions()
|
||||
);
|
||||
|
||||
afterScrollOperation();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.sql.SQLException;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ReturningWork<T> {
|
||||
/**
|
||||
* Execute the discrete work encapsulated by this work instance using the supplied connection.
|
||||
|
@ -25,5 +26,5 @@ public interface ReturningWork<T> {
|
|||
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
|
||||
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
|
||||
*/
|
||||
public T execute(Connection connection) throws SQLException;
|
||||
T execute(Connection connection) throws SQLException;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.sql.SQLException;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Work {
|
||||
/**
|
||||
* Execute the discrete work encapsulated by this work instance using the supplied connection.
|
||||
|
|
|
@ -747,7 +747,7 @@ public class ProcedureCallImpl<R>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) {
|
||||
public ScrollableResultsImplementor scroll(ScrollMode scrollMode) {
|
||||
throw new UnsupportedOperationException( "Query#scroll is not valid for ProcedureCall/StoredProcedureQuery" );
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hibernate.NonUniqueResultException;
|
|||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.RootGraph;
|
||||
import org.hibernate.metamodel.model.domain.AllowableParameterType;
|
||||
|
@ -99,29 +100,23 @@ public interface Query<R> extends TypedQuery<R>, CommonQueryContract {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the query results as <tt>ScrollableResults</tt>. The
|
||||
* scrollability of the returned results depends upon JDBC driver
|
||||
* support for scrollable <tt>ResultSet</tt>s.<br>
|
||||
* Returns scrollable access to the query results.
|
||||
*
|
||||
* @see ScrollableResults
|
||||
* This form calls {@link #scroll(ScrollMode)} using {@link Dialect#defaultScrollMode()}
|
||||
*
|
||||
* @return the result iterator
|
||||
* @apiNote The exact behavior of this method depends somewhat
|
||||
* on the JDBC driver's {@link java.sql.ResultSet} scrolling support
|
||||
*/
|
||||
ScrollableResults scroll();
|
||||
ScrollableResults<R> scroll();
|
||||
|
||||
/**
|
||||
* Return the query results as ScrollableResults. The scrollability of the returned results
|
||||
* depends upon JDBC driver support for scrollable ResultSets.
|
||||
*
|
||||
* @param scrollMode The scroll mode
|
||||
*
|
||||
* @return the result iterator
|
||||
*
|
||||
* @see ScrollableResults
|
||||
* @see ScrollMode
|
||||
* Returns scrollable access to the query results. The capabilities of the
|
||||
* returned ScrollableResults depend on the specified ScrollMode.
|
||||
*
|
||||
* @apiNote The exact behavior of this method depends somewhat
|
||||
* on the JDBC driver's {@link java.sql.ResultSet} scrolling support
|
||||
*/
|
||||
ScrollableResults scroll(ScrollMode scrollMode);
|
||||
ScrollableResults<R> scroll(ScrollMode scrollMode);
|
||||
|
||||
/**
|
||||
* Return the query results as a <tt>List</tt>. If the query contains
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.time.Instant;
|
|||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
|
@ -44,6 +43,7 @@ import org.hibernate.PropertyNotFoundException;
|
|||
import org.hibernate.QueryParameterException;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.TypeMismatchException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.query.spi.EntityGraphQueryHint;
|
||||
import org.hibernate.engine.spi.ExceptionConverter;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
|
@ -1390,51 +1390,25 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ScrollableResultsImplementor scroll() {
|
||||
return scroll( getSession().getJdbcServices().getJdbcEnvironment().getDialect().defaultScrollMode() );
|
||||
public ScrollableResultsImplementor<R> scroll() {
|
||||
final Dialect dialect = getSession().getJdbcServices().getJdbcEnvironment().getDialect();
|
||||
return scroll( dialect.defaultScrollMode() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollableResultsImplementor scroll(ScrollMode scrollMode) {
|
||||
beforeQuery();
|
||||
try {
|
||||
return doScroll( scrollMode );
|
||||
}
|
||||
finally {
|
||||
afterQuery();
|
||||
}
|
||||
}
|
||||
|
||||
protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
|
||||
// if ( getMaxResults() == 0 ) {
|
||||
// return EmptyScrollableResults.INSTANCE;
|
||||
// }
|
||||
//
|
||||
// final String query = getQueryParameterBindings().expandListValuedParameters( getQueryString(), getSession() );
|
||||
// QueryParameters queryParameters = makeQueryParametersForExecution( query );
|
||||
// queryParameters.setScrollMode( scrollMode );
|
||||
// return getSession().scroll( query, queryParameters );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Stream<R> stream() {
|
||||
if (getMaxResults() == 0){
|
||||
final Spliterator<R> spliterator = Spliterators.emptySpliterator();
|
||||
return StreamSupport.stream( spliterator, false );
|
||||
}
|
||||
final ScrollableResultsImplementor scrollableResults = scroll( ScrollMode.FORWARD_ONLY );
|
||||
final ScrollableResultsImplementor<R> scrollableResults = scroll( ScrollMode.FORWARD_ONLY );
|
||||
final ScrollableResultsIterator<R> iterator = new ScrollableResultsIterator<>( scrollableResults );
|
||||
final Spliterator<R> spliterator = Spliterators.spliteratorUnknownSize( iterator, Spliterator.NONNULL );
|
||||
|
||||
final Stream<R> stream = new StreamDecorator(
|
||||
return new StreamDecorator<>(
|
||||
StreamSupport.stream( spliterator, false ),
|
||||
scrollableResults::close
|
||||
);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* 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.query.internal;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.ResultListTransformer;
|
||||
import org.hibernate.query.TupleTransformer;
|
||||
import org.hibernate.query.spi.ParameterMetadataImplementor;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class QueryImpl<R> extends AbstractProducedQuery<R> implements Query<R> {
|
||||
private final String queryString;
|
||||
|
||||
private final QueryParameterBindingsImpl queryParameterBindings;
|
||||
|
||||
public QueryImpl(
|
||||
SharedSessionContractImplementor producer,
|
||||
ParameterMetadataImplementor parameterMetadata,
|
||||
String queryString) {
|
||||
super( producer, parameterMetadata );
|
||||
this.queryString = queryString;
|
||||
this.queryParameterBindings = QueryParameterBindingsImpl.from(
|
||||
parameterMetadata,
|
||||
producer.getFactory(),
|
||||
producer.isQueryParametersValidationEnabled()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryParameterBindings getQueryParameterBindings() {
|
||||
return queryParameterBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return queryString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<R> setTupleTransformer(TupleTransformer<R> transformer) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<R> setResultListTransformer(ResultListTransformer transformer) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<R> setParameterList(String name, Collection values, Class type) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<R> setParameterList(int position, Collection values, Class type) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNativeQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getParameterBindings() {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public Type[] getReturnTypes() {
|
||||
// return getProducer().getFactory().getReturnTypes( queryString );
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String[] getReturnAliases() {
|
||||
// return getProducer().getFactory().getReturnAliases( queryString );
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Query setEntity(int position, Object val) {
|
||||
// return setParameter( position, val, getProducer().getFactory().getTypeHelper().entity( resolveEntityName( val ) ) );
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Query setEntity(String name, Object val) {
|
||||
// return setParameter( name, val, getProducer().getFactory().getTypeHelper().entity( resolveEntityName( val ) ) );
|
||||
// }
|
||||
}
|
|
@ -1425,23 +1425,10 @@ public abstract class AbstractQuery<R> implements QueryImplementor<R> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ScrollableResultsImplementor<?> scroll() {
|
||||
public ScrollableResultsImplementor<R> scroll() {
|
||||
return scroll( getSession().getFactory().getJdbcServices().getJdbcEnvironment().getDialect().defaultScrollMode() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollableResultsImplementor<?> scroll(ScrollMode scrollMode) {
|
||||
beforeQuery( false );
|
||||
try {
|
||||
return doScroll( scrollMode );
|
||||
}
|
||||
finally {
|
||||
afterQuery();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ScrollableResultsImplementor<?> doScroll(ScrollMode scrollMode);
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( {"unchecked", "rawtypes"} )
|
||||
public Stream<R> stream() {
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.query.spi;
|
|||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
|
@ -27,4 +28,10 @@ public interface QueryImplementor<R> extends Query<R> {
|
|||
void setOptionalObject(Object optionalObject);
|
||||
|
||||
QueryParameterBindings getParameterBindings();
|
||||
|
||||
@Override
|
||||
ScrollableResultsImplementor<R> scroll();
|
||||
|
||||
@Override
|
||||
ScrollableResultsImplementor<R> scroll(ScrollMode scrollMode);
|
||||
}
|
||||
|
|
|
@ -539,7 +539,7 @@ public class NativeQueryImpl<R>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) {
|
||||
public ScrollableResultsImplementor<R> scroll(ScrollMode scrollMode) {
|
||||
return resolveSelectQueryPlan().performScroll( scrollMode, this );
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.Map;
|
|||
import javax.persistence.Tuple;
|
||||
import javax.persistence.TupleElement;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
|
@ -33,8 +32,8 @@ import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
|||
import org.hibernate.query.sqm.tree.select.SqmSelection;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
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.RowTransformerJpaTupleImpl;
|
||||
|
@ -176,6 +175,40 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ScrollableResultsImplementor<R> performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
|
||||
final CacheableSqmInterpretation sqmInterpretation = resolveCacheableSqmInterpretation( executionContext );
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||
executionContext.getQueryParameterBindings(),
|
||||
domainParameterXref,
|
||||
sqmInterpretation.getJdbcParamsXref(),
|
||||
session.getFactory().getDomainModel(),
|
||||
sqmInterpretation.getTableGroupAccess()::findTableGroup,
|
||||
session
|
||||
);
|
||||
sqmInterpretation.getJdbcSelect().bindFilterJdbcParameters( jdbcParameterBindings );
|
||||
|
||||
try {
|
||||
return session.getFactory().getJdbcServices().getJdbcSelectExecutor().scroll(
|
||||
sqmInterpretation.getJdbcSelect(),
|
||||
scrollMode,
|
||||
jdbcParameterBindings,
|
||||
executionContext,
|
||||
rowTransformer
|
||||
);
|
||||
}
|
||||
finally {
|
||||
domainParameterXref.clearExpansions();
|
||||
}
|
||||
}
|
||||
|
||||
private volatile CacheableSqmInterpretation cacheableSqmInterpretation;
|
||||
|
||||
private CacheableSqmInterpretation resolveCacheableSqmInterpretation(ExecutionContext executionContext) {
|
||||
|
@ -265,46 +298,4 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
return jdbcParamsXref;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, ExecutionContext executionContext) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
|
||||
// final SqmSelectToSqlAstConverter sqmConverter = getSqmSelectToSqlAstConverter( executionContext );
|
||||
//
|
||||
// final SqmSelectInterpretation interpretation = sqmConverter.interpret( sqm );
|
||||
//
|
||||
// final JdbcSelect jdbcSelect = SqlAstSelectToJdbcSelectConverter.interpret(
|
||||
// interpretation,
|
||||
// executionContext.getSession().getSessionFactory()
|
||||
// );
|
||||
//
|
||||
// final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref =
|
||||
// SqmConsumeHelper.generateJdbcParamsXref( domainParameterXref, sqmConverter );
|
||||
//
|
||||
// final JdbcParameterBindings jdbcParameterBindings = QueryHelper.createJdbcParameterBindings(
|
||||
// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
|
||||
// domainParameterXref,
|
||||
// jdbcParamsXref,
|
||||
// executionContext.getSession()
|
||||
// );
|
||||
//
|
||||
// try {
|
||||
// return JdbcSelectExecutorStandardImpl.INSTANCE.scroll(
|
||||
// jdbcSelect,
|
||||
// scrollMode,
|
||||
// jdbcParameterBindings,
|
||||
// executionContext,
|
||||
// rowTransformer
|
||||
// );
|
||||
// }
|
||||
// finally {
|
||||
// domainParameterXref.clearExpansions();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -517,7 +517,7 @@ public class QuerySqmImpl<R>
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ScrollableResultsImplementor doScroll(ScrollMode scrollMode) {
|
||||
public ScrollableResultsImplementor<R> scroll(ScrollMode scrollMode) {
|
||||
SqmUtil.verifyIsSelectStatement( getSqmStatement() );
|
||||
getSession().prepareForQueryExecution( requiresTxn( getLockOptions().findGreatestLockMode() ) );
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.hibernate.ScrollMode;
|
|||
import org.hibernate.cache.spi.QueryKey;
|
||||
import org.hibernate.cache.spi.QueryResultsCache;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.ast.spi.AfterLoadAction;
|
||||
import org.hibernate.query.internal.ScrollableResultsIterator;
|
||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||
|
@ -37,6 +39,7 @@ import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateSt
|
|||
import org.hibernate.sql.results.jdbc.internal.ResultSetAccess;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
import org.hibernate.sql.results.spi.ResultsConsumer;
|
||||
|
@ -62,11 +65,11 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
|
||||
@Override
|
||||
public <R> List<R> list(
|
||||
JdbcSelect jdbcSelect,
|
||||
JdbcParameterBindings jdbcParameterBindings,
|
||||
ExecutionContext executionContext,
|
||||
RowTransformer<R> rowTransformer,
|
||||
boolean uniqueFilter) {
|
||||
JdbcSelect jdbcSelect,
|
||||
JdbcParameterBindings jdbcParameterBindings,
|
||||
ExecutionContext executionContext,
|
||||
RowTransformer<R> rowTransformer,
|
||||
boolean uniqueFilter) {
|
||||
return executeQuery(
|
||||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
|
@ -162,6 +165,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
|
||||
final JdbcValues jdbcValues = resolveJdbcValuesSource(
|
||||
jdbcSelect,
|
||||
resultsConsumer.canResultsBeCached(),
|
||||
executionContext,
|
||||
new DeferredResultSetAccess(
|
||||
jdbcSelect,
|
||||
|
@ -238,22 +242,27 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
@SuppressWarnings("unchecked")
|
||||
private JdbcValues resolveJdbcValuesSource(
|
||||
JdbcSelect jdbcSelect,
|
||||
boolean canBeCached,
|
||||
ExecutionContext executionContext,
|
||||
ResultSetAccess resultSetAccess) {
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
final SessionFactoryImplementor factory = session.getFactory();
|
||||
final boolean queryCacheEnabled = factory.getSessionFactoryOptions().isQueryCacheEnabled();
|
||||
|
||||
final List<Object[]> cachedResults;
|
||||
|
||||
final boolean queryCacheEnabled = executionContext.getSession().getFactory().getSessionFactoryOptions().isQueryCacheEnabled();
|
||||
|
||||
final CacheMode cacheMode = JdbcExecHelper.resolveCacheMode( executionContext );
|
||||
|
||||
final JdbcValuesMapping jdbcValuesMapping = jdbcSelect.getJdbcValuesMappingProducer()
|
||||
.resolve( resultSetAccess, executionContext.getSession().getFactory() );
|
||||
final JdbcValuesMappingProducer mappingProducer = jdbcSelect.getJdbcValuesMappingProducer();
|
||||
final JdbcValuesMapping jdbcValuesMapping = mappingProducer.resolve( resultSetAccess, factory );
|
||||
|
||||
final QueryKey queryResultsCacheKey;
|
||||
|
||||
if ( queryCacheEnabled && cacheMode.isGetEnabled() ) {
|
||||
SqlExecLogger.INSTANCE.debugf( "Reading Query result cache data per CacheMode#isGetEnabled [%s]", cacheMode.name() );
|
||||
|
||||
final QueryResultsCache queryCache = executionContext.getSession().getFactory()
|
||||
final QueryResultsCache queryCache = factory
|
||||
.getCache()
|
||||
.getQueryResultsCache( executionContext.getQueryOptions().getResultCacheRegionName() );
|
||||
|
||||
|
@ -267,7 +276,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
jdbcSelect.getSql(),
|
||||
executionContext.getQueryOptions().getLimit(),
|
||||
executionContext.getQueryParameterBindings(),
|
||||
executionContext.getSession()
|
||||
session
|
||||
);
|
||||
|
||||
cachedResults = queryCache.get(
|
||||
|
@ -276,7 +285,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
// todo (6.0) : `querySpaces` and `session` make perfect sense as args, but its odd passing those into this method just to pass along
|
||||
// atm we do not even collect querySpaces, but we need to
|
||||
jdbcSelect.getAffectedTableNames(),
|
||||
executionContext.getSession()
|
||||
session
|
||||
);
|
||||
|
||||
// todo (6.0) : `querySpaces` and `session` are used in QueryCache#get to verify "up-to-dateness" via UpdateTimestampsCache
|
||||
|
@ -298,17 +307,14 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
|
|||
if ( cachedResults == null || cachedResults.isEmpty() ) {
|
||||
return new JdbcValuesResultSetImpl(
|
||||
resultSetAccess,
|
||||
queryResultsCacheKey,
|
||||
canBeCached ? queryResultsCacheKey : null,
|
||||
executionContext.getQueryOptions(),
|
||||
jdbcValuesMapping,
|
||||
executionContext
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new JdbcValuesCacheHit(
|
||||
cachedResults,
|
||||
jdbcValuesMapping
|
||||
);
|
||||
return new JdbcValuesCacheHit( cachedResults, jdbcValuesMapping );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -38,6 +39,8 @@ public class RowProcessingStateStandardImpl implements RowProcessingState {
|
|||
|
||||
private final RowReader<?> rowReader;
|
||||
private final JdbcValues jdbcValues;
|
||||
|
||||
// todo (6.0) : why doesn't this just use the array from JdbcValues?
|
||||
private Object[] currentRowJdbcValues;
|
||||
|
||||
public RowProcessingStateStandardImpl(
|
||||
|
@ -70,7 +73,7 @@ public class RowProcessingStateStandardImpl implements RowProcessingState {
|
|||
return rowReader;
|
||||
}
|
||||
|
||||
public boolean next() throws SQLException {
|
||||
public boolean next() {
|
||||
if ( jdbcValues.next( this ) ) {
|
||||
currentRowJdbcValues = jdbcValues.getCurrentRowValuesArray();
|
||||
return true;
|
||||
|
@ -81,6 +84,65 @@ public class RowProcessingStateStandardImpl implements RowProcessingState {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean previous() {
|
||||
if ( jdbcValues.previous( this ) ) {
|
||||
currentRowJdbcValues = jdbcValues.getCurrentRowValuesArray();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
currentRowJdbcValues = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean scroll(int i) {
|
||||
if ( jdbcValues.scroll( i, this ) ) {
|
||||
currentRowJdbcValues = jdbcValues.getCurrentRowValuesArray();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
currentRowJdbcValues = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean position(int i) {
|
||||
if ( jdbcValues.position( i, this ) ) {
|
||||
currentRowJdbcValues = jdbcValues.getCurrentRowValuesArray();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
currentRowJdbcValues = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return jdbcValues.getPosition();
|
||||
}
|
||||
|
||||
public boolean first() {
|
||||
if ( jdbcValues.first( this ) ) {
|
||||
currentRowJdbcValues = jdbcValues.getCurrentRowValuesArray();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
currentRowJdbcValues = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean last() {
|
||||
if ( jdbcValues.last( this ) ) {
|
||||
currentRowJdbcValues = jdbcValues.getCurrentRowValuesArray();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
currentRowJdbcValues = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getJdbcValue(int position) {
|
||||
return currentRowJdbcValues[ position ];
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.jdbc.internal;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.sql.results.caching.QueryCachePutManager;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
||||
|
@ -27,10 +25,9 @@ public abstract class AbstractJdbcValues implements JdbcValues {
|
|||
}
|
||||
|
||||
@Override
|
||||
public final boolean next(RowProcessingState rowProcessingState) throws SQLException {
|
||||
public final boolean next(RowProcessingState rowProcessingState) {
|
||||
final boolean hadRow = processNext( rowProcessingState );
|
||||
if ( hadRow ) {
|
||||
|
||||
queryCachePutManager.registerJdbcRow( getCurrentRowValuesArray() );
|
||||
}
|
||||
return hadRow;
|
||||
|
@ -38,6 +35,36 @@ public abstract class AbstractJdbcValues implements JdbcValues {
|
|||
|
||||
protected abstract boolean processNext(RowProcessingState rowProcessingState);
|
||||
|
||||
@Override
|
||||
public boolean previous(RowProcessingState rowProcessingState) {
|
||||
// NOTE : we do not even bother interacting with the query-cache put manager because
|
||||
// this method is implicitly related to scrolling and caching of scrolled results
|
||||
// is not supported
|
||||
return processPrevious( rowProcessingState );
|
||||
}
|
||||
|
||||
protected abstract boolean processPrevious(RowProcessingState rowProcessingState);
|
||||
|
||||
@Override
|
||||
public boolean scroll(int numberOfRows, RowProcessingState rowProcessingState) {
|
||||
// NOTE : we do not even bother interacting with the query-cache put manager because
|
||||
// this method is implicitly related to scrolling and caching of scrolled results
|
||||
// is not supported
|
||||
return processScroll( numberOfRows, rowProcessingState );
|
||||
}
|
||||
|
||||
protected abstract boolean processScroll(int numberOfRows, RowProcessingState rowProcessingState);
|
||||
|
||||
@Override
|
||||
public boolean position(int position, RowProcessingState rowProcessingState) {
|
||||
// NOTE : we do not even bother interacting with the query-cache put manager because
|
||||
// this method is implicitly related to scrolling and caching of scrolled results
|
||||
// is not supported
|
||||
return processPosition( position, rowProcessingState );
|
||||
}
|
||||
|
||||
protected abstract boolean processPosition(int position, RowProcessingState rowProcessingState);
|
||||
|
||||
@Override
|
||||
public final void finishUp(SharedSessionContractImplementor session) {
|
||||
queryCachePutManager.finishUp( session );
|
||||
|
|
|
@ -24,7 +24,7 @@ public class JdbcValuesCacheHit extends AbstractJdbcValues {
|
|||
|
||||
private Object[][] cachedData;
|
||||
private final int numberOfRows;
|
||||
private JdbcValuesMapping resolvedMapping;
|
||||
private final JdbcValuesMapping resolvedMapping;
|
||||
private int position = -1;
|
||||
|
||||
public JdbcValuesCacheHit(Object[][] cachedData, JdbcValuesMapping resolvedMapping) {
|
||||
|
@ -64,11 +64,112 @@ public class JdbcValuesCacheHit extends AbstractJdbcValues {
|
|||
// NOTE : explicitly skipping limit handling because the cached state ought
|
||||
// already be the limited size since the cache key includes limits
|
||||
|
||||
if ( position >= numberOfRows - 1 ) {
|
||||
position++;
|
||||
|
||||
if ( position >= numberOfRows ) {
|
||||
position = numberOfRows;
|
||||
return false;
|
||||
}
|
||||
|
||||
position++;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processPrevious(RowProcessingState rowProcessingState) {
|
||||
ResultsLogger.LOGGER.tracef( "JdbcValuesCacheHit#processPrevious : position = %i; numberOfRows = %i", position, numberOfRows );
|
||||
|
||||
// NOTE : explicitly skipping limit handling because the cached state ought
|
||||
// already be the limited size since the cache key includes limits
|
||||
|
||||
position--;
|
||||
|
||||
if ( position >= numberOfRows ) {
|
||||
position = numberOfRows;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processScroll(int numberOfRows, RowProcessingState rowProcessingState) {
|
||||
ResultsLogger.LOGGER.tracef( "JdbcValuesCacheHit#processScroll(%i) : position = %i; numberOfRows = %i", numberOfRows, position, this.numberOfRows );
|
||||
|
||||
// NOTE : explicitly skipping limit handling because the cached state should
|
||||
// already be the limited size since the cache key includes limits
|
||||
|
||||
position += numberOfRows;
|
||||
|
||||
if ( position > this.numberOfRows ) {
|
||||
position = this.numberOfRows;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processPosition(int position, RowProcessingState rowProcessingState) {
|
||||
ResultsLogger.LOGGER.tracef( "JdbcValuesCacheHit#processPosition(%i) : position = %i; numberOfRows = %i", position, this.position, this.numberOfRows );
|
||||
|
||||
// NOTE : explicitly skipping limit handling because the cached state should
|
||||
// already be the limited size since the cache key includes limits
|
||||
|
||||
if ( position < 0 ) {
|
||||
// we need to subtract it from `numberOfRows`
|
||||
final int newPosition = numberOfRows + position;
|
||||
ResultsLogger.LOGGER.debugf(
|
||||
"Translated negative absolute position `%i` into `%` based on `%i` number of rows",
|
||||
position,
|
||||
newPosition,
|
||||
numberOfRows
|
||||
);
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
if ( position > numberOfRows ) {
|
||||
ResultsLogger.LOGGER.debugf(
|
||||
"Absolute position `%i` exceeded number of rows `%i`",
|
||||
position,
|
||||
numberOfRows
|
||||
);
|
||||
this.position = numberOfRows;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.position = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeforeFirst(RowProcessingState rowProcessingState) {
|
||||
return position < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean first(RowProcessingState rowProcessingState) {
|
||||
position = 0;
|
||||
return numberOfRows > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAfterLast(RowProcessingState rowProcessingState) {
|
||||
return position >= numberOfRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean last(RowProcessingState rowProcessingState) {
|
||||
if ( numberOfRows == 0 ) {
|
||||
position = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
position = numberOfRows - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import java.sql.SQLException;
|
|||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.cache.spi.QueryKey;
|
||||
import org.hibernate.cache.spi.QueryResultsCache;
|
||||
import org.hibernate.query.Limit;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.exec.ExecutionException;
|
||||
|
@ -37,15 +36,6 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
|
|||
private final SqlSelection[] sqlSelections;
|
||||
private final Object[] currentRowJdbcValues;
|
||||
|
||||
// todo (6.0) - manage limit-based skips
|
||||
|
||||
private final int numberOfRowsToProcess;
|
||||
|
||||
// we start position at -1 prior to any next call so that the first next call
|
||||
// increments position to 0, which is the first row
|
||||
private int position = -1;
|
||||
|
||||
|
||||
public JdbcValuesResultSetImpl(
|
||||
ResultSetAccess resultSetAccess,
|
||||
QueryKey queryCacheKey,
|
||||
|
@ -57,25 +47,10 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
|
|||
this.valuesMapping = valuesMapping;
|
||||
this.executionContext = executionContext;
|
||||
|
||||
// todo (6.0) : decide how to handle paged/limited results
|
||||
this.numberOfRowsToProcess = interpretNumberOfRowsToProcess( queryOptions );
|
||||
|
||||
this.sqlSelections = valuesMapping.getSqlSelections().toArray( new SqlSelection[0] );
|
||||
this.currentRowJdbcValues = new Object[ sqlSelections.length ];
|
||||
}
|
||||
|
||||
private static int interpretNumberOfRowsToProcess(QueryOptions queryOptions) {
|
||||
if ( queryOptions == null || queryOptions.getLimit() == null ) {
|
||||
return -1;
|
||||
}
|
||||
final Limit limit = queryOptions.getLimit();
|
||||
if ( limit.getMaxRows() == null ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return limit.getMaxRows();
|
||||
}
|
||||
|
||||
private static QueryCachePutManager resolveQueryCachePutManager(
|
||||
ExecutionContext executionContext,
|
||||
QueryOptions queryOptions,
|
||||
|
@ -100,30 +75,164 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
|
|||
|
||||
@Override
|
||||
protected final boolean processNext(RowProcessingState rowProcessingState) {
|
||||
if ( numberOfRowsToProcess != -1 && position > numberOfRowsToProcess ) {
|
||||
// numberOfRowsToProcess != -1 means we had some limit, and
|
||||
// position > numberOfRowsToProcess means we have exceeded the
|
||||
// number of limited rows
|
||||
return advance(
|
||||
() -> {
|
||||
try {
|
||||
//noinspection RedundantIfStatement
|
||||
if ( ! resultSetAccess.getResultSet().next() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing (next) ResultSet position", e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processPrevious(RowProcessingState rowProcessingState) {
|
||||
return advance(
|
||||
() -> {
|
||||
try {
|
||||
//noinspection RedundantIfStatement
|
||||
if ( ! resultSetAccess.getResultSet().previous() ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing (previous) ResultSet position", e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processScroll(int numberOfRows, RowProcessingState rowProcessingState) {
|
||||
return advance(
|
||||
() -> {
|
||||
try {
|
||||
//noinspection RedundantIfStatement
|
||||
if ( ! resultSetAccess.getResultSet().relative( numberOfRows ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing (scroll) ResultSet position", e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosition() {
|
||||
try {
|
||||
return resultSetAccess.getResultSet().getRow() - 1;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error calling ResultSet#getRow", e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processPosition(int position, RowProcessingState rowProcessingState) {
|
||||
return advance(
|
||||
() -> {
|
||||
try {
|
||||
//noinspection RedundantIfStatement
|
||||
if ( ! resultSetAccess.getResultSet().absolute( position ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing (scroll) ResultSet position", e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeforeFirst(RowProcessingState rowProcessingState) {
|
||||
try {
|
||||
return resultSetAccess.getResultSet().isBeforeFirst();
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error calling ResultSet#isBeforeFirst", e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean first(RowProcessingState rowProcessingState) {
|
||||
return advance(
|
||||
() -> {
|
||||
try {
|
||||
//noinspection RedundantIfStatement
|
||||
if ( ! resultSetAccess.getResultSet().first() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing (first) ResultSet position", e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAfterLast(RowProcessingState rowProcessingState) {
|
||||
try {
|
||||
return resultSetAccess.getResultSet().isAfterLast();
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error calling ResultSet#isAfterLast", e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean last(RowProcessingState rowProcessingState) {
|
||||
return advance(
|
||||
() -> {
|
||||
try {
|
||||
//noinspection RedundantIfStatement
|
||||
if ( ! resultSetAccess.getResultSet().last() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing (last) ResultSet position", e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Advancer {
|
||||
boolean advance();
|
||||
}
|
||||
|
||||
private boolean advance(Advancer advancer) {
|
||||
final boolean hasResult = advancer.advance();
|
||||
if ( ! hasResult ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
position++;
|
||||
|
||||
try {
|
||||
if ( !resultSetAccess.getResultSet().next() ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error advancing JDBC ResultSet", e );
|
||||
}
|
||||
|
||||
try {
|
||||
readCurrentRowValues( rowProcessingState );
|
||||
readCurrentRowValues();
|
||||
return true;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw makeExecutionException( "Error reading JDBC row values", e );
|
||||
throw makeExecutionException( "Error reading ResultSet row values", e );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +246,7 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
|
|||
);
|
||||
}
|
||||
|
||||
private void readCurrentRowValues(RowProcessingState rowProcessingState) throws SQLException {
|
||||
private void readCurrentRowValues() throws SQLException {
|
||||
for ( final SqlSelection sqlSelection : sqlSelections ) {
|
||||
currentRowJdbcValues[ sqlSelection.getValuesArrayPosition() ] = sqlSelection.getJdbcValueExtractor().extract(
|
||||
resultSetAccess.getResultSet(),
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.sql.results.jdbc.spi;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
/**
|
||||
|
@ -21,19 +19,45 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
public interface JdbcValues {
|
||||
JdbcValuesMapping getValuesMapping();
|
||||
|
||||
// todo : ? - add ResultSet.previous() and ResultSet.absolute(int) style methods (to support ScrollableResults)?
|
||||
/**
|
||||
* Advances the "cursor position" and returns a boolean indicating whether
|
||||
* there is a row available to read via {@link #getCurrentRowValuesArray()}.
|
||||
*
|
||||
* @return {@code true} if there are results
|
||||
*/
|
||||
boolean next(RowProcessingState rowProcessingState);
|
||||
|
||||
/**
|
||||
* Think JDBC's {@code ResultSet#next}. Advances the "cursor position"
|
||||
* and return a boolean indicating whether advancing positioned the
|
||||
* cursor beyond the set of available results.
|
||||
* Advances the "cursor position" in reverse and returns a boolean indicating whether
|
||||
* there is a row available to read via {@link #getCurrentRowValuesArray()}.
|
||||
*
|
||||
* @return {@code true} indicates the call did not position the cursor beyond
|
||||
* the available results ({@link #getCurrentRowValuesArray} will not return
|
||||
* null); false indicates we are now beyond the end of the available results
|
||||
* ({@link #getCurrentRowValuesArray} will return null)
|
||||
* @return {@code true} if there are results available
|
||||
*/
|
||||
boolean next(RowProcessingState rowProcessingState) throws SQLException;
|
||||
boolean previous(RowProcessingState rowProcessingState);
|
||||
|
||||
/**
|
||||
* Advances the "cursor position" the indicated number of rows and returns a boolean
|
||||
* indicating whether there is a row available to read via {@link #getCurrentRowValuesArray()}.
|
||||
*
|
||||
* @param numberOfRows The number of rows to advance. This can also be negative meaning to
|
||||
* move in reverse
|
||||
*
|
||||
* @return {@code true} if there are results available
|
||||
*/
|
||||
boolean scroll(int numberOfRows, RowProcessingState rowProcessingState);
|
||||
|
||||
/**
|
||||
* Moves the "cursor position" to the specified position
|
||||
*/
|
||||
boolean position(int position, RowProcessingState rowProcessingState);
|
||||
|
||||
int getPosition();
|
||||
|
||||
boolean isBeforeFirst(RowProcessingState rowProcessingState);
|
||||
boolean first(RowProcessingState rowProcessingState);
|
||||
|
||||
boolean isAfterLast(RowProcessingState rowProcessingState);
|
||||
boolean last(RowProcessingState rowProcessingState);
|
||||
|
||||
/**
|
||||
* Get the JDBC values for the row currently positioned at within
|
||||
|
@ -45,11 +69,7 @@ public interface JdbcValues {
|
|||
Object[] getCurrentRowValuesArray();
|
||||
|
||||
/**
|
||||
* todo (6.0) : is this needed?
|
||||
* ^^ it's supposed to give impls a chance to write to the query cache
|
||||
* or release ResultSet it. But that could technically be handled by the
|
||||
* case of `#next` returning false the first time.
|
||||
* @param session
|
||||
* Give implementations a chance to finish processing
|
||||
*/
|
||||
void finishUp(SharedSessionContractImplementor session);
|
||||
}
|
||||
|
|
|
@ -81,16 +81,15 @@ public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, R> {
|
|||
persistenceContext.initializeNonLazyCollections();
|
||||
return results;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Error processing return rows"
|
||||
);
|
||||
}
|
||||
finally {
|
||||
rowReader.finishUp( jdbcValuesSourceProcessingState );
|
||||
jdbcValuesSourceProcessingState.finishUp();
|
||||
jdbcValues.finishUp( session );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canResultsBeCached() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
package org.hibernate.sql.results.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
|
||||
|
@ -23,4 +23,6 @@ public interface ResultsConsumer<T, R> {
|
|||
JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState,
|
||||
RowProcessingStateStandardImpl rowProcessingState,
|
||||
RowReader<R> rowReader);
|
||||
|
||||
boolean canResultsBeCached();
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public interface RowReader<R> {
|
|||
* todo (6.0) : JdbcValuesSourceProcessingOptions is available through RowProcessingState - why pass it in separately
|
||||
* should use one approach or the other
|
||||
*/
|
||||
R readRow(RowProcessingState processingState, JdbcValuesSourceProcessingOptions options) throws SQLException;
|
||||
R readRow(RowProcessingState processingState, JdbcValuesSourceProcessingOptions options);
|
||||
|
||||
/**
|
||||
* Called at the end of processing all rows
|
||||
|
|
|
@ -60,6 +60,11 @@ public class ScrollableResultsConsumer<R> implements ResultsConsumer<ScrollableR
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canResultsBeCached() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsCollectionFetches(JdbcValuesMapping valuesMapping) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* 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.function.Consumer;
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.TupleElement;
|
||||
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
|
||||
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
||||
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.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.typeCompatibleWith;
|
||||
import static org.hibernate.orm.test.query.results.ScalarQueries.MULTI_SELECTION_QUERY;
|
||||
import static org.hibernate.orm.test.query.results.ScalarQueries.SINGLE_ALIASED_SELECTION_QUERY;
|
||||
import static org.hibernate.orm.test.query.results.ScalarQueries.SINGLE_SELECTION_QUERY;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests of the Query's "domain results" via normal `Query#list` operations
|
||||
*/
|
||||
@DomainModel( annotatedClasses = BasicEntity.class )
|
||||
@SessionFactory
|
||||
public class ResultListTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUpTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.persist( new BasicEntity( 1, "value" ) )
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanUpTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery( "delete BasicEntity" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionTupleList(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Tuple> query = session.createQuery( SINGLE_SELECTION_QUERY, Tuple.class );
|
||||
verifyList(
|
||||
query,
|
||||
(tuple) -> {
|
||||
assertThat( tuple.getElements().size(), is( 1 ) );
|
||||
final TupleElement<?> element = tuple.getElements().get( 0 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( String.class ) );
|
||||
assertThat( element.getAlias(), nullValue() );
|
||||
|
||||
assertThat( tuple.toArray().length, is( 1 ) );
|
||||
|
||||
final Object byPosition = tuple.get( 0 );
|
||||
assertThat( byPosition, is( "value" ) );
|
||||
|
||||
try {
|
||||
tuple.get( "data" );
|
||||
fail( "Expecting IllegalArgumentException per JPA spec" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAliasedSelectionTupleList(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Tuple> query = session.createQuery( SINGLE_ALIASED_SELECTION_QUERY, Tuple.class );
|
||||
verifyList(
|
||||
query,
|
||||
(tuple) -> {
|
||||
assertThat( tuple.getElements().size(), is( 1 ) );
|
||||
final TupleElement<?> element = tuple.getElements().get( 0 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( String.class ) );
|
||||
assertThat( element.getAlias(), is( "state" ) );
|
||||
|
||||
assertThat( tuple.toArray().length, is( 1 ) );
|
||||
|
||||
final Object byPosition = tuple.get( 0 );
|
||||
assertThat( byPosition, is( "value" ) );
|
||||
|
||||
final Object byName = tuple.get( "state" );
|
||||
assertThat( byName, is( "value" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionsTupleList(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Tuple> query = session.createQuery( MULTI_SELECTION_QUERY, Tuple.class );
|
||||
verifyList(
|
||||
query,
|
||||
(tuple) -> {
|
||||
assertThat( tuple.getElements().size(), is( 2 ) );
|
||||
|
||||
{
|
||||
final TupleElement<?> element = tuple.getElements().get( 0 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( Integer.class ) );
|
||||
assertThat( element.getAlias(), nullValue() );
|
||||
}
|
||||
|
||||
{
|
||||
final TupleElement<?> element = tuple.getElements().get( 1 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( String.class ) );
|
||||
assertThat( element.getAlias(), nullValue() );
|
||||
}
|
||||
|
||||
assertThat( tuple.toArray().length, is( 2 ) );
|
||||
|
||||
{
|
||||
final Object byPosition = tuple.get( 0 );
|
||||
assertThat( byPosition, is( 1 ) );
|
||||
|
||||
try {
|
||||
tuple.get( "id" );
|
||||
fail( "Expecting IllegalArgumentException per JPA spec" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final Object byPosition = tuple.get( 1 );
|
||||
assertThat( byPosition, is( "value" ) );
|
||||
|
||||
try {
|
||||
tuple.get( "data" );
|
||||
fail( "Expecting IllegalArgumentException per JPA spec" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectionList(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( SINGLE_SELECTION_QUERY, String.class );
|
||||
verifyList(
|
||||
query,
|
||||
(data) -> assertThat( data, is( "value" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollSelections(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Object[]> query = session.createQuery( MULTI_SELECTION_QUERY, Object[].class );
|
||||
verifyList(
|
||||
query,
|
||||
(values) -> {
|
||||
assertThat( values[0], is( 1 ) );
|
||||
assertThat( values[1], is( "value" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private static <R> void verifyList(Query<R> query, Consumer<R> validator) {
|
||||
final List<R> results = query.list();
|
||||
assertThat( results.size(), is( 1 ) );
|
||||
validator.accept( results.get( 0 ) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ScalarQueries {
|
||||
String SINGLE_SELECTION_QUERY = "select e.data from BasicEntity e order by e.data";
|
||||
String MULTI_SELECTION_QUERY = "select e.id, e.data from BasicEntity e order by e.id";
|
||||
String SINGLE_ALIASED_SELECTION_QUERY = "select e.data as state from BasicEntity e order by e.data";
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* 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.function.Consumer;
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.TupleElement;
|
||||
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.query.spi.QueryImplementor;
|
||||
import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||
|
||||
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
|
||||
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.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.typeCompatibleWith;
|
||||
import static org.hibernate.orm.test.query.results.ScalarQueries.MULTI_SELECTION_QUERY;
|
||||
import static org.hibernate.orm.test.query.results.ScalarQueries.SINGLE_ALIASED_SELECTION_QUERY;
|
||||
import static org.hibernate.orm.test.query.results.ScalarQueries.SINGLE_SELECTION_QUERY;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Tests of the Query's "domain results" via ScrollableResults
|
||||
*/
|
||||
@DomainModel( annotatedClasses = BasicEntity.class )
|
||||
@SessionFactory
|
||||
public class ScrollableResultsTests {
|
||||
|
||||
@BeforeEach
|
||||
public void setUpTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.persist( new BasicEntity( 1, "value" ) )
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanUpTestData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery( "delete BasicEntity" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCursorPositioning(SessionFactoryScope scope) {
|
||||
// create an extra row so we can better test cursor positioning
|
||||
scope.inTransaction(
|
||||
session -> session.persist( new BasicEntity( 2, "other" ) )
|
||||
);
|
||||
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( SINGLE_SELECTION_QUERY, String.class );
|
||||
final ScrollableResultsImplementor<String> results = query.scroll( ScrollMode.SCROLL_INSENSITIVE );
|
||||
|
||||
// try to initially read in reverse - should be false
|
||||
assertThat( results.previous(), is( false ) );
|
||||
|
||||
// position at the first row
|
||||
assertThat( results.next(), is( true ) );
|
||||
String data = results.get();
|
||||
assertThat( data, is( "other" ) );
|
||||
|
||||
// position at the second (last) row
|
||||
assertThat( results.next(), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "value" ) );
|
||||
|
||||
// position after the second (last) row
|
||||
assertThat( results.next(), is( false ) );
|
||||
|
||||
// position back to the second row
|
||||
assertThat( results.previous(), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "value" ) );
|
||||
|
||||
// position back to the first row
|
||||
assertThat( results.previous(), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "other" ) );
|
||||
|
||||
// position before the first row
|
||||
assertThat( results.previous(), is( false ) );
|
||||
assertThat( results.previous(), is( false ) );
|
||||
|
||||
assertThat( results.last(), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "value" ) );
|
||||
|
||||
assertThat( results.first(), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "other" ) );
|
||||
|
||||
assertThat( results.scroll( 1 ), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "value" ) );
|
||||
|
||||
assertThat( results.scroll( -1 ), is( true ) );
|
||||
data = results.get();
|
||||
assertThat( data, is( "other" ) );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollSelectionTuple(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Tuple> query = session.createQuery( SINGLE_SELECTION_QUERY, Tuple.class );
|
||||
verifyScroll(
|
||||
query,
|
||||
(tuple) -> {
|
||||
assertThat( tuple.getElements().size(), is( 1 ) );
|
||||
final TupleElement<?> element = tuple.getElements().get( 0 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( String.class ) );
|
||||
assertThat( element.getAlias(), nullValue() );
|
||||
|
||||
assertThat( tuple.toArray().length, is( 1 ) );
|
||||
|
||||
final Object byPosition = tuple.get( 0 );
|
||||
assertThat( byPosition, is( "value" ) );
|
||||
|
||||
try {
|
||||
tuple.get( "data" );
|
||||
fail( "Expecting IllegalArgumentException per JPA spec" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollAliasedSelectionTuple(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Tuple> query = session.createQuery( SINGLE_ALIASED_SELECTION_QUERY, Tuple.class );
|
||||
verifyScroll(
|
||||
query,
|
||||
(tuple) -> {
|
||||
assertThat( tuple.getElements().size(), is( 1 ) );
|
||||
final TupleElement<?> element = tuple.getElements().get( 0 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( String.class ) );
|
||||
assertThat( element.getAlias(), is( "state" ) );
|
||||
|
||||
assertThat( tuple.toArray().length, is( 1 ) );
|
||||
|
||||
final Object byPosition = tuple.get( 0 );
|
||||
assertThat( byPosition, is( "value" ) );
|
||||
|
||||
final Object byName = tuple.get( "state" );
|
||||
assertThat( byName, is( "value" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollSelectionsTuple(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Tuple> query = session.createQuery( MULTI_SELECTION_QUERY, Tuple.class );
|
||||
verifyScroll(
|
||||
query,
|
||||
(tuple) -> {
|
||||
assertThat( tuple.getElements().size(), is( 2 ) );
|
||||
|
||||
{
|
||||
final TupleElement<?> element = tuple.getElements().get( 0 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( Integer.class ) );
|
||||
assertThat( element.getAlias(), nullValue() );
|
||||
}
|
||||
|
||||
{
|
||||
final TupleElement<?> element = tuple.getElements().get( 1 );
|
||||
assertThat( element.getJavaType(), typeCompatibleWith( String.class ) );
|
||||
assertThat( element.getAlias(), nullValue() );
|
||||
}
|
||||
|
||||
assertThat( tuple.toArray().length, is( 2 ) );
|
||||
|
||||
{
|
||||
final Object byPosition = tuple.get( 0 );
|
||||
assertThat( byPosition, is( 1 ) );
|
||||
|
||||
try {
|
||||
tuple.get( "id" );
|
||||
fail( "Expecting IllegalArgumentException per JPA spec" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
final Object byPosition = tuple.get( 1 );
|
||||
assertThat( byPosition, is( "value" ) );
|
||||
|
||||
try {
|
||||
tuple.get( "data" );
|
||||
fail( "Expecting IllegalArgumentException per JPA spec" );
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected outcome
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollSelection(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<String> query = session.createQuery( SINGLE_SELECTION_QUERY, String.class );
|
||||
verifyScroll(
|
||||
query,
|
||||
(data) -> assertThat( data, is( "value" ) )
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollSelections(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
final QueryImplementor<Object[]> query = session.createQuery( MULTI_SELECTION_QUERY, Object[].class );
|
||||
verifyScroll(
|
||||
query,
|
||||
(values) -> {
|
||||
assertThat( values[0], is( 1 ) );
|
||||
assertThat( values[1], is( "value" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static <R> void verifyScroll(Query<R> query, Consumer<R> validator) {
|
||||
try ( final ScrollableResults<R> results = query.scroll( ScrollMode.FORWARD_ONLY ) ) {
|
||||
assertThat( results.next(), is( true ) );
|
||||
validator.accept( results.get() );
|
||||
}
|
||||
|
||||
try ( final ScrollableResults<R> results = query.scroll( ScrollMode.SCROLL_INSENSITIVE ) ) {
|
||||
assertThat( results.next(), is( true ) );
|
||||
validator.accept( results.get() );
|
||||
}
|
||||
|
||||
try ( final ScrollableResults<R> results = query.scroll( ScrollMode.SCROLL_SENSITIVE ) ) {
|
||||
assertThat( results.next(), is( true ) );
|
||||
validator.accept( results.get() );
|
||||
}
|
||||
|
||||
try ( final ScrollableResults<R> results = query.scroll( ScrollMode.SCROLL_INSENSITIVE ) ) {
|
||||
assertThat( results.next(), is( true ) );
|
||||
validator.accept( results.get() );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue