HHH-11194 - Add setting to allow enabling legacy 4.x LimitHandler behavior (removed delegation).
Fix broken test on SQL Server and propagate the legacy behavior even when we extend the SQL Server of the Ingres base Dialects
This commit is contained in:
parent
9219c67da6
commit
aa3f913857
|
@ -343,6 +343,12 @@ This configuration property is used by the https://docs.jboss.org/hibernate/orm/
|
|||
It follows a pattern similar to the ANSI SQL definition of global temporary table using a "session id" column to segment rows from the various sessions.
|
||||
|
||||
This configuration property defines the database catalog used for storing the temporary tables used for bulk HQL operations.
|
||||
|`hibernate.legacy_limit_handler` | `true` or `false` (default value) |
|
||||
Setting which indicates whether or not to use `org.hibernate.dialect.pagination.LimitHandler`
|
||||
implementations that sacrifices performance optimizations to allow legacy 4.x limit behavior.
|
||||
|
||||
Legacy 4.x behavior favored performing pagination in-memory by avoiding the use of the offset value, which is overall poor performance.
|
||||
In 5.x, the limit handler behavior favors performance, thus, if the dialect doesn't support offsets, an exception is thrown instead.
|
||||
|===================================================================================================================================================================================================================================
|
||||
|
||||
[[configurations-batch]]
|
||||
|
|
|
@ -1604,8 +1604,17 @@ public interface AvailableSettings {
|
|||
*/
|
||||
String MERGE_ENTITY_COPY_OBSERVER = "hibernate.event.merge.entity_copy_observer";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Setting which indicates whether or not to use {@link org.hibernate.dialect.pagination.LimitHandler}
|
||||
* implementations that sacrifices performance optimizations to allow legacy 4.x limit behavior.
|
||||
* </p>
|
||||
* Legacy 4.x behavior favored performing pagination in-memory by avoiding the use of the offset
|
||||
* value, which is overall poor performance. In 5.x, the limit handler behavior favors performance
|
||||
* thus if the dialect doesn't support offsets, an exception is thrown instead.
|
||||
* </p>
|
||||
* Default is {@code false}.
|
||||
*
|
||||
* @since 5.2.5
|
||||
*/
|
||||
String USE_LEGACY_LIMIT_HANDLERS = "hibernate.legacy_limit_handler";
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ import org.hibernate.type.StandardBasicTypes;
|
|||
|
||||
public class Cache71Dialect extends Dialect {
|
||||
|
||||
private final TopLimitHandler limitHandler;
|
||||
private LimitHandler limitHandler;
|
||||
|
||||
/**
|
||||
* Creates new <code>Cache71Dialect</code> instance. Sets up the JDBC /
|
||||
|
@ -208,7 +208,7 @@ public class Cache71Dialect extends Dialect {
|
|||
super();
|
||||
commonRegistration();
|
||||
register71Functions();
|
||||
this.limitHandler = new TopLimitHandler(true, true);
|
||||
this.limitHandler = new TopLimitHandler( true, true );
|
||||
}
|
||||
|
||||
protected final void commonRegistration() {
|
||||
|
@ -544,6 +544,9 @@ public class Cache71Dialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return super.getLimitHandler();
|
||||
}
|
||||
return limitHandler;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,28 @@ public class DB2390Dialect extends DB2Dialect {
|
|||
}
|
||||
};
|
||||
|
||||
private static final AbstractLimitHandler LEGACY_LIMIT_HANDLER = new AbstractLimitHandler() {
|
||||
@Override
|
||||
public String processSql(String sql, RowSelection selection) {
|
||||
return sql + " fetch first " + getMaxOrLimit( selection ) + " rows only";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMaxForLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsVariableLimit() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean supportsSequences() {
|
||||
return false;
|
||||
|
@ -86,12 +108,16 @@ public class DB2390Dialect extends DB2Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
return LIMIT_HANDLER;
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return LEGACY_LIMIT_HANDLER;
|
||||
}
|
||||
else {
|
||||
return LIMIT_HANDLER;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return new DB2390IdentityColumnSupport();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.ScrollMode;
|
|||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Sequence;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.function.CastFunction;
|
||||
import org.hibernate.dialect.function.SQLFunction;
|
||||
|
@ -53,6 +54,8 @@ import org.hibernate.dialect.pagination.LegacyLimitHandler;
|
|||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
import org.hibernate.engine.jdbc.LobCreator;
|
||||
import org.hibernate.engine.jdbc.env.internal.DefaultSchemaNameResolver;
|
||||
import org.hibernate.engine.jdbc.env.spi.AnsiSqlKeywords;
|
||||
|
@ -146,6 +149,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
private final UniqueDelegate uniqueDelegate;
|
||||
|
||||
private boolean legacyLimitHandlerBehavior;
|
||||
|
||||
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -307,7 +311,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
* @param serviceRegistry The service registry
|
||||
*/
|
||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
// by default, nothing to do
|
||||
resolveLegacyLimitHandlerBehavior( serviceRegistry );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2859,4 +2863,19 @@ public abstract class Dialect implements ConversionContext {
|
|||
public boolean supportsNationalizedTypes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isLegacyLimitHandlerBehaviorEnabled() {
|
||||
return legacyLimitHandlerBehavior;
|
||||
}
|
||||
|
||||
private void resolveLegacyLimitHandlerBehavior(ServiceRegistry serviceRegistry) {
|
||||
// HHH-11194
|
||||
// Temporary solution to set whether legacy limit handler behavior should be used.
|
||||
final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class );
|
||||
legacyLimitHandlerBehavior = configurationService.getSetting(
|
||||
AvailableSettings.USE_LEGACY_LIMIT_HANDLERS,
|
||||
StandardConverters.BOOLEAN,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.dialect.function.VarArgsSQLFunction;
|
|||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.InformixIdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.FirstLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LegacyFirstLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.unique.InformixUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
|
@ -178,6 +179,9 @@ public class InformixDialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return LegacyFirstLimitHandler.INSTANCE;
|
||||
}
|
||||
return FirstLimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.dialect.function.SQLFunctionTemplate;
|
|||
import org.hibernate.dialect.function.StandardSQLFunction;
|
||||
import org.hibernate.dialect.function.VarArgsSQLFunction;
|
||||
import org.hibernate.dialect.pagination.FirstLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LegacyFirstLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
@ -47,6 +48,7 @@ import org.hibernate.type.StandardBasicTypes;
|
|||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class IngresDialect extends Dialect {
|
||||
|
||||
/**
|
||||
* Constructs a IngresDialect
|
||||
*/
|
||||
|
@ -220,6 +222,9 @@ public class IngresDialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return LegacyFirstLimitHandler.INSTANCE;
|
||||
}
|
||||
return FirstLimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,28 @@ public class RDMSOS2200Dialect extends Dialect {
|
|||
}
|
||||
};
|
||||
|
||||
private static final AbstractLimitHandler LEGACY_LIMIT_HANDLER = new AbstractLimitHandler() {
|
||||
@Override
|
||||
public String processSql(String sql, RowSelection selection) {
|
||||
return sql + " fetch first " + getMaxOrLimit( selection ) + " rows only ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimitOffset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsVariableLimit() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a RDMSOS2200Dialect
|
||||
*/
|
||||
|
@ -343,6 +365,9 @@ public class RDMSOS2200Dialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return LEGACY_LIMIT_HANDLER;
|
||||
}
|
||||
return LIMIT_HANDLER;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.dialect.function.SQLFunctionTemplate;
|
|||
import org.hibernate.dialect.function.StandardSQLFunction;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.LegacyLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.TopLimitHandler;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
@ -84,6 +85,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return new LegacyLimitHandler( this );
|
||||
}
|
||||
return limitHandler;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hibernate.dialect.lock.PessimisticWriteUpdateLockingStrategy;
|
|||
import org.hibernate.dialect.lock.SelectLockingStrategy;
|
||||
import org.hibernate.dialect.lock.UpdateLockingStrategy;
|
||||
import org.hibernate.dialect.pagination.FirstLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LegacyFirstLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.hql.spi.id.IdTableSupportStandardImpl;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
|
@ -84,7 +85,6 @@ public class TimesTenDialect extends Dialect {
|
|||
registerFunction( "sysdate", new NoArgSQLFunction( "sysdate", StandardBasicTypes.TIMESTAMP, false ) );
|
||||
registerFunction( "getdate", new NoArgSQLFunction( "getdate", StandardBasicTypes.TIMESTAMP, false ) );
|
||||
registerFunction( "nvl", new StandardSQLFunction( "nvl" ) );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,6 +159,9 @@ public class TimesTenDialect extends Dialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( isLegacyLimitHandlerBehaviorEnabled() ) {
|
||||
return LegacyFirstLimitHandler.INSTANCE;
|
||||
}
|
||||
return FirstLimitHandler.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.dialect.pagination;
|
||||
|
||||
import java.util.Locale;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
|
||||
|
||||
/**
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
public class FirstLimitHandler extends AbstractLimitHandler {
|
||||
public class FirstLimitHandler extends LegacyFirstLimitHandler {
|
||||
|
||||
public static final FirstLimitHandler INSTANCE = new FirstLimitHandler();
|
||||
|
||||
|
@ -27,29 +26,6 @@ public class FirstLimitHandler extends AbstractLimitHandler {
|
|||
if ( hasOffset ) {
|
||||
throw new UnsupportedOperationException( "query result offset is not supported" );
|
||||
}
|
||||
return new StringBuilder( sql.length() + 16 )
|
||||
.append( sql )
|
||||
.insert( sql.toLowerCase(Locale.ROOT).indexOf( "select" ) + 6, " first " + getMaxOrLimit( selection ) )
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMaxForLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimitOffset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsVariableLimit() {
|
||||
return false;
|
||||
return super.processSql( sql, selection );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.engine.spi.RowSelection;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class LegacyFirstLimitHandler extends AbstractLimitHandler {
|
||||
|
||||
public static final LegacyFirstLimitHandler INSTANCE = new LegacyFirstLimitHandler();
|
||||
|
||||
LegacyFirstLimitHandler() {
|
||||
// NOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processSql(String sql, RowSelection selection) {
|
||||
return new StringBuilder( sql.length() + 16 )
|
||||
.append( sql )
|
||||
.insert( sql.toLowerCase( Locale.ROOT).indexOf( "select" ) + 6, " first " + getMaxOrLimit( selection ) )
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useMaxForLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLimitOffset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsVariableLimit() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.RequiresDialects;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class LegacyLimitHandlerTestCase extends
|
||||
BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
private final String TEST_SQL = "SELECT field FROM table";
|
||||
|
||||
@Override
|
||||
protected void addSettings(Map settings) {
|
||||
settings.put( AvailableSettings.USE_LEGACY_LIMIT_HANDLERS, "true" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11194")
|
||||
@RequiresDialect(Cache71Dialect.class)
|
||||
public void testCache71DialectLegacyLimitHandler() {
|
||||
assertLimitHandlerEquals( "SELECT TOP ? field FROM table" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11194")
|
||||
@RequiresDialect(DB2390Dialect.class)
|
||||
public void testDB2390DialectLegacyLimitHandler() {
|
||||
assertLimitHandlerEquals( "SELECT field FROM table fetch first 6 rows only" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11194")
|
||||
@RequiresDialects({ @RequiresDialect(InformixDialect.class), @RequiresDialect(IngresDialect.class)})
|
||||
public void testInformixDialectOrIngresDialectLegacyLimitHandler() {
|
||||
assertLimitHandlerEquals( "SELECT first 6 field FROM table" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11194")
|
||||
@RequiresDialect(RDMSOS2200Dialect.class)
|
||||
public void testRDMSOS2200DialectLegacyLimitHandler() {
|
||||
assertLimitHandlerEquals( "SELECT field FROM table fetch first 5 rows only" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11194")
|
||||
@RequiresDialect(value = SQLServerDialect.class, strictMatching = true)
|
||||
public void testSQLServerDialectLegacyLimitHandler() {
|
||||
assertLimitHandlerEquals( "SELECT top 6 field FROM table" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11194")
|
||||
@RequiresDialect(TimesTenDialect.class)
|
||||
public void testTimesTenDialectLegacyLimitHandler() {
|
||||
assertLimitHandlerEquals( "SELECT first 6 field FROM table" );
|
||||
}
|
||||
|
||||
private void assertLimitHandlerEquals(String sql) {
|
||||
assertEquals( sql, getDialect().getLimitHandler().processSql( TEST_SQL, toRowSelection( 1, 5 ) ) );
|
||||
}
|
||||
|
||||
private RowSelection toRowSelection(int firstRow, int maxRows) {
|
||||
RowSelection selection = new RowSelection();
|
||||
selection.setFirstRow( firstRow );
|
||||
selection.setMaxRows( maxRows );
|
||||
return selection;
|
||||
}
|
||||
}
|
|
@ -60,6 +60,29 @@ implement JPA methods now in core I decided to implement more of a composition a
|
|||
has been moved into the `org.hibernate.Cache` and `org.hibernate.engine.spi.CacheImplementor` contracts
|
||||
helping implement JPA's `javax.persistence.Cache` role.
|
||||
|
||||
== LimitHandler changes
|
||||
|
||||
In Hibernate 4.3, dialect implementations that did not support a limit offset would fetch all rows for a query and
|
||||
perform pagination in-memory. This solution, while functional, could have severe performance penalties. In 5.x,
|
||||
we prefered to favor performance optimizations which meant dialect implementations would throw an exception if a
|
||||
limit offset was specified but the dialect didn't support such syntax.
|
||||
|
||||
As of 5.2.5.Final, we have introduced a new setting, `hibernate.legacy_limit_handler`, that is designed to allow
|
||||
users to enable the legacy 4.3 limit handler behavior. By default, this setting is _false_.
|
||||
|
||||
The specific dialects impacted by this change are restricted to the following.
|
||||
|
||||
* Cache71Dialect
|
||||
* DB2390Dialect
|
||||
* InformixDialect
|
||||
* IngresDialect
|
||||
* RDMSOS2200Dialect
|
||||
* SQLServerDialect
|
||||
* TimesTenDialect
|
||||
|
||||
NOTE: If a dialect that extends any in the above list but overrides the limit handler implementation, then those
|
||||
dialects remain unchanged, e.g. SQLServer2005Dialect.
|
||||
|
||||
|
||||
== Misc
|
||||
|
||||
|
|
Loading…
Reference in New Issue