From 08d5d994dc0f6547ae1403d8f6c8058a8821f056 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 29 Nov 2012 12:33:54 -0600 Subject: [PATCH] HHH-1168 - Problem combining locking and paging on Oracle (cherry picked from commit 6e71a0907eba1a83ef50f3f6d4bf860c7df292e5) --- .../java/org/hibernate/dialect/Dialect.java | 4 +- .../hibernate/dialect/Oracle8iDialect.java | 4 +- .../hibernate/internal/CoreMessageLogger.java | 6 +-- .../java/org/hibernate/loader/Loader.java | 33 +++++------- .../loader/criteria/CriteriaLoader.java | 53 ++++++------------- .../org/hibernate/loader/hql/QueryLoader.java | 7 +-- 6 files changed, 38 insertions(+), 69 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 01366ac40e..499637ff36 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2321,7 +2321,7 @@ public abstract class Dialect implements ConversionContext { return false; } - public boolean supportsLockingAndPaging() { - return true; + public boolean useFollowOnLocking() { + return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java index d18d13cfcf..faa634c637 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java @@ -578,7 +578,7 @@ public class Oracle8iDialect extends Dialect { } @Override - public boolean supportsLockingAndPaging() { - return false; + public boolean useFollowOnLocking() { + return true; } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java index f5084cf9d2..e9dedd8441 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java @@ -1586,9 +1586,9 @@ public interface CoreMessageLogger extends BasicLogger { @LogMessage(level = WARN) @Message( - value = "Encountered request which combined locking and paging, however dialect reports that database does " + - "not support that combination. Results will be locked after initial query executes", + value = "Encountered request for locking however dialect reports that database prefers locking be done in a " + + "separate select; results will be locked after initial query executes", id = 444 ) - void delayedLockingDueToPaging(); + void usingFollowOnLocking(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 02e85bfa9b..c187d5abf5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -249,30 +249,23 @@ public abstract class Loader { public void afterLoad(SessionImplementor session, Object entity, Loadable persister); } - protected boolean shouldDelayLockingDueToPaging( - String sql, + protected boolean shouldUseFollowOnLocking( QueryParameters parameters, Dialect dialect, List afterLoadActions) { - final LockOptions lockOptions = parameters.getLockOptions(); - final RowSelection rowSelection = parameters.getRowSelection(); - final LimitHandler limitHandler = dialect.buildLimitHandler( sql, rowSelection ); - if ( LimitHelper.useLimit( limitHandler, rowSelection ) ) { - // user has requested a combination of paging and locking. See if the dialect supports that - // (ahem, Oracle...) - if ( ! dialect.supportsLockingAndPaging() ) { - LOG.delayedLockingDueToPaging(); - afterLoadActions.add( - new AfterLoadAction() { - private final LockOptions originalLockOptions = lockOptions.makeCopy(); - @Override - public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { - ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); - } + if ( dialect.useFollowOnLocking() ) { + LOG.usingFollowOnLocking(); + final LockOptions lockOptions = parameters.getLockOptions(); + afterLoadActions.add( + new AfterLoadAction() { + private final LockOptions originalLockOptions = lockOptions.makeCopy(); + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( originalLockOptions ).lock( persister.getEntityName(), entity ); } - ); - parameters.setLockOptions( new LockOptions() ); - } + } + ); + parameters.setLockOptions( new LockOptions() ); return true; } return false; diff --git a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index a402ae8ee7..be8aabd7bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -38,11 +38,8 @@ import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.QueryParameters; -import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CriteriaImpl; @@ -207,43 +204,27 @@ public class CriteriaLoader extends OuterJoinLoader { return sql; } - // user is request locking, lets see if we can apply locking directly to the SQL... + if ( dialect.useFollowOnLocking() ) { + // Dialect prefers to perform locking in a separate step + LOG.usingFollowOnLocking(); + final LockOptions lockOptionsToUse = new LockOptions(); + lockOptionsToUse.setLockMode( lockOptions.getEffectiveLockMode( "this_" ) ); + lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); + lockOptionsToUse.setScope( lockOptions.getScope() ); - // some dialects wont allow locking with paging... - final RowSelection rowSelection = parameters.getRowSelection(); - final LimitHandler limitHandler = dialect.buildLimitHandler( sql, rowSelection ); - if ( LimitHelper.useLimit( limitHandler, rowSelection ) ) { - // user has requested a combination of paging and locking. See if the dialect supports that - // (ahem, Oracle...) - if ( ! dialect.supportsLockingAndPaging() ) { - LOG.delayedLockingDueToPaging(); - - // this one is kind of ugly. currently we do not track the needed alias-to-entity - // mapping into the "hydratedEntities" which drives these callbacks - // so for now apply the root lock mode to all. The root lock mode is listed in - // the alias specific map under the alias "this_"... - final LockOptions lockOptionsToUse = new LockOptions(); - lockOptionsToUse.setLockMode( lockOptions.getEffectiveLockMode( "this_" ) ); - lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); - lockOptionsToUse.setScope( lockOptions.getScope() ); - - afterLoadActions.add( - new AfterLoadAction() { - @Override - public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { - ( (Session) session ).buildLockRequest( lockOptionsToUse ) - .lock( persister.getEntityName(), entity ); - } + afterLoadActions.add( + new AfterLoadAction() { + @Override + public void afterLoad(SessionImplementor session, Object entity, Loadable persister) { + ( (Session) session ).buildLockRequest( lockOptionsToUse ) + .lock( persister.getEntityName(), entity ); } - ); - parameters.setLockOptions( new LockOptions() ); - return sql; - } + } + ); + parameters.setLockOptions( new LockOptions() ); + return sql; } - // there are other conditions we might want to add here, such as checking the result types etc - // but those are better served after we have redone the SQL generation to use ASTs. - final LockOptions locks = new LockOptions(lockOptions.getLockMode()); locks.setScope( lockOptions.getScope()); locks.setTimeOut( lockOptions.getTimeOut()); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java index c9a01dea03..c4be211c2d 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/hql/QueryLoader.java @@ -23,7 +23,6 @@ */ package org.hibernate.loader.hql; -import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -39,12 +38,8 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.QueryException; import org.hibernate.ScrollableResults; -import org.hibernate.Session; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.pagination.LimitHandler; -import org.hibernate.dialect.pagination.LimitHelper; import org.hibernate.engine.spi.QueryParameters; -import org.hibernate.engine.spi.RowSelection; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventSource; @@ -334,7 +329,7 @@ public class QueryLoader extends BasicLoader { // user is request locking, lets see if we can apply locking directly to the SQL... // some dialects wont allow locking with paging... - if ( shouldDelayLockingDueToPaging( sql, parameters, dialect, afterLoadActions ) ) { + if ( shouldUseFollowOnLocking( parameters, dialect, afterLoadActions ) ) { return sql; }