package org.hibernate.dialect.pagination;

import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.hibernate.engine.spi.RowSelection;

Default implementation of LimitHandler interface.
Author:Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
/** * Default implementation of {@link LimitHandler} interface. * * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */
public abstract class AbstractLimitHandler implements LimitHandler { protected final String sql; protected final RowSelection selection;
Default constructor. SQL query and selection criteria required to allow LIMIT clause pre-processing.
Params:
  • sql – SQL query.
  • selection – Selection criteria. null in case of unlimited number of rows.
/** * Default constructor. SQL query and selection criteria required to allow LIMIT clause pre-processing. * * @param sql SQL query. * @param selection Selection criteria. {@code null} in case of unlimited number of rows. */
public AbstractLimitHandler(String sql, RowSelection selection) { this.sql = sql; this.selection = selection; } @Override public boolean supportsLimit() { return false; } @Override public boolean supportsLimitOffset() { return supportsLimit(); }
Does this handler support bind variables (i.e., prepared statement parameters) for its limit/offset?
Returns:True if bind variables can be used; false otherwise.
/** * Does this handler support bind variables (i.e., prepared statement * parameters) for its limit/offset? * * @return True if bind variables can be used; false otherwise. */
public boolean supportsVariableLimit() { return supportsLimit(); }
ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit. Does this dialect require us to bind the parameters in reverse order?
Returns:true if the correct order is limit, offset
/** * ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit. * Does this dialect require us to bind the parameters in reverse order? * * @return true if the correct order is limit, offset */
public boolean bindLimitParametersInReverseOrder() { return false; }
Does the LIMIT clause come at the start of the SELECT statement, rather than at the end?
Returns:true if limit parameters should come before other parameters
/** * Does the <tt>LIMIT</tt> clause come at the start of the * <tt>SELECT</tt> statement, rather than at the end? * * @return true if limit parameters should come before other parameters */
public boolean bindLimitParametersFirst() { return false; }
Does the LIMIT clause take a "maximum" row number instead of a total number of returned rows?

This is easiest understood via an example. Consider you have a table with 20 rows, but you only want to retrieve rows number 11 through 20. Generally, a limit with offset would say that the offset = 11 and the limit = 10 (we only want 10 rows at a time); this is specifying the total number of returned rows. Some dialects require that we instead specify offset = 11 and limit = 20, where 20 is the "last" row we want relative to offset (i.e. total number of rows = 20 - 11 = 9)

So essentially, is limit relative from offset? Or is limit absolute?
Returns:True if limit is relative from offset; false otherwise.
/** * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead * of a total number of returned rows? * <p/> * This is easiest understood via an example. Consider you have a table * with 20 rows, but you only want to retrieve rows number 11 through 20. * Generally, a limit with offset would say that the offset = 11 and the * limit = 10 (we only want 10 rows at a time); this is specifying the * total number of returned rows. Some dialects require that we instead * specify offset = 11 and limit = 20, where 20 is the "last" row we want * relative to offset (i.e. total number of rows = 20 - 11 = 9) * <p/> * So essentially, is limit relative from offset? Or is limit absolute? * * @return True if limit is relative from offset; false otherwise. */
public boolean useMaxForLimit() { return false; }
Generally, if there is no limit applied to a Hibernate query we do not apply any limits to the SQL query. This option forces that the limit be written to the SQL query.
Returns:True to force limit into SQL query even if none specified in Hibernate query; false otherwise.
/** * Generally, if there is no limit applied to a Hibernate query we do not apply any limits * to the SQL query. This option forces that the limit be written to the SQL query. * * @return True to force limit into SQL query even if none specified in Hibernate query; false otherwise. */
public boolean forceLimitUsage() { return false; }
Hibernate APIs explicitly state that setFirstResult() should be a zero-based offset. Here we allow the Dialect a chance to convert that value based on what the underlying db or driver will expect.

NOTE: what gets passed into AbstractLimitHandler(String, RowSelection) is the zero-based offset. Dialects which do not supportsVariableLimit should take care to perform any needed first-row-conversion calls prior to injecting the limit values into the SQL string.
Params:
  • zeroBasedFirstResult – The user-supplied, zero-based first row offset.
See Also:
Returns:The corresponding db/dialect specific offset.
/** * Hibernate APIs explicitly state that setFirstResult() should be a zero-based offset. Here we allow the * Dialect a chance to convert that value based on what the underlying db or driver will expect. * <p/> * NOTE: what gets passed into {@link #AbstractLimitHandler(String, RowSelection)} is the zero-based offset. * Dialects which do not {@link #supportsVariableLimit} should take care to perform any needed first-row-conversion * calls prior to injecting the limit values into the SQL string. * * @param zeroBasedFirstResult The user-supplied, zero-based first row offset. * * @return The corresponding db/dialect specific offset. * * @see org.hibernate.Query#setFirstResult * @see org.hibernate.Criteria#setFirstResult */
public int convertToFirstRowValue(int zeroBasedFirstResult) { return zeroBasedFirstResult; } @Override public String getProcessedSql() { throw new UnsupportedOperationException( "Paged queries not supported by " + getClass().getName() ); } @Override public int bindLimitParametersAtStartOfQuery(PreparedStatement statement, int index) throws SQLException { return bindLimitParametersFirst() ? bindLimitParameters( statement, index ) : 0; } @Override public int bindLimitParametersAtEndOfQuery(PreparedStatement statement, int index) throws SQLException { return !bindLimitParametersFirst() ? bindLimitParameters( statement, index ) : 0; } @Override public void setMaxRows(PreparedStatement statement) throws SQLException { }
Default implementation of binding parameter values needed by the LIMIT clause.
Params:
  • statement – Statement to which to bind limit parameter values.
  • index – Index from which to start binding.
Throws:
  • SQLException – Indicates problems binding parameter values.
Returns:The number of parameter values bound.
/** * Default implementation of binding parameter values needed by the LIMIT clause. * * @param statement Statement to which to bind limit parameter values. * @param index Index from which to start binding. * @return The number of parameter values bound. * @throws SQLException Indicates problems binding parameter values. */
protected int bindLimitParameters(PreparedStatement statement, int index) throws SQLException { if ( !supportsVariableLimit() || !LimitHelper.hasMaxRows( selection ) ) { return 0; } final int firstRow = convertToFirstRowValue( LimitHelper.getFirstRow( selection ) ); final int lastRow = getMaxOrLimit(); final boolean hasFirstRow = supportsLimitOffset() && ( firstRow > 0 || forceLimitUsage() ); final boolean reverse = bindLimitParametersInReverseOrder(); if ( hasFirstRow ) { statement.setInt( index + ( reverse ? 1 : 0 ), firstRow ); } statement.setInt( index + ( reverse || !hasFirstRow ? 0 : 1 ), lastRow ); return hasFirstRow ? 2 : 1; }
Some dialect-specific LIMIT clauses require the maximum last row number (aka, first_row_number + total_row_count), while others require the maximum returned row count (the total maximum number of rows to return).
Returns:The appropriate value to bind into the limit clause.
/** * Some dialect-specific LIMIT clauses require the maximum last row number * (aka, first_row_number + total_row_count), while others require the maximum * returned row count (the total maximum number of rows to return). * * @return The appropriate value to bind into the limit clause. */
protected int getMaxOrLimit() { final int firstRow = convertToFirstRowValue( LimitHelper.getFirstRow( selection ) ); final int lastRow = selection.getMaxRows(); return useMaxForLimit() ? lastRow + firstRow : lastRow; } }