private Object[] createDeletedState(
     EntityPersister persister, Object[] currentState, EventSource session) {
   Type[] propTypes = persister.getPropertyTypes();
   final Object[] deletedState = new Object[propTypes.length];
   //		TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(),
   // deletedState, session );
   boolean[] copyability = new boolean[propTypes.length];
   java.util.Arrays.fill(copyability, true);
   TypeHelper.deepCopy(currentState, propTypes, copyability, deletedState, session);
   return deletedState;
 }
  @Override
  public void execute() throws HibernateException {
    final Serializable id = getId();
    final EntityPersister persister = getPersister();
    final SessionImplementor session = getSession();
    final Object instance = getInstance();

    final boolean veto = preUpdate();

    final SessionFactoryImplementor factory = session.getFactory();
    Object previousVersion = this.previousVersion;
    if (persister.isVersionPropertyGenerated()) {
      // we need to grab the version value from the entity, otherwise
      // we have issues with generated-version entities that may have
      // multiple actions queued during the same flush
      previousVersion = persister.getVersion(instance);
    }

    final Object ck;
    if (persister.hasCache()) {
      final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
      ck = cache.generateCacheKey(id, persister, factory, session.getTenantIdentifier());
      lock = cache.lockItem(session, ck, previousVersion);
    } else {
      ck = null;
    }

    if (!veto) {
      persister.update(
          id,
          state,
          dirtyFields,
          hasDirtyCollection,
          previousState,
          previousVersion,
          instance,
          rowId,
          session);
    }

    final EntityEntry entry = session.getPersistenceContext().getEntry(instance);
    if (entry == null) {
      throw new AssertionFailure("possible nonthreadsafe access to session");
    }

    if (entry.getStatus() == Status.MANAGED || persister.isVersionPropertyGenerated()) {
      // get the updated snapshot of the entity state by cloning current state;
      // it is safe to copy in place, since by this time no-one else (should have)
      // has a reference  to the array
      TypeHelper.deepCopy(
          state, persister.getPropertyTypes(), persister.getPropertyCheckability(), state, session);
      if (persister.hasUpdateGeneratedProperties()) {
        // this entity defines proeprty generation, so process those generated
        // values...
        persister.processUpdateGeneratedProperties(id, instance, state, session);
        if (persister.isVersionPropertyGenerated()) {
          nextVersion = Versioning.getVersion(state, persister);
        }
      }
      // have the entity entry doAfterTransactionCompletion post-update processing, passing it the
      // update state and the new version (if one).
      entry.postUpdate(instance, state, nextVersion);
    }

    if (persister.hasCache()) {
      if (persister.isCacheInvalidationRequired() || entry.getStatus() != Status.MANAGED) {
        persister.getCacheAccessStrategy().remove(session, ck);
      } else {
        // TODO: inefficient if that cache is just going to ignore the updated state!
        final CacheEntry ce = persister.buildCacheEntry(instance, state, nextVersion, getSession());
        cacheEntry = persister.getCacheEntryStructure().structure(ce);

        final boolean put = cacheUpdate(persister, previousVersion, ck);
        if (put && factory.getStatistics().isStatisticsEnabled()) {
          factory
              .getStatisticsImplementor()
              .secondLevelCachePut(getPersister().getCacheAccessStrategy().getRegion().getName());
        }
      }
    }

    session
        .getPersistenceContext()
        .getNaturalIdHelper()
        .manageSharedNaturalIdCrossReference(
            persister, id, state, previousNaturalIdValues, CachedNaturalIdValueSource.UPDATE);

    postUpdate();

    if (factory.getStatistics().isStatisticsEnabled() && !veto) {
      factory.getStatisticsImplementor().updateEntity(getPersister().getEntityName());
    }
  }
  /**
   * Performs all the actual work needed to save an entity (well to get the save moved to the
   * execution queue).
   *
   * @param entity The entity to be saved
   * @param key The id to be used for saving the entity (or null, in the case of identity columns)
   * @param persister The entity's persister instance.
   * @param useIdentityColumn Should an identity column be used for id generation?
   * @param anything Generally cascade-specific information.
   * @param source The session which is the source of the current event.
   * @param requiresImmediateIdAccess Is access to the identifier required immediately after the
   *     completion of the save? persist(), for example, does not require this...
   * @return The id used to save the entity; may be null depending on the type of id generator used
   *     and the requiresImmediateIdAccess value
   */
  protected Serializable performSaveOrReplicate(
      Object entity,
      EntityKey key,
      EntityPersister persister,
      boolean useIdentityColumn,
      Object anything,
      EventSource source,
      boolean requiresImmediateIdAccess) {

    Serializable id = key == null ? null : key.getIdentifier();

    boolean inTxn = source.isTransactionInProgress();
    boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;

    // Put a placeholder in entries, so we don't recurse back and try to save() the
    // same object again. QUESTION: should this be done before onSave() is called?
    // likewise, should it be done before onUpdate()?
    EntityEntry original =
        source
            .getPersistenceContext()
            .addEntry(
                entity,
                Status.SAVING,
                null,
                null,
                id,
                null,
                LockMode.WRITE,
                useIdentityColumn,
                persister,
                false,
                false);

    cascadeBeforeSave(source, persister, entity, anything);

    Object[] values = persister.getPropertyValuesToInsert(entity, getMergeMap(anything), source);
    Type[] types = persister.getPropertyTypes();

    boolean substitute = substituteValuesIfNecessary(entity, id, values, persister, source);

    if (persister.hasCollections()) {
      substitute = substitute || visitCollectionsBeforeSave(entity, id, values, types, source);
    }

    if (substitute) {
      persister.setPropertyValues(entity, values);
    }

    TypeHelper.deepCopy(values, types, persister.getPropertyUpdateability(), values, source);

    AbstractEntityInsertAction insert =
        addInsertAction(
            values, id, entity, persister, useIdentityColumn, source, shouldDelayIdentityInserts);

    // postpone initializing id in case the insert has non-nullable transient dependencies
    // that are not resolved until cascadeAfterSave() is executed
    cascadeAfterSave(source, persister, entity, anything);
    if (useIdentityColumn && insert.isEarlyInsert()) {
      if (!EntityIdentityInsertAction.class.isInstance(insert)) {
        throw new IllegalStateException(
            "Insert should be using an identity column, but action is of unexpected type: "
                + insert.getClass().getName());
      }
      id = ((EntityIdentityInsertAction) insert).getGeneratedId();

      insert.handleNaturalIdPostSaveNotifications(id);
    }

    EntityEntry newEntry = source.getPersistenceContext().getEntry(entity);

    if (newEntry != original) {
      EntityEntryExtraState extraState = newEntry.getExtraState(EntityEntryExtraState.class);
      if (extraState == null) {
        newEntry.addExtraState(original.getExtraState(EntityEntryExtraState.class));
      }
    }

    return id;
  }