private AbstractEntityInsertAction addInsertAction(
     Object[] values,
     Serializable id,
     Object entity,
     EntityPersister persister,
     boolean useIdentityColumn,
     EventSource source,
     boolean shouldDelayIdentityInserts) {
   if (useIdentityColumn) {
     EntityIdentityInsertAction insert =
         new EntityIdentityInsertAction(
             values,
             entity,
             persister,
             isVersionIncrementDisabled(),
             source,
             shouldDelayIdentityInserts);
     source.getActionQueue().addAction(insert);
     return insert;
   } else {
     Object version = Versioning.getVersion(values, persister);
     EntityInsertAction insert =
         new EntityInsertAction(
             id, values, entity, version, persister, isVersionIncrementDisabled(), source);
     source.getActionQueue().addAction(insert);
     return insert;
   }
 }
  @Override
  public void insert(Serializable id, Object[] fields, Object object, SessionImplementor session)
      throws HibernateException {

    final int span = getTableSpan();
    // insert operations are always dynamic in OGM
    boolean[] propertiesToInsert = getPropertiesToInsert(fields);
    for (int j = 0; j < span; j++) {
      if (isInverseTable(j)) {
        return;
      }

      // note: it is conceptually possible that a UserType could map null to
      //	  a non-null value, so the following is arguable:
      if (isNullableTable(j) && isAllNull(fields, j)) {
        return;
      }

      if (log.isTraceEnabled()) {
        log.trace("Inserting entity: " + MessageHelper.infoString(this, id, getFactory()));
        if (j == 0 && isVersioned()) {
          log.trace("Version: " + Versioning.getVersion(fields, this));
        }
      }

      final EntityKey key = EntityKeyBuilder.fromPersister(this, id, session);
      Tuple resultset = gridDialect.getTuple(key, this.getTupleContext());
      // add the discriminator
      if (j == 0) {
        if (resultset != null) {
          throw new HibernateException(
              "trying to insert an already existing entity: "
                  + MessageHelper.infoString(this, id, getFactory()));
        }

        if (discriminator.isNeeded()) {
          resultset = createNewResultSetIfNull(key, resultset, id, session);
          resultset.put(getDiscriminatorColumnName(), getDiscriminatorValue());
        }
      }

      resultset = createNewResultSetIfNull(key, resultset, id, session);

      // dehydrate
      dehydrate(
          resultset, fields, propertiesToInsert, getPropertyColumnInsertable(), j, id, session);
      gridDialect.updateTuple(resultset, key, getTupleContext());
    }
  }
  /**
   * Perform any property value substitution that is necessary (interceptor callback, version
   * initialization...)
   *
   * @param entity The entity
   * @param id The entity identifier
   * @param values The snapshot entity state
   * @param persister The entity persister
   * @param source The originating session
   * @return True if the snapshot state changed such that reinjection of the values into the entity
   *     is required.
   */
  protected boolean substituteValuesIfNecessary(
      Object entity,
      Serializable id,
      Object[] values,
      EntityPersister persister,
      SessionImplementor source) {
    boolean substitute =
        source
            .getInterceptor()
            .onSave(entity, id, values, persister.getPropertyNames(), persister.getPropertyTypes());

    // keep the existing version number in the case of replicate!
    if (persister.isVersioned()) {
      substitute =
          Versioning.seedVersion(
                  values, persister.getVersionProperty(), persister.getVersionType(), source)
              || substitute;
    }
    return substitute;
  }
  @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());
    }
  }
  @Override
  public void execute() throws HibernateException {
    nullifyTransientReferencesIfNotAlready();

    final EntityPersister persister = getPersister();
    final SessionImplementor session = getSession();
    final Object instance = getInstance();
    final Serializable id = getId();

    final boolean veto = preInsert();

    // Don't need to lock the cache here, since if someone
    // else inserted the same pk first, the insert would fail

    if (!veto) {

      persister.insert(id, getState(), instance, session);

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

      entry.postInsert(getState());

      if (persister.hasInsertGeneratedProperties()) {
        persister.processInsertGeneratedProperties(id, instance, getState(), session);
        if (persister.isVersionPropertyGenerated()) {
          version = Versioning.getVersion(getState(), persister);
        }
        entry.postUpdate(instance, getState(), version);
      }

      getSession().getPersistenceContext().registerInsertedKey(getPersister(), getId());
    }

    final SessionFactoryImplementor factory = getSession().getFactory();

    if (isCachePutEnabled(persister, session)) {
      final CacheEntry ce = persister.buildCacheEntry(instance, getState(), version, session);
      cacheEntry = persister.getCacheEntryStructure().structure(ce);
      final CacheKey ck =
          session.generateCacheKey(
              id, persister.getIdentifierType(), persister.getRootEntityName());
      final boolean put = persister.getCacheAccessStrategy().insert(ck, cacheEntry, version);

      if (put && factory.getStatistics().isStatisticsEnabled()) {
        factory
            .getStatisticsImplementor()
            .secondLevelCachePut(getPersister().getCacheAccessStrategy().getRegion().getName());
      }
    }

    handleNaturalIdPostSaveNotifications(id);

    postInsert();

    if (factory.getStatistics().isStatisticsEnabled() && !veto) {
      factory.getStatisticsImplementor().insertEntity(getPersister().getEntityName());
    }

    markExecuted();
  }