HHH-4546 JPA-2 locking. LockOptions can include per table alias LockMode

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18243 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Scott Marlow 2009-12-16 18:23:31 +00:00
parent 1336b83e03
commit 0776dbab9a
26 changed files with 275 additions and 172 deletions

View File

@ -23,6 +23,11 @@
*/
package org.hibernate;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
/**
* Contains locking details (LockMode, Timeout and Scope).
*
@ -59,6 +64,8 @@ public class LockOptions
private boolean scope=false;// if true, cascade (pessimistic only) lock to collections and relationships
// owned by the entity.
private Map /* <String, LockMode> */ lockModesByName = new HashMap();
public LockOptions() {
}
@ -87,6 +94,49 @@ public class LockOptions
return this;
}
/**
* Specify the LockMode to be used for the specified alias.
*
* The ability to set the lockMode for a table alias is intended
* for internal Hibernate use.
*
* @param lockMode
* @param alias used to reference the LockMode.
* @return this LockRequest instance for operation chaining.
*/
public LockOptions setAliasLockMode(LockMode lockMode, String alias) {
lockModesByName.put(alias, lockMode);
return this;
}
/**
* Get the lock mode for the specified alias.
*
* @param alias used to reference the LockMode.
* @return the lock mode.
*/
public LockMode getAliasLockMode(String alias) {
return (LockMode)lockModesByName.get(alias);
}
/**
* Get the number of aliases that have LockModes specified
*
* @return the number of aliases
*/
public int getAliasLockCount() {
return lockModesByName.size();
}
/**
* Iterator for accessing Alias (key) and LockMode (value) as Map.Entry
*
* @return Iterator for accessing the Map.Entry's
*/
public Iterator getAliasLockIterator() {
return lockModesByName.entrySet().iterator();
}
/**
* Get the timeout setting.
*
@ -128,8 +178,10 @@ public class LockOptions
return this;
}
/**
* Copy From to Dest
* Shallow copy From to Dest
*
* @param from is copied from
* @param dest is copied to
* @return dest
@ -138,6 +190,7 @@ public class LockOptions
dest.setLockMode(from.getLockMode());
dest.setScope(from.getScope());
dest.setTimeOut(from.getTimeOut());
dest.lockModesByName = new HashMap(from.lockModesByName);
return dest;
}

View File

@ -209,15 +209,13 @@ public interface Query {
/**
* Set the lock options for the objects idententified by the
* given alias that appears in the <tt>FROM</tt> clause.
* @param alias a query alias, or <tt>this</tt> for a collection filter
*/
public Query setLockOptions(String alias, LockOptions lockOptions);
public Query setLockOptions(LockOptions lockOptions);
/**
* Set the lockmode for the objects idententified by the
* given alias that appears in the <tt>FROM</tt> clause.
* @param alias a query alias, or <tt>this</tt> for a collection filter
* @deprecated Instead use setLockOptions
*/
public Query setLockMode(String alias, LockMode lockMode);

View File

@ -169,14 +169,13 @@ abstract class AbstractTransactSQLDialect extends Dialect {
}
}
public String applyLocksToSql(String sql, Map aliasedLockOptions, Map keyColumnNames) {
Iterator itr = aliasedLockOptions.entrySet().iterator();
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) {
Iterator itr = aliasedLockOptions.getAliasLockIterator();
StringBuffer buffer = new StringBuffer( sql );
int correction = 0;
while ( itr.hasNext() ) {
final Map.Entry entry = ( Map.Entry ) itr.next();
final LockOptions lockOption = ( LockOptions ) entry.getValue();
final LockMode lockMode = lockOption.getLockMode();
final LockMode lockMode = ( LockMode ) entry.getValue();
if ( lockMode.greaterThan( LockMode.READ ) ) {
final String alias = ( String ) entry.getKey();
int start = -1, end = -1;

View File

@ -1138,11 +1138,11 @@ public abstract class Dialect {
* <tt>SELECT FOR UPDATE</tt> to achieve this in their own fashion.
*
* @param sql the SQL string to modify
* @param aliasedLockOptions a map of lock options indexed by aliased table names.
* @param aliasedLockOptions lock options indexed by aliased table names.
* @param keyColumnNames a map of key columns indexed by aliased table names.
* @return the modified SQL string.
*/
public String applyLocksToSql(String sql, Map aliasedLockOptions, Map keyColumnNames) {
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) {
return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
}

View File

@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2009, Red Hat Middleware LLC 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 Middleware LLC.
@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.QueryException;
import org.hibernate.ScrollMode;
import org.hibernate.LockOptions;
import org.hibernate.impl.FilterImpl;
import org.hibernate.dialect.Dialect;
import org.hibernate.hql.classic.ParserHelper;
@ -55,7 +56,7 @@ public final class QueryParameters {
private Type[] positionalParameterTypes;
private Object[] positionalParameterValues;
private Map namedParameters;
private Map lockOptions;
private LockOptions lockOptions;
private RowSelection rowSelection;
private boolean cacheable;
private String cacheRegion;
@ -133,7 +134,7 @@ public final class QueryParameters {
public QueryParameters(
final Type[] positionalParameterTypes,
final Object[] positionalParameterValues,
final Map lockOptions,
final LockOptions lockOptions,
final RowSelection rowSelection,
final boolean cacheable,
final String cacheRegion,
@ -161,7 +162,7 @@ public final class QueryParameters {
final Type[] positionalParameterTypes,
final Object[] positionalParameterValues,
final Map namedParameters,
final Map lockOptions,
final LockOptions lockOptions,
final RowSelection rowSelection,
final boolean readOnly,
final boolean cacheable,
@ -188,7 +189,7 @@ public final class QueryParameters {
final Type[] positionalParameterTypes,
final Object[] positionalParameterValues,
final Map namedParameters,
final Map lockOptions,
final LockOptions lockOptions,
final RowSelection rowSelection,
final boolean readOnly,
final boolean cacheable,
@ -258,14 +259,10 @@ public final class QueryParameters {
rowSelection = selection;
}
public Map getLockOptions() {
public LockOptions getLockOptions() {
return lockOptions;
}
public void setLockOptions(Map map) {
lockOptions = map;
}
public void traceParameters(SessionFactoryImplementor factory) throws HibernateException {
Printer print = new Printer( factory );
if ( positionalParameterValues.length != 0 ) {

View File

@ -1037,40 +1037,50 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
holderClass = clazz;
}
protected LockOptions[] getLockOptions(Map lockOptions) {
protected LockMode[] getLockModes(LockOptions lockOptions) {
// unfortunately this stuff can't be cached because
// it is per-invocation, not constant for the
// QueryTranslator instance
HashMap nameLockOptions = new HashMap();
if ( lockOptions != null ) {
Iterator iter = lockOptions.entrySet().iterator();
if ( lockOptions == null) {
lockOptions = LockOptions.NONE;
}
if ( lockOptions.getAliasLockCount() > 0 ) {
Iterator iter = lockOptions.getAliasLockIterator();
while ( iter.hasNext() ) {
Map.Entry me = ( Map.Entry ) iter.next();
nameLockOptions.put( getAliasName( ( String ) me.getKey() ),
me.getValue() );
}
}
LockOptions[] lockOptionsArray = new LockOptions[names.length];
LockMode[] lockModesArray = new LockMode[names.length];
for ( int i = 0; i < names.length; i++ ) {
LockOptions lm = ( LockOptions ) nameLockOptions.get( names[i] );
if ( lm == null ) lm = LockOptions.NONE;
lockOptionsArray[i] = lm;
LockMode lm = ( LockMode ) nameLockOptions.get( names[i] );
//if ( lm == null ) lm = LockOptions.NONE;
if ( lm == null ) lm = lockOptions.getLockMode();
lockModesArray[i] = lm;
}
return lockOptionsArray;
return lockModesArray;
}
protected String applyLocks(String sql, Map lockOptions, Dialect dialect) throws QueryException {
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException {
// can't cache this stuff either (per-invocation)
final String result;
if ( lockOptions == null || lockOptions.size() == 0 ) {
result = sql;
if ( lockOptions == null ||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
return sql;
}
else {
Map aliasedLockOptions = new HashMap();
Iterator iter = lockOptions.entrySet().iterator();
LockOptions locks = new LockOptions();
locks.setLockMode(lockOptions.getLockMode());
locks.setTimeOut(lockOptions.getTimeOut());
locks.setScope(lockOptions.getScope());
Iterator iter = lockOptions.getAliasLockIterator();
while ( iter.hasNext() ) {
Map.Entry me = ( Map.Entry ) iter.next();
aliasedLockOptions.put( getAliasName( ( String ) me.getKey() ), me.getValue() );
locks.setAliasLockMode( (LockMode) me.getValue(), getAliasName( ( String ) me.getKey() ) );
}
Map keyColumnNames = null;
if ( dialect.forUpdateOfColumns() ) {
@ -1079,7 +1089,7 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
keyColumnNames.put( names[i], persisters[i].getIdentifierColumnNames() );
}
}
result = dialect.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames );
result = dialect.applyLocksToSql( sql, locks, keyColumnNames );
}
logQuery( queryString, result );
return result;

View File

@ -30,7 +30,6 @@ import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
import java.io.Serializable;
@ -47,6 +46,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.LockOptions;
import org.hibernate.LockMode;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.mapping.Table;
@ -428,10 +428,10 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
" from " + tableName + ' ' + alias +
" where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
HashMap lockMap = new HashMap();
lockMap.put( alias, LockOptions.UPGRADE );
LockOptions lockOptions = new LockOptions(LockMode.UPGRADE);
lockOptions.setAliasLockMode(LockMode.UPGRADE, alias);
Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
return dialect.applyLocksToSql( query, lockMap, updateTargetColumnsMap );
return dialect.applyLocksToSql( query, lockOptions, updateTargetColumnsMap );
}
protected String buildUpdateQuery() {

View File

@ -49,6 +49,7 @@ import org.hibernate.NonUniqueResultException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.Query;
import org.hibernate.QueryException;
import org.hibernate.LockOptions;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.RowSelection;
import org.hibernate.engine.SessionImplementor;
@ -227,7 +228,7 @@ public abstract class AbstractQueryImpl implements Query {
return session;
}
protected abstract Map getLockOptions();
protected abstract LockOptions getLockOptions();
// Parameter handling code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -24,7 +24,6 @@
*/
package org.hibernate.impl;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -48,7 +47,7 @@ import org.hibernate.engine.query.ParameterMetadata;
*/
public class QueryImpl extends AbstractQueryImpl {
private Map lockOptions = new HashMap(2);
private LockOptions lockOptions = new LockOptions();
public QueryImpl(
String queryString,
@ -126,15 +125,18 @@ public class QueryImpl extends AbstractQueryImpl {
}
public Query setLockMode(String alias, LockMode lockMode) {
return setLockOptions( alias, new LockOptions(lockMode) );
lockOptions.setAliasLockMode(lockMode, alias);
return this;
}
public Query setLockOptions(String alias, LockOptions lockOption) {
lockOptions.put(alias, lockOption);
public Query setLockOptions(LockOptions lockOption) {
this.lockOptions.setLockMode(lockOption.getLockMode());
this.lockOptions.setScope(lockOption.getScope());
this.lockOptions.setTimeOut(lockOptions.getTimeOut());
return this;
}
protected Map getLockOptions() {
protected LockOptions getLockOptions() {
return lockOptions;
}

View File

@ -254,13 +254,13 @@ public class SQLQueryImpl extends AbstractQueryImpl implements SQLQuery {
throw new UnsupportedOperationException("cannot set the lock mode for a native SQL query");
}
public Query setLockOptions(String alias, LockOptions lockOptions) {
public Query setLockOptions(LockOptions lockOptions) {
throw new UnsupportedOperationException("cannot set lock options for a native SQL query");
}
protected Map getLockOptions() {
protected LockOptions getLockOptions() {
//we never need to apply locks to the SQL
return CollectionHelper.EMPTY_MAP;
return null;
}
public SQLQuery addScalar(String columnAlias, Type type) {

View File

@ -82,7 +82,8 @@ public class JoinWalker {
protected CollectionPersister[] collectionPersisters;
protected int[] collectionOwners;
protected String[] aliases;
protected LockOptions[] lockOptionsArray;
protected LockOptions lockOptions;
protected LockMode[] lockModeArray;
protected String sql;
protected JoinWalker(
@ -101,12 +102,12 @@ public class JoinWalker {
this.collectionSuffixes = collectionSuffixes;
}
public LockOptions[] getLockModeOptions() {
return lockOptionsArray;
public LockOptions getLockModeOptions() {
return lockOptions;
}
public void setLockOptionsArray(LockOptions[] lockOptionsArray) {
this.lockOptionsArray = lockOptionsArray;
public LockMode[] getLockModeArray() {
return lockModeArray;
}
public String[] getSuffixes() {
@ -993,12 +994,14 @@ public class JoinWalker {
collectionPersisters = collections==0 ? null : new CollectionPersister[collections];
collectionSuffixes = BasicLoader.generateSuffixes( joins + 1, collections );
this.lockOptions = lockOptions;
persisters = new Loadable[joins];
aliases = new String[joins];
owners = new int[joins];
ownerAssociationTypes = new EntityType[joins];
lockOptionsArray = ArrayHelper.fillArray(lockOptions, joins);
lockModeArray = ArrayHelper.fillArray(lockOptions.getLockMode(), joins);
int i=0;
int j=0;
Iterator iter = associations.iterator();

View File

@ -178,14 +178,15 @@ public abstract class Loader {
*
* @param lockOptions a collection of lock options specified dynamically via the Query interface
*/
protected abstract LockOptions[] getLockOptions(Map lockOptions);
//protected abstract LockOptions[] getLockOptions(Map lockOptions);
protected abstract LockMode[] getLockModes(LockOptions lockOptions);
/**
* Append <tt>FOR UPDATE OF</tt> clause, if necessary. This
* empty superclass implementation merely returns its first
* argument.
*/
protected String applyLocks(String sql, Map lockOptions, Dialect dialect) throws HibernateException {
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws HibernateException {
return sql;
}
@ -214,34 +215,13 @@ public abstract class Loader {
return null;
}
private Map buildLockMap(Map locks) {
Map result = locks;
if ( result == null ) {
LockOptions[] lockArray = getLockOptions(result);
String[] aliases = getAliases();
if (aliases != null &&
lockArray != null &&
lockArray.length > 0 &&
lockArray.length == aliases.length &&
lockArray[0].getLockMode() != LockMode.NONE ) {
result = new HashMap();
for ( int looper = 0; looper < lockArray.length; looper++ ) {
result.put(aliases[looper], lockArray[looper]);
}
}
}
return result;
}
/**
* Modify the SQL, adding lock hints and comments, if necessary
*/
protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect)
throws HibernateException {
Map locks = buildLockMap(parameters.getLockOptions());
sql = applyLocks( sql, locks, dialect );
sql = applyLocks( sql, parameters.getLockOptions(), dialect );
return getFactory().getSettings().isCommentsEnabled() ?
prependComment( sql, parameters ) : sql;
@ -312,7 +292,7 @@ public abstract class Loader {
resultSet,
session,
queryParameters,
getLockOptions( queryParameters.getLockOptions() ),
getLockModes( queryParameters.getLockOptions() ),
null,
hydratedObjects,
new EntityKey[entitySpan],
@ -358,7 +338,7 @@ public abstract class Loader {
resultSet,
session,
queryParameters,
getLockOptions( queryParameters.getLockOptions() ),
getLockModes( queryParameters.getLockOptions() ),
null,
hydratedObjects,
loadedKeys,
@ -595,7 +575,7 @@ public abstract class Loader {
final ResultSet resultSet,
final SessionImplementor session,
final QueryParameters queryParameters,
final LockOptions[] lockOptionsArray,
final LockMode[] lockModesArray,
final EntityKey optionalObjectKey,
final List hydratedObjects,
final EntityKey[] keys,
@ -626,7 +606,7 @@ public abstract class Loader {
keys,
queryParameters.getOptionalObject(),
optionalObjectKey,
lockOptionsArray,
lockModesArray,
hydratedObjects,
session
);
@ -723,7 +703,7 @@ public abstract class Loader {
// that I could do the control breaking at the means to know when to stop
final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockOptions[] lockOptionsArray = getLockOptions( queryParameters.getLockOptions() );
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
final boolean createSubselects = isSubselectLoadingEnabled();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final List results = new ArrayList();
@ -745,7 +725,7 @@ public abstract class Loader {
rs,
session,
queryParameters,
lockOptionsArray,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
@ -1205,7 +1185,7 @@ public abstract class Loader {
final EntityKey[] keys,
final Object optionalObject,
final EntityKey optionalObjectKey,
final LockOptions[] lockOptions,
final LockMode[] lockModes,
final List hydratedObjects,
final SessionImplementor session)
throws HibernateException, SQLException {
@ -1242,7 +1222,7 @@ public abstract class Loader {
persisters[i],
key,
object,
lockOptions[i],
lockModes[i],
session
);
}
@ -1253,7 +1233,7 @@ public abstract class Loader {
persisters[i],
descriptors[i].getRowIdAlias(),
key,
lockOptions[i],
lockModes[i],
optionalObjectKey,
optionalObject,
hydratedObjects,
@ -1279,10 +1259,9 @@ public abstract class Loader {
final Loadable persister,
final EntityKey key,
final Object object,
final LockOptions lockOptions,
final LockMode lockMode,
final SessionImplementor session)
throws HibernateException, SQLException {
LockMode lockMode = (lockOptions == null) ? null : lockOptions.getLockMode();
if ( !persister.isInstance( object, session.getEntityMode() ) ) {
throw new WrongClassException(
"loaded object was of wrong class " + object.getClass(),
@ -1318,14 +1297,13 @@ public abstract class Loader {
final Loadable persister,
final String rowIdAlias,
final EntityKey key,
final LockOptions lockOptions,
final LockMode lockMode,
final EntityKey optionalObjectKey,
final Object optionalObject,
final List hydratedObjects,
final SessionImplementor session)
throws HibernateException, SQLException {
LockMode lockMode = (lockOptions == null) ? null : lockOptions.getLockMode();
final String instanceClass = getInstanceClass(
final String instanceClass = getInstanceClass(
rs,
i,
persister,

View File

@ -27,6 +27,7 @@ package org.hibernate.loader;
import java.util.Map;
import org.hibernate.LockOptions;
import org.hibernate.LockMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.LoadQueryInfluencers;
@ -48,7 +49,8 @@ public abstract class OuterJoinLoader extends BasicLoader {
protected CollectionPersister[] collectionPersisters;
protected int[] collectionOwners;
protected String[] aliases;
protected LockOptions[] lockOptionsArray;
private LockOptions lockOptions;
protected LockMode[] lockModeArray;
protected int[] owners;
protected EntityType[] ownerAssociationTypes;
protected String sql;
@ -92,8 +94,12 @@ public abstract class OuterJoinLoader extends BasicLoader {
return ownerAssociationTypes;
}
protected LockOptions[] getLockOptions(Map lockModes) {
return lockOptionsArray;
protected LockMode[] getLockModes(LockOptions lockOptions) {
return lockModeArray;
}
protected LockOptions getLockOptions() {
return lockOptions;
}
public LoadQueryInfluencers getLoadQueryInfluencers() {
@ -116,7 +122,8 @@ public abstract class OuterJoinLoader extends BasicLoader {
persisters = walker.getPersisters();
collectionPersisters = walker.getCollectionPersisters();
ownerAssociationTypes = walker.getOwnerAssociationTypes();
lockOptionsArray = walker.getLockModeOptions();
lockOptions = walker.getLockModeOptions();
lockModeArray = walker.getLockModeArray();
suffixes = walker.getSuffixes();
collectionSuffixes = walker.getCollectionSuffixes();
owners = walker.getOwners();

View File

@ -1,7 +1,7 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2009, Red Hat Middleware LLC 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 Middleware LLC.
@ -144,38 +144,42 @@ public class CriteriaLoader extends OuterJoinLoader {
return querySpaces;
}
protected String applyLocks(String sqlSelectString, Map lockOptions, Dialect dialect) throws QueryException {
if ( lockOptions == null || lockOptions.isEmpty() ) {
protected String applyLocks(String sqlSelectString, LockOptions lockOptions, Dialect dialect) throws QueryException {
if ( lockOptions == null ||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
return sqlSelectString;
}
final Map aliasedLockOptions = new HashMap();
final LockOptions locks = new LockOptions(lockOptions.getLockMode());
locks.setScope( lockOptions.getScope());
locks.setTimeOut( lockOptions.getTimeOut());
final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
final String[] drivingSqlAliases = getAliases();
for ( int i = 0; i < drivingSqlAliases.length; i++ ) {
final LockOptions lockOption = ( LockOptions ) lockOptions.get( drivingSqlAliases[i] );
if ( lockOption != null ) {
final LockMode lockMode = lockOptions.getAliasLockMode( drivingSqlAliases[i] );
if ( lockMode != null ) {
final Lockable drivingPersister = ( Lockable ) getEntityPersisters()[i];
final String rootSqlAlias = drivingPersister.getRootTableAlias( drivingSqlAliases[i] );
aliasedLockOptions.put( rootSqlAlias, lockOption );
locks.setAliasLockMode(lockMode, rootSqlAlias);
if ( keyColumnNames != null ) {
keyColumnNames.put( rootSqlAlias, drivingPersister.getRootTableIdentifierColumnNames() );
}
}
}
return dialect.applyLocksToSql( sqlSelectString, aliasedLockOptions, keyColumnNames );
return dialect.applyLocksToSql( sqlSelectString, locks, keyColumnNames );
}
protected LockOptions[] getLockOptions(Map lockOptions) {
protected LockMode[] getLockModes(LockOptions lockOptions) {
final String[] entityAliases = getAliases();
if ( entityAliases == null ) {
return null;
}
final int size = entityAliases.length;
LockOptions[] lockModesArray = new LockOptions[size];
LockMode[] lockModesArray = new LockMode[size];
for ( int i=0; i<size; i++ ) {
LockOptions lockOption = (LockOptions) lockOptions.get( entityAliases[i] );
lockModesArray[i] = lockOption==null ? LockOptions.NONE : lockOption;
LockMode lockMode = lockOptions.getAliasLockMode( entityAliases[i] );
lockModesArray[i] = lockMode==null ? lockOptions.getLockMode() : lockMode;
}
return lockModesArray;
}

View File

@ -41,6 +41,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.LockOptions;
import org.hibernate.hql.ast.util.SessionFactoryHelper;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.criterion.Criterion;
@ -272,18 +273,18 @@ public class CriteriaQueryTranslator implements CriteriaQuery {
}
public QueryParameters getQueryParameters() {
LockOptions lockOptions = new LockOptions();
RowSelection selection = new RowSelection();
selection.setFirstRow( rootCriteria.getFirstResult() );
selection.setMaxRows( rootCriteria.getMaxResults() );
selection.setTimeout( rootCriteria.getTimeout() );
selection.setFetchSize( rootCriteria.getFetchSize() );
Map lockModes = new HashMap();
Iterator iter = rootCriteria.getLockModes().entrySet().iterator();
while ( iter.hasNext() ) {
Map.Entry me = ( Map.Entry ) iter.next();
final Criteria subcriteria = getAliasedCriteria( ( String ) me.getKey() );
lockModes.put( getSQLAlias( subcriteria ), me.getValue() );
lockOptions.setAliasLockMode( (LockMode)me.getValue(), getSQLAlias( subcriteria ) );
}
List values = new ArrayList();
List types = new ArrayList();
@ -292,7 +293,7 @@ public class CriteriaQueryTranslator implements CriteriaQuery {
CriteriaImpl.Subcriteria subcriteria = ( CriteriaImpl.Subcriteria ) iter.next();
LockMode lm = subcriteria.getLockMode();
if ( lm != null ) {
lockModes.put( getSQLAlias( subcriteria ), lm );
lockOptions.setAliasLockMode( lm, getSQLAlias( subcriteria ) );
}
if ( subcriteria.getWithClause() != null )
{
@ -322,7 +323,7 @@ public class CriteriaQueryTranslator implements CriteriaQuery {
return new QueryParameters(
typeArray,
valueArray,
lockModes,
lockOptions,
selection,
rootCriteria.getCacheable(),
rootCriteria.getCacheRegion(),

View File

@ -79,7 +79,8 @@ public class CustomLoader extends Loader {
private final int[] collectionOwners;
private final CollectionAliases[] collectionAliases;
private final LockOptions[] lockOptions;
private final LockMode[] lockModes;
// private final String[] sqlAliases;
// private final String[] sqlAliasSuffixes;
private final ResultRowProcessor rowProcessor;
@ -107,7 +108,7 @@ public class CustomLoader extends Loader {
List collectionOwners = new ArrayList();
List collectionAliases = new ArrayList();
List lockOptions = new ArrayList();
List lockModes = new ArrayList();
List resultColumnProcessors = new ArrayList();
List nonScalarReturnList = new ArrayList();
List resultTypes = new ArrayList();
@ -134,8 +135,7 @@ public class CustomLoader extends Loader {
RootReturn rootRtn = ( RootReturn ) rtn;
Queryable persister = ( Queryable ) factory.getEntityPersister( rootRtn.getEntityName() );
entityPersisters.add( persister );
// TODO: get lock options from rootRTN
lockOptions.add( new LockOptions(rootRtn.getLockMode()) );
lockModes.add( (rootRtn.getLockMode()) );
resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
nonScalarReturnList.add( rtn );
entityOwners.add( new Integer( -1 ) );
@ -149,8 +149,7 @@ public class CustomLoader extends Loader {
String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
QueryableCollection persister = ( QueryableCollection ) factory.getCollectionPersister( role );
collectionPersisters.add( persister );
// TODO: get lock options from collRtn
lockOptions.add( new LockOptions(collRtn.getLockMode()) );
lockModes.add( collRtn.getLockMode() );
resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
nonScalarReturnList.add( rtn );
collectionOwners.add( new Integer( -1 ) );
@ -172,8 +171,7 @@ public class CustomLoader extends Loader {
NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
entityOwners.add( new Integer( ownerIndex ) );
// TODO: get lock options from fetchRtn
lockOptions.add( new LockOptions(fetchRtn.getLockMode()) );
lockModes.add( fetchRtn.getLockMode() );
Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
EntityType fetchedType = ( EntityType ) ownerPersister.getPropertyType( fetchRtn.getOwnerProperty() );
String entityName = fetchedType.getAssociatedEntityName( getFactory() );
@ -189,8 +187,7 @@ public class CustomLoader extends Loader {
NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
collectionOwners.add( new Integer( ownerIndex ) );
// TODO: get lock options from fetchRtn
lockOptions.add( new LockOptions(fetchRtn.getLockMode()) );
lockModes.add( fetchRtn.getLockMode() );
Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
String role = ownerPersister.getEntityName() + '.' + fetchRtn.getOwnerProperty();
QueryableCollection persister = ( QueryableCollection ) factory.getCollectionPersister( role );
@ -233,9 +230,9 @@ public class CustomLoader extends Loader {
this.collectionAliases[i] = ( CollectionAliases ) collectionAliases.get( i );
}
this.lockOptions = new LockOptions[ lockOptions.size() ];
for ( int i = 0; i < lockOptions.size(); i++ ) {
this.lockOptions[i] = ( LockOptions ) lockOptions.get( i );
this.lockModes = new LockMode[ lockModes.size() ];
for ( int i = 0; i < lockModes.size(); i++ ) {
this.lockModes[i] = ( LockMode ) lockModes.get( i );
}
this.resultTypes = ArrayHelper.toTypeArray( resultTypes );
@ -293,8 +290,8 @@ public class CustomLoader extends Loader {
return querySpaces;
}
protected LockOptions[] getLockOptions(Map lockModesMap) {
return lockOptions;
protected LockMode[] getLockModes(LockOptions lockOptions) {
return lockModes;
}
protected Loadable[] getEntityPersisters() {

View File

@ -101,7 +101,7 @@ public class QueryLoader extends BasicLoader {
private ResultTransformer implicitResultTransformer;
private String[] queryReturnAliases;
private LockOptions[] defaultLockOptions;
private LockMode[] defaultLockModes;
/**
@ -198,7 +198,7 @@ public class QueryLoader extends BasicLoader {
}
//NONE, because its the requested lock mode, not the actual!
defaultLockOptions = ArrayHelper.fillArray( LockOptions.NONE, size );
defaultLockModes = ArrayHelper.fillArray( LockMode.NONE, size );
}
// -- Loader implementation --
@ -279,40 +279,45 @@ public class QueryLoader extends BasicLoader {
/**
* @param lockOptions a collection of lock modes specified dynamically via the Query interface
*/
protected LockOptions[] getLockOptions(Map lockOptions) {
protected LockMode[] getLockModes(LockOptions lockOptions) {
if ( lockOptions==null || lockOptions.size()==0 ) {
return defaultLockOptions;
if ( lockOptions == null ||
lockOptions.getAliasLockCount() == 0 ) {
return defaultLockModes;
}
else {
// unfortunately this stuff can't be cached because
// it is per-invocation, not constant for the
// QueryTranslator instance
LockOptions[] lockOptionsArray = new LockOptions[entityAliases.length];
LockMode[] lockModesArray = new LockMode[entityAliases.length];
for ( int i = 0; i < entityAliases.length; i++ ) {
LockOptions options = (LockOptions) lockOptions.get( entityAliases[i] );
if ( options == null ) {
LockMode lockMode = lockOptions.getAliasLockMode( entityAliases[i] );
if ( lockMode == null ) {
//NONE, because its the requested lock mode, not the actual!
options = LockOptions.NONE;
lockMode = lockOptions.getLockMode();
}
lockOptionsArray[i] = options;
lockModesArray[i] = lockMode;
}
return lockOptionsArray;
return lockModesArray;
}
}
protected String applyLocks(String sql, Map lockOptions, Dialect dialect) throws QueryException {
if ( lockOptions == null || lockOptions.size() == 0 ) {
protected String applyLocks(String sql, LockOptions lockOptions, Dialect dialect) throws QueryException {
if ( lockOptions == null ||
( lockOptions.getLockMode() == LockMode.NONE && lockOptions.getAliasLockCount() == 0 ) ) {
return sql;
}
// 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 Map aliasedLockOptions = new HashMap();
final LockOptions locks = new LockOptions(lockOptions.getLockMode());
locks.setScope( lockOptions.getScope());
locks.setTimeOut( lockOptions.getTimeOut());
final Map keyColumnNames = dialect.forUpdateOfColumns() ? new HashMap() : null;
final Iterator iter = lockOptions.entrySet().iterator();
final Iterator iter = lockOptions.getAliasLockIterator();
while ( iter.hasNext() ) {
Map.Entry me = ( Map.Entry ) iter.next();
final String userAlias = ( String ) me.getKey();
@ -329,12 +334,12 @@ public class QueryLoader extends BasicLoader {
final QueryNode select = ( QueryNode ) queryTranslator.getSqlAST();
final Lockable drivingPersister = ( Lockable ) select.getFromClause().getFromElement( userAlias ).getQueryable();
final String sqlAlias = drivingPersister.getRootTableAlias( drivingSqlAlias );
aliasedLockOptions.put( sqlAlias, me.getValue() );
locks.setAliasLockMode( (LockMode)me.getValue(), sqlAlias);
if ( keyColumnNames != null ) {
keyColumnNames.put( sqlAlias, drivingPersister.getRootTableIdentifierColumnNames() );
}
}
return dialect.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames );
return dialect.applyLocksToSql( sql, locks, keyColumnNames );
}
protected boolean upgradeLocks() {

View File

@ -26,6 +26,7 @@ package org.hibernate.sql;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import org.hibernate.LockMode;
import org.hibernate.QueryException;
@ -40,19 +41,30 @@ public class ForUpdateFragment {
private final StringBuffer aliases = new StringBuffer();
private boolean isNowaitEnabled;
private final Dialect dialect;
private LockMode lockMode;
private LockOptions lockOptions;
public ForUpdateFragment(Dialect dialect) {
this.dialect = dialect;
}
public ForUpdateFragment(Dialect dialect, Map lockOptions, Map keyColumnNames) throws QueryException {
public ForUpdateFragment(Dialect dialect, LockOptions lockOptions, Map keyColumnNames) throws QueryException {
this( dialect );
LockMode upgradeType = null;
Iterator iter = lockOptions.entrySet().iterator();
Iterator iter = lockOptions.getAliasLockIterator();
this.lockOptions = lockOptions;
if ( !iter.hasNext()) { // no tables referenced
final LockMode lockMode = lockOptions.getLockMode();
if ( LockMode.READ.lessThan( lockMode ) ) {
upgradeType = lockMode;
this.lockMode = lockMode;
}
}
while ( iter.hasNext() ) {
final Map.Entry me = ( Map.Entry ) iter.next();
final LockOptions lockOption = ( LockOptions ) me.getValue();
final LockMode lockMode = lockOption.getLockMode();
final LockMode lockMode = ( LockMode ) me.getValue();
if ( LockMode.READ.lessThan( lockMode ) ) {
final String tableAlias = ( String ) me.getKey();
if ( dialect.forUpdateOfColumns() ) {
@ -89,9 +101,17 @@ public class ForUpdateFragment {
}
public String toFragmentString() {
if ( aliases.length() == 0 ) {
if ( aliases.length() == 0) {
if ( lockOptions != null ) {
return dialect.getForUpdateString(lockOptions);
}
else if ( lockMode != null ) {
return dialect.getForUpdateString(lockMode);
}
return "";
}
// TODO: pass lockmode
return isNowaitEnabled ?
dialect.getForUpdateNowaitString( aliases.toString() ) :
dialect.getForUpdateString( aliases.toString() );

View File

@ -542,7 +542,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
}
private LockOptions getLockRequest(LockModeType lockModeType, Map<String, Object> properties) {
public LockOptions getLockRequest(LockModeType lockModeType, Map<String, Object> properties) {
LockOptions lockOptions = new LockOptions();
lockOptions.setLockMode(getLockMode(lockModeType));
if ( properties != null ) {
@ -569,7 +569,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
return lockOptions;
}
private LockModeType getLockModeType(LockMode lockMode) {
private static LockModeType getLockModeType(LockMode lockMode) {
if ( lockMode == LockMode.NONE )
return LockModeType.NONE;
else if ( lockMode == LockMode.OPTIMISTIC || lockMode == LockMode.READ )
@ -590,7 +590,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
}
private LockMode getLockMode(LockModeType lockMode) {
private static LockMode getLockMode(LockModeType lockMode) {
switch ( lockMode ) {
case READ:

View File

@ -217,7 +217,7 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
}
/* TODO:
else if ( "org.hibernate.lockMode".equals( hintName ) ) {
query.setLockMode( alias, lockMode );
query.setAliasLockMode( alias, lockMode );
}*/
else {
skipped = true;
@ -242,16 +242,9 @@ public abstract class AbstractQueryImpl<X> implements TypedQuery<X> {
return QueryHints.getDefinedHints();
}
private javax.persistence.LockModeType jpaLockMode = javax.persistence.LockModeType.NONE;
public abstract TypedQuery<X> setLockMode(javax.persistence.LockModeType lockModeType);
public TypedQuery<X> setLockMode(javax.persistence.LockModeType lockModeType) {
this.jpaLockMode = lockModeType;
return this;
}
public javax.persistence.LockModeType getLockMode() {
return jpaLockMode;
}
public abstract javax.persistence.LockModeType getLockMode();
private FlushModeType jpaFlushMode;

View File

@ -24,11 +24,14 @@
package org.hibernate.ejb;
import javax.persistence.PersistenceException;
import javax.persistence.LockModeType;
import org.hibernate.HibernateException;
import org.hibernate.StaleStateException;
import org.hibernate.LockOptions;
import java.util.Map;
/**
* Additional internal contracts for the Hibernate {@link javax.persistence.EntityManager} implementation.
*
@ -96,4 +99,13 @@ public interface HibernateEntityManagerImplementor extends HibernateEntityManage
public void throwPersistenceException(HibernateException e);
public PersistenceException wrapStaleStateException(StaleStateException e);
/**
* Convert from JPA-2 LockModeType & properties into LockOptions
*
* @param lockModeType is the requested lock type
* @param properties are the lock properties
* @return the LockOptions
*/
public LockOptions getLockRequest(LockModeType lockModeType, Map<String, Object> properties);
}

View File

@ -40,6 +40,7 @@ import static javax.persistence.TemporalType.TIME;
import static javax.persistence.TemporalType.TIMESTAMP;
import javax.persistence.TypedQuery;
import javax.persistence.PersistenceException;
import javax.persistence.TransactionRequiredException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -50,6 +51,7 @@ import org.hibernate.HibernateException;
import org.hibernate.QueryParameterException;
import org.hibernate.TypeMismatchException;
import org.hibernate.SQLQuery;
import org.hibernate.LockOptions;
import org.hibernate.engine.query.NamedParameterDescriptor;
import org.hibernate.engine.query.OrdinalParameterDescriptor;
import org.hibernate.hql.QueryExecutionRequestException;
@ -568,4 +570,23 @@ public class QueryImpl<X> extends org.hibernate.ejb.AbstractQueryImpl<X> impleme
}
}
}
private javax.persistence.LockModeType jpaLockMode = javax.persistence.LockModeType.NONE;
@SuppressWarnings({ "unchecked" })
public TypedQuery<X> setLockMode(javax.persistence.LockModeType lockModeType) {
if (! getEntityManager().isTransactionInProgress()) {
throw new TransactionRequiredException( "no transaction is in progress" );
}
this.jpaLockMode = lockModeType;
query.setLockOptions(getEntityManager().getLockRequest(lockModeType, null));
return this;
}
public javax.persistence.LockModeType getLockMode() {
return jpaLockMode;
}
}

View File

@ -80,6 +80,9 @@ public class TransactionImpl implements EntityTransaction {
if (e instanceof StaleStateException) {
wrappedException = entityManager.wrapStaleStateException( (StaleStateException) e );
}
else if (e instanceof HibernateException) {
throw entityManager.convert( (HibernateException)e );
}
else {
wrappedException = e;
}

View File

@ -219,7 +219,7 @@ public abstract class AbstractAuditQuery implements AuditQuery {
if (cacheMode != null) query.setCacheMode(cacheMode);
if (timeout != null) query.setTimeout(timeout);
if (lockOptions != null && lockOptions.getLockMode() != LockMode.NONE) {
query.setLockOptions("e", lockOptions);
query.setLockMode("e", lockOptions.getLockMode());
}
}
}

View File

@ -1,6 +1,5 @@
package org.hibernate.test.dialect.unit.lockhint;
import java.util.HashMap;
import java.util.Collections;
import org.hibernate.junit.UnitTestCase;
@ -56,8 +55,8 @@ public abstract class AbstractLockHintTest extends UnitTestCase {
}
public void verify() {
HashMap lockOptions = new HashMap();
lockOptions.put( aliasToLock, new LockOptions(LockMode.UPGRADE) );
LockOptions lockOptions = new LockOptions(LockMode.UPGRADE);
lockOptions.setAliasLockMode(LockMode.UPGRADE, aliasToLock);
String actualProcessedSql = dialect.applyLocksToSql( rawSql, lockOptions, Collections.EMPTY_MAP );
assertEquals( expectedProcessedSql, actualProcessedSql );
}

View File

@ -1355,7 +1355,7 @@ public class FooBarTest extends LegacyTestCase {
s.save(baz);
Query q = s.createQuery("from Foo foo, Bar bar");
if ( !(getDialect() instanceof DB2Dialect) ) {
q.setLockOptions("bar", LockOptions.UPGRADE);
q.setLockMode("bar", LockMode.UPGRADE);
}
Object[] result = (Object[]) q.uniqueResult();
Object b = result[0];
@ -1369,7 +1369,7 @@ public class FooBarTest extends LegacyTestCase {
s.createQuery( "from Foo foo" ).list();
assertTrue( s.getCurrentLockMode(b)==LockMode.NONE );
q = s.createQuery("from Foo foo");
q.setLockOptions("foo", LockOptions.READ);
q.setLockMode("foo", LockMode.READ);
q.list();
assertTrue( s.getCurrentLockMode(b)==LockMode.READ);
s.evict(baz);
@ -1388,9 +1388,9 @@ public class FooBarTest extends LegacyTestCase {
tx = s.beginTransaction();
q = s.createQuery("from Foo foo, Bar bar, Bar bar2");
if ( !(getDialect() instanceof DB2Dialect) ) {
q.setLockOptions("bar", LockOptions.UPGRADE);
q.setLockMode("bar", LockMode.UPGRADE);
}
q.setLockOptions("bar2", LockOptions.READ);
q.setLockMode("bar2", LockMode.READ);
result = (Object[]) q.list().get(0);
if ( !(getDialect() instanceof DB2Dialect) ) {
assertTrue( s.getCurrentLockMode( result[0] )==LockMode.UPGRADE && s.getCurrentLockMode( result[1] )==LockMode.UPGRADE );