docs: @Override
  	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 );
  				}
  			}
  		}
  	}