HHH-14624 Oracle from version 12 started supporting the syntax for pagination
This commit is contained in:
parent
2952b60cc3
commit
422b80b80d
|
@ -10,6 +10,9 @@ import org.hibernate.boot.model.TypeContributions;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
import org.hibernate.dialect.identity.Oracle12cIdentityColumnSupport;
|
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.ConfigurationService;
|
||||||
import org.hibernate.engine.config.spi.StandardConverters;
|
import org.hibernate.engine.config.spi.StandardConverters;
|
||||||
import org.hibernate.service.ServiceRegistry;
|
import org.hibernate.service.ServiceRegistry;
|
||||||
|
@ -24,6 +27,8 @@ import org.hibernate.type.WrappedMaterializedBlobType;
|
||||||
public class Oracle12cDialect extends Oracle10gDialect {
|
public class Oracle12cDialect extends Oracle10gDialect {
|
||||||
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
||||||
|
|
||||||
|
private static final AbstractLimitHandler LIMIT_HANDLER = Oracle12LimitHandler.INSTANCE;
|
||||||
|
|
||||||
public Oracle12cDialect() {
|
public Oracle12cDialect() {
|
||||||
super();
|
super();
|
||||||
getDefaultProperties().setProperty( Environment.BATCH_VERSIONED_DATA, "true" );
|
getDefaultProperties().setProperty( Environment.BATCH_VERSIONED_DATA, "true" );
|
||||||
|
@ -62,4 +67,9 @@ public class Oracle12cDialect extends Oracle10gDialect {
|
||||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
return new Oracle12cIdentityColumnSupport();
|
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.PreparedStatement;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.QueryParameters;
|
||||||
import org.hibernate.engine.spi.RowSelection;
|
import org.hibernate.engine.spi.RowSelection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,6 +45,18 @@ public interface LimitHandler {
|
||||||
*/
|
*/
|
||||||
String processSql(String sql, RowSelection selection);
|
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.
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -2021,7 +2021,7 @@ public abstract class Loader {
|
||||||
final LimitHandler limitHandler = getLimitHandler(
|
final LimitHandler limitHandler = getLimitHandler(
|
||||||
queryParameters.getRowSelection()
|
queryParameters.getRowSelection()
|
||||||
);
|
);
|
||||||
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
|
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters );
|
||||||
|
|
||||||
// Adding locks and comments.
|
// Adding locks and comments.
|
||||||
sql = preprocessSQL( sql, queryParameters, getFactory(), afterLoadActions );
|
sql = preprocessSQL( sql, queryParameters, getFactory(), afterLoadActions );
|
||||||
|
|
|
@ -149,7 +149,7 @@ public abstract class AbstractLoadPlanBasedLoader {
|
||||||
final LimitHandler limitHandler = getLimitHandler(
|
final LimitHandler limitHandler = getLimitHandler(
|
||||||
queryParameters.getRowSelection()
|
queryParameters.getRowSelection()
|
||||||
);
|
);
|
||||||
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters.getRowSelection() );
|
String sql = limitHandler.processSql( queryParameters.getFilteredSQL(), queryParameters );
|
||||||
|
|
||||||
// Adding locks and comments.
|
// Adding locks and comments.
|
||||||
sql = session.getJdbcServices().getJdbcEnvironment().getDialect()
|
sql = session.getJdbcServices().getJdbcEnvironment().getDialect()
|
||||||
|
|
Loading…
Reference in New Issue