001: package org.hibernate.dialect.lock;
002:
003: import org.hibernate.persister.entity.Lockable;
004: import org.hibernate.engine.SessionImplementor;
005: import org.hibernate.engine.SessionFactoryImplementor;
006: import org.hibernate.StaleObjectStateException;
007: import org.hibernate.JDBCException;
008: import org.hibernate.LockMode;
009: import org.hibernate.sql.SimpleSelect;
010: import org.hibernate.pretty.MessageHelper;
011: import org.hibernate.exception.JDBCExceptionHelper;
012:
013: import java.io.Serializable;
014: import java.sql.PreparedStatement;
015: import java.sql.ResultSet;
016: import java.sql.SQLException;
017:
018: /**
019: * A locking strategy where the locks are obtained through select statements.
020: * <p/>
021: * For non-read locks, this is achieved through the Dialect's specific
022: * SELECT ... FOR UPDATE syntax.
023: *
024: * @see org.hibernate.dialect.Dialect#getForUpdateString(org.hibernate.LockMode)
025: * @see org.hibernate.dialect.Dialect#appendLockHint(org.hibernate.LockMode, String)
026: * @since 3.2
027: *
028: * @author Steve Ebersole
029: */
030: public class SelectLockingStrategy implements LockingStrategy {
031:
032: private final Lockable lockable;
033: private final LockMode lockMode;
034: private final String sql;
035:
036: /**
037: * Construct a locking strategy based on SQL SELECT statements.
038: *
039: * @param lockable The metadata for the entity to be locked.
040: * @param lockMode Indictates the type of lock to be acquired.
041: */
042: public SelectLockingStrategy(Lockable lockable, LockMode lockMode) {
043: this .lockable = lockable;
044: this .lockMode = lockMode;
045: this .sql = generateLockString();
046: }
047:
048: /**
049: * @see LockingStrategy#lock
050: */
051: public void lock(Serializable id, Object version, Object object,
052: SessionImplementor session)
053: throws StaleObjectStateException, JDBCException {
054:
055: SessionFactoryImplementor factory = session.getFactory();
056: try {
057: PreparedStatement st = session.getBatcher()
058: .prepareSelectStatement(sql);
059: try {
060: lockable.getIdentifierType().nullSafeSet(st, id, 1,
061: session);
062: if (lockable.isVersioned()) {
063: lockable.getVersionType().nullSafeSet(
064: st,
065: version,
066: lockable.getIdentifierType().getColumnSpan(
067: factory) + 1, session);
068: }
069:
070: ResultSet rs = st.executeQuery();
071: try {
072: if (!rs.next()) {
073: if (factory.getStatistics()
074: .isStatisticsEnabled()) {
075: factory.getStatisticsImplementor()
076: .optimisticFailure(
077: lockable.getEntityName());
078: }
079: throw new StaleObjectStateException(lockable
080: .getEntityName(), id);
081: }
082: } finally {
083: rs.close();
084: }
085: } finally {
086: session.getBatcher().closeStatement(st);
087: }
088:
089: } catch (SQLException sqle) {
090: throw JDBCExceptionHelper.convert(session.getFactory()
091: .getSQLExceptionConverter(), sqle,
092: "could not lock: "
093: + MessageHelper.infoString(lockable, id,
094: session.getFactory()), sql);
095: }
096: }
097:
098: protected LockMode getLockMode() {
099: return lockMode;
100: }
101:
102: protected String generateLockString() {
103: SessionFactoryImplementor factory = lockable.getFactory();
104: SimpleSelect select = new SimpleSelect(factory.getDialect())
105: .setLockMode(lockMode)
106: .setTableName(lockable.getRootTableName())
107: .addColumn(
108: lockable.getRootTableIdentifierColumnNames()[0])
109: .addCondition(
110: lockable.getRootTableIdentifierColumnNames(),
111: "=?");
112: if (lockable.isVersioned()) {
113: select.addCondition(lockable.getVersionColumnName(), "=?");
114: }
115: if (factory.getSettings().isCommentsEnabled()) {
116: select.setComment(lockMode + " lock "
117: + lockable.getEntityName());
118: }
119: return select.toStatementString();
120: }
121: }
|