HHH-14624 Oracle from version 12 started supporting the syntax for pagination
(cherry picked from commit 422b80b80d
)
This commit is contained in:
parent
120d17dcb4
commit
75a00df126
|
@ -10,6 +10,9 @@ import org.hibernate.boot.model.TypeContributions;
|
|||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.AbstractLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.Oracle12LimitHandler;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
|
@ -24,6 +27,8 @@ import org.hibernate.type.WrappedMaterializedBlobType;
|
|||
public class Oracle12cDialect extends Oracle10gDialect {
|
||||
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
||||
|
||||
private static final AbstractLimitHandler LIMIT_HANDLER = Oracle12LimitHandler.INSTANCE;
|
||||
|
||||
public Oracle12cDialect() {
|
||||
super();
|
||||
getDefaultProperties().setProperty( Environment.BATCH_VERSIONED_DATA, "true" );
|
||||
|
@ -62,4 +67,9 @@ public class Oracle12cDialect extends Oracle10gDialect {
|
|||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return new Oracle12cIdentityColumnSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return LIMIT_HANDLER;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.dialect.pagination;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
|
||||
/**
|
||||
|
@ -44,6 +45,18 @@ public interface LimitHandler {
|
|||
*/
|
||||
String processSql(String sql, RowSelection selection);
|
||||
|
||||
/**
|
||||
* Return processed SQL query.
|
||||
*
|
||||
* @param sql the SQL query to process.
|
||||
* @param queryParameters the queryParameters.
|
||||
*
|
||||
* @return Query statement with LIMIT clause applied.
|
||||
*/
|
||||
default String processSql(String sql, QueryParameters queryParameters ){
|
||||
return processSql( sql, queryParameters.getRowSelection() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind parameter values needed by the LIMIT clause before original SELECT statement.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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.dialect.pagination;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.engine.spi.QueryParameters;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
|
||||
/**
|
||||
* A {@link LimitHandler} for databases which support the
|
||||
* ANSI SQL standard syntax {@code FETCH FIRST m ROWS ONLY}
|
||||
* and {@code OFFSET n ROWS FETCH NEXT m ROWS ONLY}.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class Oracle12LimitHandler extends AbstractLimitHandler {
|
||||
|
||||
public boolean bindLimitParametersInReverseOrder;
|
||||
public boolean useMaxForLimit;
|
||||
|
||||
public static final Oracle12LimitHandler INSTANCE = new Oracle12LimitHandler();
|
||||
|
||||
Oracle12LimitHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processSql(String sql, RowSelection selection) {
|
||||
final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
|
||||
final boolean hasMaxRows = LimitHelper.hasMaxRows( selection );
|
||||
|
||||
if ( !hasMaxRows ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
return processSql( sql, getForUpdateIndex( sql ), hasFirstRow );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processSql(String sql, QueryParameters queryParameters) {
|
||||
final RowSelection selection = queryParameters.getRowSelection();
|
||||
|
||||
final boolean hasFirstRow = LimitHelper.hasFirstRow( selection );
|
||||
final boolean hasMaxRows = LimitHelper.hasMaxRows( selection );
|
||||
|
||||
if ( !hasMaxRows ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
final LockOptions lockOptions = queryParameters.getLockOptions();
|
||||
if ( lockOptions != null ) {
|
||||
final LockMode lockMode = lockOptions.getLockMode();
|
||||
switch ( lockMode ) {
|
||||
case UPGRADE:
|
||||
case PESSIMISTIC_READ:
|
||||
case PESSIMISTIC_WRITE:
|
||||
case UPGRADE_NOWAIT:
|
||||
case FORCE:
|
||||
case PESSIMISTIC_FORCE_INCREMENT:
|
||||
case UPGRADE_SKIPLOCKED:
|
||||
return processSql( sql, selection );
|
||||
default:
|
||||
return processSqlOffsetFetch( sql, hasFirstRow );
|
||||
}
|
||||
}
|
||||
return processSqlOffsetFetch( sql, hasFirstRow );
|
||||
}
|
||||
|
||||
private String processSqlOffsetFetch(String sql, boolean hasFirstRow) {
|
||||
|
||||
final int forUpdateLastIndex = getForUpdateIndex( sql );
|
||||
|
||||
if ( forUpdateLastIndex > -1 ) {
|
||||
return processSql( sql, forUpdateLastIndex, hasFirstRow );
|
||||
}
|
||||
|
||||
bindLimitParametersInReverseOrder = false;
|
||||
useMaxForLimit = false;
|
||||
|
||||
sql = normalizeStatement( sql );
|
||||
final int offsetFetchLength;
|
||||
final String offsetFetchString;
|
||||
if ( hasFirstRow ) {
|
||||
offsetFetchString = " offset ? rows fetch next ? rows only";
|
||||
}
|
||||
else {
|
||||
offsetFetchString = " fetch first ? rows only";
|
||||
}
|
||||
offsetFetchLength = sql.length() + offsetFetchString.length();
|
||||
|
||||
return new StringBuilder( offsetFetchLength ).append( sql ).append( offsetFetchString ).toString();
|
||||
}
|
||||
|
||||
private String processSql(String sql, int forUpdateIndex, boolean hasFirstRow) {
|
||||
bindLimitParametersInReverseOrder = true;
|
||||
useMaxForLimit = true;
|
||||
sql = normalizeStatement( sql );
|
||||
|
||||
String forUpdateClause = null;
|
||||
boolean isForUpdate = false;
|
||||
if ( forUpdateIndex > -1 ) {
|
||||
// save 'for update ...' and then remove it
|
||||
forUpdateClause = sql.substring( forUpdateIndex );
|
||||
sql = sql.substring( 0, forUpdateIndex - 1 );
|
||||
isForUpdate = true;
|
||||
}
|
||||
|
||||
final StringBuilder pagingSelect;
|
||||
|
||||
final int forUpdateClauseLength;
|
||||
if ( forUpdateClause == null ) {
|
||||
forUpdateClauseLength = 0;
|
||||
}
|
||||
else {
|
||||
forUpdateClauseLength = forUpdateClause.length() + 1;
|
||||
}
|
||||
|
||||
if ( hasFirstRow ) {
|
||||
pagingSelect = new StringBuilder( sql.length() + forUpdateClauseLength + 98 );
|
||||
pagingSelect.append( "select * from ( select row_.*, rownum rownum_ from ( " );
|
||||
pagingSelect.append( sql );
|
||||
pagingSelect.append( " ) row_ where rownum <= ?) where rownum_ > ?" );
|
||||
}
|
||||
else {
|
||||
pagingSelect = new StringBuilder( sql.length() + forUpdateClauseLength + 37 );
|
||||
pagingSelect.append( "select * from ( " );
|
||||
pagingSelect.append( sql );
|
||||
pagingSelect.append( " ) where rownum <= ?" );
|
||||
}
|
||||
|
||||
if ( isForUpdate ) {
|
||||
pagingSelect.append( " " );
|
||||
pagingSelect.append( forUpdateClause );
|
||||
}
|
||||
|
||||
return pagingSelect.toString();
|
||||
}
|
||||
|
||||
private String normalizeStatement(String sql) {
|
||||
return sql.trim().replaceAll( "\\s+", " " );
|
||||
}
|
||||
|
||||
private int getForUpdateIndex(String sql) {
|
||||
final int forUpdateLastIndex = sql.toLowerCase( Locale.ROOT ).lastIndexOf( "for update" );
|
||||
// We need to recognize cases like : select a from t where b = 'for update';
|
||||
final int lastIndexOfQuote = sql.lastIndexOf( "'" );
|
||||
if ( forUpdateLastIndex > -1 ) {
|
||||
if ( lastIndexOfQuote == -1 ) {
|
||||
return forUpdateLastIndex;
|
||||
}
|
||||
if ( lastIndexOfQuote > forUpdateLastIndex ) {
|
||||
return -1;
|
||||
}
|
||||
return forUpdateLastIndex;
|
||||
}
|
||||
return forUpdateLastIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bindLimitParametersInReverseOrder() {
|
||||
return bindLimitParametersInReverseOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMaxForLimit() {
|
||||
return useMaxForLimit;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -2004,7 +2004,7 @@ public abstract class Loader {
|
|||
final LimitHandler limitHandler = getLimitHandler(
|
||||
queryParameters.getRowSelection()
|
||||
);
|
||||
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
|
||||
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters );
|
||||
|
||||
// Adding locks and comments.
|
||||
sql = preprocessSQL( sql, queryParameters, getFactory(), afterLoadActions );
|
||||
|
|
|
@ -158,7 +158,7 @@ public abstract class AbstractLoadPlanBasedLoader {
|
|||
final LimitHandler limitHandler = getLimitHandler(
|
||||
queryParameters.getRowSelection()
|
||||
);
|
||||
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
|
||||
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters );
|
||||
|
||||
// Adding locks and comments.
|
||||
sql = session.getJdbcServices().getJdbcEnvironment().getDialect()
|
||||
|
|
Loading…
Reference in New Issue