@Override
  public void doAfterTransactionCompletion(boolean success, SessionImplementor session)
      throws CacheException {
    final EntityPersister persister = getPersister();
    if (persister.hasCache()) {
      final EntityRegionAccessStrategy cache = persister.getCacheAccessStrategy();
      final Object ck =
          cache.generateCacheKey(
              getId(), persister, session.getFactory(), session.getTenantIdentifier());

      if (success && cacheEntry != null /*!persister.isCacheInvalidationRequired()*/) {
        final boolean put = cacheAfterUpdate(cache, ck);

        if (put && getSession().getFactory().getStatistics().isStatisticsEnabled()) {
          getSession()
              .getFactory()
              .getStatisticsImplementor()
              .secondLevelCachePut(cache.getRegion().getName());
        }
      } else {
        cache.unlockItem(session, ck, lock);
      }
    }
    postCommitUpdate(success);
  }
  @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());
    }
  }