001: package org.hibernate.dialect.lock;
002:
003: import org.hibernate.persister.entity.Lockable;
004: import org.hibernate.LockMode;
005: import org.hibernate.HibernateException;
006: import org.hibernate.StaleObjectStateException;
007: import org.hibernate.JDBCException;
008: import org.hibernate.pretty.MessageHelper;
009: import org.hibernate.exception.JDBCExceptionHelper;
010: import org.hibernate.sql.Update;
011: import org.hibernate.engine.SessionImplementor;
012: import org.hibernate.engine.SessionFactoryImplementor;
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015:
016: import java.io.Serializable;
017: import java.sql.PreparedStatement;
018: import java.sql.SQLException;
019:
020: /**
021: * A locking strategy where the locks are obtained through update statements.
022: * <p/>
023: * This strategy is not valid for read style locks.
024: *
025: * @since 3.2
026: *
027: * @author Steve Ebersole
028: */
029: public class UpdateLockingStrategy implements LockingStrategy {
030: private static final Log log = LogFactory
031: .getLog(UpdateLockingStrategy.class);
032:
033: private final Lockable lockable;
034: private final LockMode lockMode;
035: private final String sql;
036:
037: /**
038: * Construct a locking strategy based on SQL UPDATE statements.
039: *
040: * @param lockable The metadata for the entity to be locked.
041: * @param lockMode Indictates the type of lock to be acquired. Note that
042: * read-locks are not valid for this strategy.
043: */
044: public UpdateLockingStrategy(Lockable lockable, LockMode lockMode) {
045: this .lockable = lockable;
046: this .lockMode = lockMode;
047: if (lockMode.lessThan(LockMode.UPGRADE)) {
048: throw new HibernateException("[" + lockMode
049: + "] not valid for update statement");
050: }
051: if (!lockable.isVersioned()) {
052: log
053: .warn("write locks via update not supported for non-versioned entities ["
054: + lockable.getEntityName() + "]");
055: this .sql = null;
056: } else {
057: this .sql = generateLockString();
058: }
059: }
060:
061: /**
062: * @see LockingStrategy#lock
063: */
064: public void lock(Serializable id, Object version, Object object,
065: SessionImplementor session)
066: throws StaleObjectStateException, JDBCException {
067: if (!lockable.isVersioned()) {
068: throw new HibernateException(
069: "write locks via update not supported for non-versioned entities ["
070: + lockable.getEntityName() + "]");
071: }
072: // todo : should we additionally check the current isolation mode explicitly?
073: SessionFactoryImplementor factory = session.getFactory();
074: try {
075: PreparedStatement st = session.getBatcher()
076: .prepareSelectStatement(sql);
077: try {
078: lockable.getVersionType().nullSafeSet(st, version, 1,
079: session);
080: int offset = 2;
081:
082: lockable.getIdentifierType().nullSafeSet(st, id,
083: offset, session);
084: offset += lockable.getIdentifierType().getColumnSpan(
085: factory);
086:
087: if (lockable.isVersioned()) {
088: lockable.getVersionType().nullSafeSet(st, version,
089: offset, session);
090: }
091:
092: int affected = st.executeUpdate();
093: if (affected < 0) {
094: factory
095: .getStatisticsImplementor()
096: .optimisticFailure(lockable.getEntityName());
097: throw new StaleObjectStateException(lockable
098: .getEntityName(), id);
099: }
100:
101: } finally {
102: session.getBatcher().closeStatement(st);
103: }
104:
105: } catch (SQLException sqle) {
106: throw JDBCExceptionHelper.convert(session.getFactory()
107: .getSQLExceptionConverter(), sqle,
108: "could not lock: "
109: + MessageHelper.infoString(lockable, id,
110: session.getFactory()), sql);
111: }
112: }
113:
114: protected String generateLockString() {
115: SessionFactoryImplementor factory = lockable.getFactory();
116: Update update = new Update(factory.getDialect());
117: update.setTableName(lockable.getRootTableName());
118: update.setPrimaryKeyColumnNames(lockable
119: .getRootTableIdentifierColumnNames());
120: update.setVersionColumnName(lockable.getVersionColumnName());
121: update.addColumn(lockable.getVersionColumnName());
122: if (factory.getSettings().isCommentsEnabled()) {
123: update.setComment(lockMode + " lock "
124: + lockable.getEntityName());
125: }
126: return update.toStatementString();
127: }
128:
129: protected LockMode getLockMode() {
130: return lockMode;
131: }
132: }
|