HHH-1168 - Problem combining locking and paging on Oracle
This commit is contained in:
parent
c01dd40a65
commit
4b2871cfba
|
@ -2399,4 +2399,8 @@ public abstract class Dialect implements ConversionContext {
|
|||
public boolean forceLobAsLastValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsLockingAndPaging() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -577,4 +577,8 @@ public class Oracle8iDialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockingAndPaging() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -953,7 +953,8 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
|
|||
if ( stats ) startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, false, session );
|
||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, false, afterLoadActions, session );
|
||||
final PreparedStatement st = (PreparedStatement) rs.getStatement();
|
||||
HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer());
|
||||
Iterator result = new IteratorImpl( rs, st, session, queryParameters.isReadOnly( session ), returnTypes, getColumnNames(), hi );
|
||||
|
@ -1094,8 +1095,13 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException {
|
||||
protected String applyLocks(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) throws QueryException {
|
||||
// can't cache this stuff either (per-invocation)
|
||||
final LockOptions lockOptions = parameters.getLockOptions();
|
||||
final String result;
|
||||
if ( lockOptions == null ||
|
||||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
|
||||
|
|
|
@ -1583,4 +1583,12 @@ public interface CoreMessageLogger extends BasicLogger {
|
|||
id = 443
|
||||
)
|
||||
void tooManyInExpressions(String dialectName, int limit, String paramName, int size);
|
||||
|
||||
@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",
|
||||
id = 444
|
||||
)
|
||||
void delayedLockingDueToPaging();
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
|
|||
private Collection<String> querySpaces;
|
||||
|
||||
private final boolean callable;
|
||||
private final LockOptions lockOptions = new LockOptions();
|
||||
|
||||
/**
|
||||
* Constructs a SQLQueryImpl given a sql query defined in the mappings.
|
||||
|
@ -245,8 +246,8 @@ public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
|
|||
|
||||
@Override
|
||||
public LockOptions getLockOptions() {
|
||||
//we never need to apply locks to the SQL
|
||||
return null;
|
||||
//we never need to apply locks to the SQL, however the native-sql loader handles this specially
|
||||
return lockOptions;
|
||||
}
|
||||
|
||||
public SQLQuery addScalar(final String columnAlias, final Type type) {
|
||||
|
|
|
@ -26,6 +26,7 @@ package org.hibernate.internal;
|
|||
import java.sql.CallableStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -299,6 +300,8 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
|
|||
this.session = session;
|
||||
}
|
||||
|
||||
// todo : this would be a great way to add locking to stored procedure support (at least where returning entities).
|
||||
|
||||
public List processResultSet(ResultSet resultSet) throws SQLException {
|
||||
super.autoDiscoverTypes( resultSet );
|
||||
return super.processResultSet(
|
||||
|
@ -307,7 +310,8 @@ public class StoredProcedureOutputsImpl implements StoredProcedureOutputs {
|
|||
session,
|
||||
true,
|
||||
null,
|
||||
Integer.MAX_VALUE
|
||||
Integer.MAX_VALUE,
|
||||
Collections.<AfterLoadAction>emptyList()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.sql.SQLException;
|
|||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -47,6 +48,7 @@ import org.hibernate.LockOptions;
|
|||
import org.hibernate.QueryException;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.WrongClassException;
|
||||
import org.hibernate.cache.spi.FilterKey;
|
||||
|
@ -59,6 +61,7 @@ import org.hibernate.dialect.pagination.LimitHelper;
|
|||
import org.hibernate.dialect.pagination.NoopLimitHandler;
|
||||
import org.hibernate.engine.internal.TwoPhaseLoad;
|
||||
import org.hibernate.engine.jdbc.ColumnNameCache;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
|
@ -195,7 +198,11 @@ public abstract class Loader {
|
|||
* empty superclass implementation merely returns its first
|
||||
* argument.
|
||||
*/
|
||||
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws HibernateException {
|
||||
protected String applyLocks(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) throws HibernateException {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
@ -227,13 +234,48 @@ public abstract class Loader {
|
|||
/**
|
||||
* Modify the SQL, adding lock hints and comments, if necessary
|
||||
*/
|
||||
protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect)
|
||||
throws HibernateException {
|
||||
protected String preprocessSQL(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) throws HibernateException {
|
||||
sql = applyLocks( sql, parameters, dialect, afterLoadActions );
|
||||
return getFactory().getSettings().isCommentsEnabled()
|
||||
? prependComment( sql, parameters )
|
||||
: sql;
|
||||
}
|
||||
|
||||
sql = applyLocks( sql, parameters.getLockOptions(), dialect );
|
||||
protected static interface AfterLoadAction {
|
||||
public void afterLoad(SessionImplementor session, Object entity, Loadable persister);
|
||||
}
|
||||
|
||||
return getFactory().getSettings().isCommentsEnabled() ?
|
||||
prependComment( sql, parameters ) : sql;
|
||||
protected boolean shouldDelayLockingDueToPaging(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> 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 );
|
||||
}
|
||||
}
|
||||
);
|
||||
parameters.setLockOptions( new LockOptions() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String prependComment(String sql, QueryParameters parameters) {
|
||||
|
@ -843,7 +885,9 @@ public abstract class Loader {
|
|||
selection.getMaxRows() :
|
||||
Integer.MAX_VALUE;
|
||||
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, false, session );
|
||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
||||
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, false, afterLoadActions, session );
|
||||
final Statement st = rs.getStatement();
|
||||
|
||||
// would be great to move all this below here into another method that could also be used
|
||||
|
@ -853,7 +897,7 @@ public abstract class Loader {
|
|||
// that I could do the control breaking at the means to know when to stop
|
||||
|
||||
try {
|
||||
return processResultSet( rs, queryParameters, session, returnProxies, forcedResultTransformer, maxRows );
|
||||
return processResultSet( rs, queryParameters, session, returnProxies, forcedResultTransformer, maxRows, afterLoadActions );
|
||||
}
|
||||
finally {
|
||||
st.close();
|
||||
|
@ -867,7 +911,8 @@ public abstract class Loader {
|
|||
SessionImplementor session,
|
||||
boolean returnProxies,
|
||||
ResultTransformer forcedResultTransformer,
|
||||
int maxRows) throws SQLException {
|
||||
int maxRows,
|
||||
List<AfterLoadAction> afterLoadActions) throws SQLException {
|
||||
final int entitySpan = getEntityPersisters().length;
|
||||
final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
|
||||
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
|
||||
|
@ -902,8 +947,16 @@ public abstract class Loader {
|
|||
|
||||
LOG.tracev( "Done processing result set ({0} rows)", count );
|
||||
|
||||
initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) );
|
||||
if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );
|
||||
initializeEntitiesAndCollections(
|
||||
hydratedObjects,
|
||||
rs,
|
||||
session,
|
||||
queryParameters.isReadOnly( session ),
|
||||
afterLoadActions
|
||||
);
|
||||
if ( createSubselects ) {
|
||||
createSubselects( subselectResultKeys, queryParameters, session );
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
@ -989,8 +1042,22 @@ public abstract class Loader {
|
|||
final List hydratedObjects,
|
||||
final Object resultSetId,
|
||||
final SessionImplementor session,
|
||||
final boolean readOnly)
|
||||
throws HibernateException {
|
||||
final boolean readOnly) throws HibernateException {
|
||||
initializeEntitiesAndCollections(
|
||||
hydratedObjects,
|
||||
resultSetId,
|
||||
session,
|
||||
readOnly,
|
||||
Collections.<AfterLoadAction>emptyList()
|
||||
);
|
||||
}
|
||||
|
||||
private void initializeEntitiesAndCollections(
|
||||
final List hydratedObjects,
|
||||
final Object resultSetId,
|
||||
final SessionImplementor session,
|
||||
final boolean readOnly,
|
||||
List<AfterLoadAction> afterLoadActions) throws HibernateException {
|
||||
|
||||
final CollectionPersister[] collectionPersisters = getCollectionPersisters();
|
||||
if ( collectionPersisters != null ) {
|
||||
|
@ -1042,9 +1109,19 @@ public abstract class Loader {
|
|||
// split off from initializeEntity. It *must* occur after
|
||||
// endCollectionLoad to ensure the collection is in the
|
||||
// persistence context.
|
||||
if ( hydratedObjects!=null ) {
|
||||
if ( hydratedObjects != null ) {
|
||||
for ( Object hydratedObject : hydratedObjects ) {
|
||||
TwoPhaseLoad.postLoad( hydratedObject, session, post );
|
||||
if ( afterLoadActions != null ) {
|
||||
for ( AfterLoadAction afterLoadAction : afterLoadActions ) {
|
||||
final EntityEntry entityEntry = session.getPersistenceContext().getEntry( hydratedObject );
|
||||
if ( entityEntry == null ) {
|
||||
// big problem
|
||||
throw new HibernateException( "Could not locate EntityEntry immediately after two-phase load" );
|
||||
}
|
||||
afterLoadAction.afterLoad( session, hydratedObject, (Loadable) entityEntry.getPersister() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1721,15 +1798,17 @@ public abstract class Loader {
|
|||
protected ResultSet executeQueryStatement(
|
||||
final QueryParameters queryParameters,
|
||||
final boolean scroll,
|
||||
List<AfterLoadAction> afterLoadActions,
|
||||
final SessionImplementor session) throws SQLException {
|
||||
return executeQueryStatement( getSQLString(), queryParameters, scroll, session );
|
||||
return executeQueryStatement( getSQLString(), queryParameters, scroll, afterLoadActions, session );
|
||||
}
|
||||
|
||||
protected ResultSet executeQueryStatement(
|
||||
final String sqlStatement,
|
||||
final QueryParameters queryParameters,
|
||||
final boolean scroll,
|
||||
final SessionImplementor session) throws SQLException {
|
||||
String sqlStatement,
|
||||
QueryParameters queryParameters,
|
||||
boolean scroll,
|
||||
List<AfterLoadAction> afterLoadActions,
|
||||
SessionImplementor session) throws SQLException {
|
||||
|
||||
// Processing query filters.
|
||||
queryParameters.processFilters( sqlStatement, session );
|
||||
|
@ -1742,7 +1821,7 @@ public abstract class Loader {
|
|||
String sql = limitHandler.getProcessedSql();
|
||||
|
||||
// Adding locks and comments.
|
||||
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect() );
|
||||
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions );
|
||||
|
||||
final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session );
|
||||
return getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session );
|
||||
|
@ -2499,7 +2578,7 @@ public abstract class Loader {
|
|||
if ( stats ) startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, true, session );
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, true, Collections.<AfterLoadAction>emptyList(), session );
|
||||
final PreparedStatement st = (PreparedStatement) rs.getStatement();
|
||||
|
||||
if ( stats ) {
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.dialect.pagination.LimitHelper;
|
||||
|
@ -250,10 +252,11 @@ public class DynamicBatchingCollectionInitializerBuilder extends BatchingCollect
|
|||
selection.getMaxRows() :
|
||||
Integer.MAX_VALUE;
|
||||
|
||||
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, session );
|
||||
final List<AfterLoadAction> afterLoadActions = Collections.emptyList();
|
||||
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
||||
final Statement st = rs.getStatement();
|
||||
try {
|
||||
processResultSet( rs, queryParameters, session, true, null, maxRows );
|
||||
processResultSet( rs, queryParameters, session, true, null, maxRows, afterLoadActions );
|
||||
}
|
||||
finally {
|
||||
st.close();
|
||||
|
|
|
@ -36,14 +36,19 @@ import org.hibernate.LockOptions;
|
|||
import org.hibernate.QueryException;
|
||||
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;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.loader.OuterJoinLoader;
|
||||
import org.hibernate.persister.entity.Loadable;
|
||||
import org.hibernate.persister.entity.Lockable;
|
||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
|
@ -190,12 +195,55 @@ public class CriteriaLoader extends OuterJoinLoader {
|
|||
return querySpaces;
|
||||
}
|
||||
|
||||
protected String applyLocks(String sqlSelectString, LockOptions lockOptions, Dialect dialect) throws QueryException {
|
||||
@Override
|
||||
protected String applyLocks(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) throws QueryException {
|
||||
final LockOptions lockOptions = parameters.getLockOptions();
|
||||
if ( lockOptions == null ||
|
||||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
|
||||
return sqlSelectString;
|
||||
return sql;
|
||||
}
|
||||
|
||||
// user is request locking, lets see if we can apply locking directly to the SQL...
|
||||
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
);
|
||||
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());
|
||||
|
@ -213,7 +261,7 @@ public class CriteriaLoader extends OuterJoinLoader {
|
|||
}
|
||||
}
|
||||
}
|
||||
return dialect.applyLocksToSql( sqlSelectString, locks, keyColumnNames );
|
||||
return dialect.applyLocksToSql( sql, locks, keyColumnNames );
|
||||
}
|
||||
|
||||
protected LockMode[] getLockModes(LockOptions lockOptions) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.sql.ResultSet;
|
|||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -38,7 +39,12 @@ 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.hql.internal.HolderInstantiator;
|
||||
|
@ -50,6 +56,7 @@ import org.hibernate.loader.Loader;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.Loadable;
|
||||
import org.hibernate.persister.entity.Lockable;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
import org.hibernate.type.CollectionType;
|
||||
|
@ -331,6 +338,35 @@ public class CustomLoader extends Loader {
|
|||
return list( session, queryParameters, querySpaces, resultTypes );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String applyLocks(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) throws QueryException {
|
||||
final LockOptions lockOptions = parameters.getLockOptions();
|
||||
if ( lockOptions == null ||
|
||||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
// user is request locking, lets see if we can apply locking directly to the SQL...
|
||||
|
||||
// some dialects wont allow locking with paging...
|
||||
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.getLockOptions().setLockMode( LockMode.READ );
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
public ScrollableResults scroll(
|
||||
final QueryParameters queryParameters,
|
||||
final SessionImplementor session) throws HibernateException {
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.io.Serializable;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -253,10 +254,11 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
|
|||
selection.getMaxRows() :
|
||||
Integer.MAX_VALUE;
|
||||
|
||||
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, session );
|
||||
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
|
||||
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
|
||||
final Statement st = rs.getStatement();
|
||||
try {
|
||||
return processResultSet( rs, queryParameters, session, false, null, maxRows );
|
||||
return processResultSet( rs, queryParameters, session, false, null, maxRows, afterLoadActions );
|
||||
}
|
||||
finally {
|
||||
st.close();
|
||||
|
|
|
@ -23,10 +23,12 @@
|
|||
*/
|
||||
package org.hibernate.loader.hql;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -37,8 +39,12 @@ 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;
|
||||
|
@ -307,16 +313,35 @@ public class QueryLoader extends BasicLoader {
|
|||
return lockModesArray;
|
||||
}
|
||||
|
||||
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException {
|
||||
@Override
|
||||
protected String applyLocks(
|
||||
String sql,
|
||||
QueryParameters parameters,
|
||||
Dialect dialect,
|
||||
List<AfterLoadAction> afterLoadActions) throws QueryException {
|
||||
// can't cache this stuff either (per-invocation)
|
||||
// we are given a map of user-alias -> lock mode
|
||||
// create a new map of sql-alias -> lock mode
|
||||
|
||||
final LockOptions lockOptions = parameters.getLockOptions();
|
||||
|
||||
if ( lockOptions == null ||
|
||||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
// 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 ) ) {
|
||||
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.
|
||||
|
||||
|
||||
// we need both the set of locks and the columns to reference in locks
|
||||
// as the ultimate output of this section...
|
||||
final LockOptions locks = new LockOptions( lockOptions.getLockMode() );
|
||||
|
@ -490,7 +515,7 @@ public class QueryLoader extends BasicLoader {
|
|||
if ( queryParameters.isCallable() ) {
|
||||
throw new QueryException("iterate() not supported for callable statements");
|
||||
}
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, false, session );
|
||||
final ResultSet rs = executeQueryStatement( queryParameters, false, Collections.<AfterLoadAction>emptyList(), session );
|
||||
final PreparedStatement st = (PreparedStatement) rs.getStatement();
|
||||
final Iterator result = new IteratorImpl(
|
||||
rs,
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.locking.paging;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Entity
|
||||
public class Door {
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
public Door() {
|
||||
}
|
||||
|
||||
public Door(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
public Integer getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.locking.paging;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.Query;
|
||||
import org.hibernate.SQLQuery;
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Test of paging and locking in combination
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-1168" )
|
||||
public class PagingAndLockingTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { Door.class };
|
||||
}
|
||||
|
||||
@Before
|
||||
public void createTestData() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.save( new Door( 1, "Front" ) );
|
||||
session.save( new Door( 2, "Back" ) );
|
||||
session.save( new Door( 3, "Garage" ) );
|
||||
session.save( new Door( 4, "French" ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@After
|
||||
public void deleteTestData() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.createQuery( "delete Door" ).executeUpdate();
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHql() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
Query qry = session.createQuery( "from Door" );
|
||||
qry.getLockOptions().setLockMode( LockMode.PESSIMISTIC_WRITE );
|
||||
qry.setFirstResult( 2 );
|
||||
qry.setMaxResults( 2 );
|
||||
@SuppressWarnings("unchecked") List<Door> results = qry.list();
|
||||
assertEquals( 2, results.size() );
|
||||
for ( Door door : results ) {
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) );
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCriteria() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
Criteria criteria = session.createCriteria( Door.class );
|
||||
criteria.setLockMode( LockMode.PESSIMISTIC_WRITE );
|
||||
criteria.setFirstResult( 2 );
|
||||
criteria.setMaxResults( 2 );
|
||||
@SuppressWarnings("unchecked") List<Door> results = criteria.list();
|
||||
assertEquals( 2, results.size() );
|
||||
for ( Door door : results ) {
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) );
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Ignore( "Support for locking on native-sql queries not yet implemented" )
|
||||
public void testNativeSql() {
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
SQLQuery qry = session.createSQLQuery( "select * from door" );
|
||||
qry.addRoot( "door", Door.class );
|
||||
qry.getLockOptions().setLockMode( LockMode.PESSIMISTIC_WRITE );
|
||||
qry.setFirstResult( 2 );
|
||||
qry.setMaxResults( 2 );
|
||||
@SuppressWarnings("unchecked") List<Door> results = qry.list();
|
||||
assertEquals( 2, results.size() );
|
||||
for ( Door door : results ) {
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) );
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
# Free Software Foundation, Inc.
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301 USA
|
||||
#
|
||||
|
||||
hibernate.dialect org.hibernate.dialect.H2Dialect
|
||||
hibernate.connection.driver_class org.h2.Driver
|
||||
hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.hibernate.engine.query.spi.NamedParameterDescriptor;
|
|||
import org.hibernate.engine.query.spi.OrdinalParameterDescriptor;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.QueryExecutionRequestException;
|
||||
import org.hibernate.internal.SQLQueryImpl;
|
||||
import org.hibernate.jpa.AvailableSettings;
|
||||
import org.hibernate.jpa.HibernateQuery;
|
||||
import org.hibernate.jpa.internal.util.ConfigurationHelper;
|
||||
|
@ -242,12 +243,13 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
|
||||
@Override
|
||||
protected boolean canApplyLockModes() {
|
||||
return org.hibernate.internal.QueryImpl.class.isInstance( query );
|
||||
return org.hibernate.internal.QueryImpl.class.isInstance( query )
|
||||
|| SQLQueryImpl.class.isInstance( query );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyAliasSpecificLockMode(String alias, LockMode lockMode) {
|
||||
( (org.hibernate.internal.QueryImpl) query ).getLockOptions().setAliasSpecificLockMode( alias, lockMode );
|
||||
query.getLockOptions().setAliasSpecificLockMode( alias, lockMode );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -631,18 +633,16 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
|
|||
throw new IllegalStateException( "Not a JPAQL/Criteria query" );
|
||||
}
|
||||
this.jpaLockMode = lockModeType;
|
||||
( (org.hibernate.internal.QueryImpl) query ).getLockOptions().setLockMode(
|
||||
LockModeTypeHelper.getLockMode( lockModeType )
|
||||
);
|
||||
if ( getHints()!=null && getHints().containsKey( AvailableSettings.LOCK_TIMEOUT ) ) {
|
||||
applyLockTimeout( ConfigurationHelper.getInteger( getHints().get( AvailableSettings.LOCK_TIMEOUT )) );
|
||||
query.getLockOptions().setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) );
|
||||
if ( getHints() != null && getHints().containsKey( AvailableSettings.LOCK_TIMEOUT ) ) {
|
||||
applyLockTimeout( ConfigurationHelper.getInteger( getHints().get( AvailableSettings.LOCK_TIMEOUT ) ) );
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyLockTimeout(int timeout) {
|
||||
( (org.hibernate.internal.QueryImpl) query ).getLockOptions().setTimeOut( timeout );
|
||||
query.getLockOptions().setTimeOut( timeout );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -82,6 +82,32 @@ public class QueryLockingTest extends BaseEntityManagerFunctionalTestCase {
|
|||
em.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNativeSql() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
em.getTransaction().begin();
|
||||
QueryImpl query = em.createNativeQuery( "select * from lockable l" ).unwrap( QueryImpl.class );
|
||||
|
||||
org.hibernate.internal.SQLQueryImpl hibernateQuery = (org.hibernate.internal.SQLQueryImpl) query.getHibernateQuery();
|
||||
// assertEquals( LockMode.NONE, hibernateQuery.getLockOptions().getLockMode() );
|
||||
// assertNull( hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
|
||||
// assertEquals( LockMode.NONE, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) );
|
||||
|
||||
// NOTE : LockModeType.READ should map to LockMode.OPTIMISTIC
|
||||
query.setLockMode( LockModeType.READ );
|
||||
assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertNull( hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
|
||||
assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) );
|
||||
|
||||
query.setHint( AvailableSettings.ALIAS_SPECIFIC_LOCK_MODE+".l", LockModeType.PESSIMISTIC_WRITE );
|
||||
assertEquals( LockMode.OPTIMISTIC, hibernateQuery.getLockOptions().getLockMode() );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getAliasSpecificLockMode( "l" ) );
|
||||
assertEquals( LockMode.PESSIMISTIC_WRITE, hibernateQuery.getLockOptions().getEffectiveLockMode( "l" ) );
|
||||
|
||||
em.getTransaction().commit();
|
||||
em.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPessimisticForcedIncrementOverall() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
|
|
Loading…
Reference in New Issue