/** * PUBLIC: * Set the locking policy to use numeric version locking. * This updates the version field on all updates, first comparing that the field has not changed to detect locking conflicts. * * The parameter 'shouldStoreInCache' configures the version lock value to be stored in the cache or in the object. * Note: if using a stateless model where the object can be passed to a client and then later updated in a different transaction context, * then the version lock value should not be stored in the cache, but in the object to ensure it is the correct value for that object. * @see TimestampLockingPolicy */ public void useVersionLocking(String writeLockFieldName, boolean shouldStoreInCache) { VersionLockingPolicy policy = new VersionLockingPolicy(writeLockFieldName); if (shouldStoreInCache) { policy.storeInCache(); } else { policy.storeInObject(); } setOptimisticLockingPolicy(policy); }
/** * INTERNAL: * When given an expression, this method will return a new expression with * the optimistic locking values included. The values are taken from the * passed in database row. This expression will be used in a delete call. */ public Expression buildDeleteExpression(DatabaseTable table, Expression mainExpression, AbstractRecord row) { //use the same expression as update return buildUpdateExpression(table, mainExpression, row, null); }
/** * Check if the new object's version has been set, if so, then it was an existing object that was deleted. * Raise an error instead of reincarnating the object. */ public void checkNewObjectLockVersion(Object clone, Object primaryKey, ClassDescriptor descriptor, UnitOfWorkImpl unitOfWork) { //bug272704: throw an exception if this object is new yet has a version set to avoid merging in deleted objects if (descriptor.usesVersionLocking()){ VersionLockingPolicy policy = (VersionLockingPolicy)descriptor.getOptimisticLockingPolicy(); Object baseValue = policy.getBaseValue(); Object objectLockValue = policy.getWriteLockValue(clone, primaryKey, unitOfWork); if (policy.isNewerVersion(objectLockValue, baseValue)) { throw OptimisticLockException.objectChangedSinceLastMerge(clone); } } }
/** * INTERNAL: * Compares the value with the value from the object (or cache). * Will return true if the currentValue is newer than the domainObject. */ public boolean isNewerVersion(Object currentValue, Object domainObject, Object primaryKey, AbstractSession session) { Number writeLockFieldValue; Number newWriteLockFieldValue = (Number)currentValue; if (isStoredInCache()) { writeLockFieldValue = (Number)session.getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, domainObject.getClass(), getDescriptor()); } else { writeLockFieldValue = (Number)lockValueFromObject(domainObject); } return isNewerVersion(newWriteLockFieldValue, writeLockFieldValue); }
/** * INTERNAL: * This method will return the optimistic lock value for the object */ public Object getWriteLockValue(Object domainObject, java.util.Vector primaryKey, AbstractSession session) { Number writeLockFieldValue; if (isStoredInCache()) { writeLockFieldValue = (Number)session.getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, domainObject.getClass(), getDescriptor()); } else { writeLockFieldValue = (Number)lockValueFromObject(domainObject); } return writeLockFieldValue; }
/** * INTERNAL: * It is responsible for initializing the policy properties; */ public void initializeProperties() { DatabaseField dbField = getWriteLockField(); dbField = descriptor.buildField(dbField); setWriteLockField(dbField); if (isStoredInCache() && (dbField.getType() == null)) { // Set the default type, only if un-mapped. dbField.setType(getDefaultLockingFieldType()); } Enumeration enumtr = this.getUnmappedFields().elements(); while (enumtr.hasMoreElements()) { DatabaseField lockField; lockField = (DatabaseField)enumtr.nextElement(); descriptor.getFields().addElement(lockField); } }
/** * INTERNAL: * This method gets the write lock value from either the cache or * the object stored in the query. It then returns the new incremented value. */ public Object getNewLockValue(ModifyQuery query) { Class objectClass = query.getDescriptor().getJavaClass(); Number value; Number newWriteLockValue = null; if (isStoredInCache()) { value = (Number)query.getSession().getIdentityMapAccessorInstance().getWriteLockValue(((WriteObjectQuery)query).getPrimaryKey(), objectClass, getDescriptor()); } else { value = (Number)lockValueFromObject(((ObjectLevelModifyQuery)query).getObject()); } if (value == null) { throw OptimisticLockException.noVersionNumberWhenUpdating(((ObjectLevelModifyQuery)query).getObject(), (ObjectLevelModifyQuery)query); } // Increment the value, this goes to the database newWriteLockValue = incrementWriteLockValue(value); return newWriteLockValue; }
/** * INTERNAL: * This method updates the modify row, and the domain object * with the new lock value. */ public void updateRowAndObjectForUpdate(ObjectLevelModifyQuery query, Object domainObject) { Object lockValue = getNewLockValue(query); if (isStoredInCache()) { query.getSession().getIdentityMapAccessor().updateWriteLockValue(query.getPrimaryKey(), domainObject.getClass(), lockValue); } updateWriteLockValueForWrite(query, lockValue); }
/** * INTERNAL: * Check the row count for lock failure. */ public void validateDelete(int rowCount, Object object, DeleteObjectQuery query) { if (rowCount <= 0) { // Mark the object as invalid in the session cache, only if version is the same as in query. Object primaryKey = query.getPrimaryKey(); AbstractSession session = query.getSession().getParentIdentityMapSession(query, true, true); CacheKey cacheKey = session.getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, query.getReferenceClass(), query.getDescriptor(), false); if ((cacheKey != null) && (cacheKey.getObject() != null) && (query.getObjectChangeSet() != null)) { Object queryVersion = query.getObjectChangeSet().getInitialWriteLockValue(); Object cacheVersion = getWriteLockValue(cacheKey.getObject(), primaryKey, session); if (compareWriteLockValues(queryVersion, cacheVersion) != 0) { cacheKey.setInvalidationState(CacheKey.CACHE_KEY_INVALID); } } throw OptimisticLockException.objectChangedSinceLastReadWhenDeleting(object, query); } }
/** * INTERNAL: * Only applicable when the value is stored in the cache. Will merge with the parent unit of work. */ public void mergeIntoParentCache(UnitOfWorkImpl uow, Object primaryKey, Object object) { if (isStoredInCache()) { Object parentValue = uow.getParentIdentityMapSession(descriptor, false, false).getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, object.getClass(), getDescriptor()); uow.getIdentityMapAccessor().updateWriteLockValue(primaryKey, object.getClass(), parentValue); } }
/** * INTERNAL: * This method must be included in any locking policy. * Put the initial writelock value into the modifyRow. */ public void setupWriteFieldsForInsert(ObjectLevelModifyQuery query) { Object lockValue = getInitialWriteValue(query.getSession()); ObjectChangeSet objectChangeSet = query.getObjectChangeSet(); if (objectChangeSet != null) { objectChangeSet.setInitialWriteLockValue(lockValue); } updateWriteLockValueForWrite(query, lockValue); }
/** * INTERNAL: * When given an expression, this method will return a new expression * with the optimistic locking values included. The values are taken * from the passed in database row. This expression will be used in * an update call. */ public Expression buildUpdateExpression(DatabaseTable table, Expression mainExpression, AbstractRecord row, AbstractRecord row2) { if (cachedExpression == null) { cachedExpression = buildExpression(); } if (getWriteLockField().getTableName().equals(table.getName())) { return mainExpression.and(cachedExpression); } return mainExpression; }
/** * INTERNAL: * Process a version accessor. */ @Override public void process() { // This will initialize the m_field variable. Accessible through getField(). super.process(); // Process an @Version or version element if there is one. if (getDescriptor().usesOptimisticLocking()) { // Ignore the version locking if it is already set. getLogger().logConfigMessage(MetadataLogger.IGNORE_VERSION_LOCKING, this); } else { MetadataClass lockType = getRawClass(); getDatabaseField().setTypeName(getJavaClassName(lockType)); if (isValidVersionLockingType(lockType) || isValidTimestampVersionLockingType(lockType)) { for (MetadataDescriptor owningDescriptor : getOwningDescriptors()) { VersionLockingPolicy policy = isValidVersionLockingType(lockType) ? new VersionLockingPolicy(getDatabaseField()) : new TimestampLockingPolicy(getDatabaseField()); policy.storeInObject(); policy.setIsCascaded(getDescriptor().usesCascadedOptimisticLocking()); owningDescriptor.setOptimisticLockingPolicy(policy); } } else { throw ValidationException.invalidTypeForVersionAttribute(getAttributeName(), lockType, getJavaClass()); } } } }
/** * INTERNAL: * Compares the value with the value from the object (or cache). * Will return true if the currentValue is newer than the domainObject. */ public boolean isNewerVersion(Object currentValue, Object domainObject, java.util.Vector primaryKey, AbstractSession session) { Number writeLockFieldValue; Number newWriteLockFieldValue = (Number)currentValue; if (isStoredInCache()) { writeLockFieldValue = (Number)session.getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, domainObject.getClass(), getDescriptor()); } else { writeLockFieldValue = (Number)lockValueFromObject(domainObject); } return isNewerVersion(newWriteLockFieldValue, writeLockFieldValue); }
/** * INTERNAL: * This method will return the optimistic lock value for the object */ public Object getWriteLockValue(Object domainObject, Object primaryKey, AbstractSession session) { Number writeLockFieldValue; if (isStoredInCache()) { if (primaryKey == null) { return null; } writeLockFieldValue = (Number)session.getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, domainObject.getClass(), getDescriptor()); } else { writeLockFieldValue = (Number)lockValueFromObject(domainObject); } return writeLockFieldValue; }
/** * INTERNAL: * It is responsible for initializing the policy properties; */ public void initializeProperties() { DatabaseField dbField = getWriteLockField(); dbField = descriptor.buildField(dbField); setWriteLockField(dbField); if (isStoredInCache() && (dbField.getType() == null)) { // Set the default type, only if un-mapped. dbField.setType(getDefaultLockingFieldType()); } Enumeration enumtr = this.getUnmappedFields().elements(); while (enumtr.hasMoreElements()) { DatabaseField lockField; lockField = (DatabaseField)enumtr.nextElement(); descriptor.getFields().addElement(lockField); } }
/** * INTERNAL: * This method gets the write lock value from either the cache or * the object stored in the query. It then returns the new incremented value. */ public Object getNewLockValue(ModifyQuery query) { Class objectClass = query.getDescriptor().getJavaClass(); Number value; Number newWriteLockValue = null; if (isStoredInCache()) { value = (Number)query.getSession().getIdentityMapAccessorInstance().getWriteLockValue(((WriteObjectQuery)query).getPrimaryKey(), objectClass, getDescriptor()); } else { value = (Number)lockValueFromObject(((ObjectLevelModifyQuery)query).getObject()); } if (value == null) { throw OptimisticLockException.noVersionNumberWhenUpdating(((ObjectLevelModifyQuery)query).getObject(), (ObjectLevelModifyQuery)query); } // Increment the value, this goes to the database newWriteLockValue = incrementWriteLockValue(value); return newWriteLockValue; }
/** * INTERNAL: * This method updates the modify row, and the domain object * with the new lock value. */ public void updateRowAndObjectForUpdate(ObjectLevelModifyQuery query, Object domainObject) { Object lockValue = getNewLockValue(query); if (isStoredInCache()) { query.getSession().getIdentityMapAccessor().updateWriteLockValue(query.getPrimaryKey(), domainObject.getClass(), lockValue); } updateWriteLockValueForWrite(query, lockValue); }
/** * INTERNAL: * Check the row count for lock failure. */ public void validateUpdate(int rowCount, Object object, WriteObjectQuery query) { if (rowCount <= 0) { // Mark the object as invalid in the session cache, only if version is the same as in query. Object primaryKey = query.getPrimaryKey(); AbstractSession session = query.getSession().getParentIdentityMapSession(query, true, true); CacheKey cacheKey = session.getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, query.getReferenceClass(), query.getDescriptor(), false); if ((cacheKey != null) && (cacheKey.getObject() != null) && (query.getObjectChangeSet() != null)) { Object queryVersion = query.getObjectChangeSet().getInitialWriteLockValue(); Object cacheVersion = getWriteLockValue(cacheKey.getObject(), primaryKey, session); if (compareWriteLockValues(queryVersion, cacheVersion) >= 0) { cacheKey.setInvalidationState(CacheKey.CACHE_KEY_INVALID); } } throw OptimisticLockException.objectChangedSinceLastReadWhenUpdating(object, query); } } }
/** * INTERNAL: * Only applicable when the value is stored in the cache. Will merge with the parent unit of work. */ public void mergeIntoParentCache(UnitOfWorkImpl uow, Vector primaryKey, Object object) { if (isStoredInCache()) { Object parentValue = uow.getParent().getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, object.getClass(), getDescriptor()); uow.getIdentityMapAccessor().updateWriteLockValue(primaryKey, object.getClass(), parentValue); } }