in core/src/main/java/org/hibernate/ogm/persister/impl/OgmEntityPersister.java [1127:1284]
public void update(
final Serializable id,
final Object[] fields,
final int[] dirtyFields,
final boolean hasDirtyCollection,
final Object[] oldFields,
final Object oldVersion,
final Object object,
final Object rowId,
final SharedSessionContractImplementor session) throws HibernateException {
//note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
// oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)
//TODO support "multi table" entities
final boolean[] tableUpdateNeeded = getTableUpdateNeeded( dirtyFields, hasDirtyCollection );
final int span = getTableSpan();
final boolean[] propsToUpdate;
EntityEntry entry = session.getPersistenceContext().getEntry( object );
// Ensure that an immutable or non-modifiable entity is not being updated unless it is
// in the process of being deleted.
if ( entry == null && ! isMutable() ) {
throw new IllegalStateException( "Updating immutable entity that is not in session yet!" );
}
//we always use a dynamicUpdate model for Infinispan
if ( (
//getEntityMetamodel().isDynamicUpdate() &&
dirtyFields != null ) ) {
propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection );
// don't need to check laziness (dirty checking algorithm handles that)
}
else if ( ! isModifiableEntity( entry ) ) {
//TODO does that apply to OGM?
// We need to generate UPDATE SQL when a non-modifiable entity (e.g., read-only or immutable)
// needs:
// - to have references to transient entities set to null before being deleted
// - to have version incremented do to a "dirty" association
// If dirtyFields == null, then that means that there are no dirty properties to
// to be updated; an empty array for the dirty fields needs to be passed to
// getPropertiesToUpdate() instead of null.
propsToUpdate = getPropertiesToUpdate(
( dirtyFields == null ? ArrayHelper.EMPTY_INT_ARRAY : dirtyFields ),
hasDirtyCollection
);
// don't need to check laziness (dirty checking algorithm handles that)
}
else {
// For the case of dynamic-update="false", or no snapshot, we update all properties
//TODO handle lazy
propsToUpdate = getPropertyUpdateability( object );
}
final SessionFactoryImplementor factory = getFactory();
if ( log.isTraceEnabled() ) {
log.trace( "Updating entity: " + MessageHelper.infoString( this, id, factory ) );
if ( isVersioned() ) {
log.trace( "Existing version: " + oldVersion + " -> New version: " + fields[getVersionProperty()] );
}
}
for ( int j = 0; j < span; j++ ) {
// Now update only the tables with dirty properties (and the table with the version number)
if ( tableUpdateNeeded[j] ) {
final EntityKey key = EntityKeyBuilder.fromPersister( this, id, session );
final boolean useVersion = j == 0 && isVersioned();
if ( usesNonAtomicOptimisticLocking ) {
final Tuple tupleInDatastore = getFreshTuple( key, session );
final EntityMetamodel entityMetamodel = getEntityMetamodel();
// Write any appropriate versioning conditional parameters
if ( useVersion && entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.VERSION ) {
if ( checkVersion( propsToUpdate ) ) {
checkVersionAndRaiseSOSE( id, oldVersion, session, tupleInDatastore );
}
}
else if ( isAllOrDirtyOptLocking() && oldFields != null ) {
boolean[] versionability = getPropertyVersionability(); //TODO: is this really necessary????
boolean[] includeOldField = entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL
? getPropertyUpdateability()
: propsToUpdate;
//TODO do a diff on the properties value from resultset and the dirty value
GridType[] types = gridPropertyTypes;
for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
boolean include = includeOldField[i] &&
isPropertyOfTable( i, j ) &&
versionability[i]; //TODO: is this really necessary????
if ( include ) {
final GridType type = types[i];
//FIXME what do do with settable?
boolean[] settable = type.toColumnNullness( oldFields[i], factory );
final Object snapshotValue = type.nullSafeGet(
tupleInDatastore, getPropertyColumnNames( i ), session, object
);
if ( !type.isEqual( oldFields[i], snapshotValue, factory ) ) {
raiseStaleObjectStateException( id );
}
}
}
}
}
TuplePointer tuplePointer = getSharedTuplePointer( key, object, session );
Tuple resultset = tuplePointer.getTuple();
resultset = createNewResultSetIfNull( key, resultset, id, session );
resultset.setSnapshotType( SnapshotType.UPDATE );
saveSharedTuple( object, resultset, session );
if ( mightManageInverseAssociations ) {
removeFromInverseAssociations( resultset, j, id, session );
}
dehydrate( resultset, fields, propsToUpdate, j, id, session );
// TODO OGM-616 Also use this facet for "all columns" optimistic locking strategy
if ( isVersioned() && optimisticLockingAwareGridDialect != null ) {
Tuple oldVersionTuple = new Tuple();
oldVersionTuple.put( getVersionColumnName(), oldVersion );
boolean success = optimisticLockingAwareGridDialect.updateTupleWithOptimisticLock( key, oldVersionTuple, resultset, getTupleContext( session ) );
// If there is an error handler registered, pass the applied/failed operation to it as needed
if ( success ) {
if ( invocationCollectingGridDialect != null ) {
invocationCollectingGridDialect.onUpdateTupleWithOptimisticLockSuccess( key, oldVersionTuple, resultset );
}
}
else {
if ( invocationCollectingGridDialect != null ) {
try {
raiseStaleObjectStateException( id );
}
catch (Exception e) {
invocationCollectingGridDialect.onUpdateTupleWithOptimisticLockFailure( key, oldVersionTuple, resultset, e );
}
}
else {
raiseStaleObjectStateException( id );
}
}
}
else {
insertOrUpdateTuple( key, tuplePointer, hasUpdateGeneratedProperties(), session );
}
if ( mightManageInverseAssociations ) {
addToInverseAssociations( resultset, j, id, session );
}
}
}
}