001: //$Id: Versioning.java 10858 2006-11-21 23:28:29Z steve.ebersole@jboss.com $
002: package org.hibernate.engine;
003:
004: import org.apache.commons.logging.Log;
005: import org.apache.commons.logging.LogFactory;
006:
007: import org.hibernate.persister.entity.EntityPersister;
008: import org.hibernate.type.VersionType;
009:
010: /**
011: * Utilities for dealing with optimisitic locking values.
012: *
013: * @author Gavin King
014: */
015: public final class Versioning {
016: /**
017: * Apply no optimistic locking
018: */
019: public static final int OPTIMISTIC_LOCK_NONE = -1;
020:
021: /**
022: * Apply optimisitc locking based on the defined version or timestamp
023: * property.
024: */
025: public static final int OPTIMISTIC_LOCK_VERSION = 0;
026:
027: /**
028: * Apply optimisitc locking based on the a current vs. snapshot comparison
029: * of <b>all</b> properties.
030: */
031: public static final int OPTIMISTIC_LOCK_ALL = 2;
032:
033: /**
034: * Apply optimisitc locking based on the a current vs. snapshot comparison
035: * of <b>dirty</b> properties.
036: */
037: public static final int OPTIMISTIC_LOCK_DIRTY = 1;
038:
039: private static final Log log = LogFactory.getLog(Versioning.class);
040:
041: /**
042: * Private constructor disallowing instantiation.
043: */
044: private Versioning() {
045: }
046:
047: /**
048: * Create an initial optimisitc locking value according the {@link VersionType}
049: * contract for the version property.
050: *
051: * @param versionType The version type.
052: * @param session The originating session
053: * @return The initial optimisitc locking value
054: */
055: private static Object seed(VersionType versionType,
056: SessionImplementor session) {
057: Object seed = versionType.seed(session);
058: if (log.isTraceEnabled())
059: log.trace("Seeding: " + seed);
060: return seed;
061: }
062:
063: /**
064: * Create an initial optimisitc locking value according the {@link VersionType}
065: * contract for the version property <b>if required</b> and inject it into
066: * the snapshot state.
067: *
068: * @param fields The current snapshot state
069: * @param versionProperty The index of the version property
070: * @param versionType The version type
071: * @param session The orginating session
072: * @return True if we injected a new version value into the fields array; false
073: * otherwise.
074: */
075: public static boolean seedVersion(Object[] fields,
076: int versionProperty, VersionType versionType,
077: SessionImplementor session) {
078: Object initialVersion = fields[versionProperty];
079: if (initialVersion == null ||
080: // This next bit is to allow for both unsaved-value="negative"
081: // and for "older" behavior where version number did not get
082: // seeded if it was already set in the object
083: // TODO: shift it into unsaved-value strategy
084: ((initialVersion instanceof Number) && ((Number) initialVersion)
085: .longValue() < 0)) {
086: fields[versionProperty] = seed(versionType, session);
087: return true;
088: } else {
089: if (log.isTraceEnabled()) {
090: log.trace("using initial version: " + initialVersion);
091: }
092: return false;
093: }
094: }
095:
096: /**
097: * Generate the next increment in the optimisitc locking value according
098: * the {@link VersionType} contract for the version property.
099: *
100: * @param version The current version
101: * @param versionType The version type
102: * @param session The originating session
103: * @return The incremented optimistic locking value.
104: */
105: public static Object increment(Object version,
106: VersionType versionType, SessionImplementor session) {
107: Object next = versionType.next(version, session);
108: if (log.isTraceEnabled()) {
109: log.trace("Incrementing: "
110: + versionType.toLoggableString(version, session
111: .getFactory())
112: + " to "
113: + versionType.toLoggableString(next, session
114: .getFactory()));
115: }
116: return next;
117: }
118:
119: /**
120: * Inject the optimisitc locking value into the entity state snapshot.
121: *
122: * @param fields The state snapshot
123: * @param version The optimisitc locking value
124: * @param persister The entity persister
125: */
126: public static void setVersion(Object[] fields, Object version,
127: EntityPersister persister) {
128: if (!persister.isVersioned()) {
129: return;
130: }
131: fields[persister.getVersionProperty()] = version;
132: }
133:
134: /**
135: * Extract the optimisitc locking value out of the entity state snapshot.
136: *
137: * @param fields The state snapshot
138: * @param persister The entity persister
139: * @return The extracted optimisitc locking value
140: */
141: public static Object getVersion(Object[] fields,
142: EntityPersister persister) {
143: if (!persister.isVersioned()) {
144: return null;
145: }
146: return fields[persister.getVersionProperty()];
147: }
148:
149: /**
150: * Do we need to increment the version number, given the dirty properties?
151: *
152: * @param dirtyProperties The array of property indexes which were deemed dirty
153: * @param hasDirtyCollections Were any collections found to be dirty (structurally changed)
154: * @param propertyVersionability An array indicating versionability of each property.
155: * @return True if a version increment is required; false otherwise.
156: */
157: public static boolean isVersionIncrementRequired(
158: final int[] dirtyProperties,
159: final boolean hasDirtyCollections,
160: final boolean[] propertyVersionability) {
161: if (hasDirtyCollections) {
162: return true;
163: }
164: for (int i = 0; i < dirtyProperties.length; i++) {
165: if (propertyVersionability[dirtyProperties[i]]) {
166: return true;
167: }
168: }
169: return false;
170: }
171:
172: }
|