@Override
  public void doBeforeTransactionCompletion(SessionImplementor session) {
    final EntityPersister persister = entry.getPersister();

    final Object latestVersion = persister.getCurrentVersion(entry.getId(), session);
    if (!entry.getVersion().equals(latestVersion)) {
      throw new OptimisticLockException(
          object,
          "Newer version ["
              + latestVersion
              + "] of entity ["
              + MessageHelper.infoString(entry.getEntityName(), entry.getId())
              + "] found in database");
    }
  }
  /**
   * process cascade save/update at the start of a flush to discover any newly referenced entity
   * that must be passed to saveOrUpdate(), and also apply orphan delete
   */
  private void prepareEntityFlushes(EventSource session, PersistenceContext persistenceContext)
      throws HibernateException {

    LOG.debug("Processing flush-time cascades");

    final Object anything = getAnything();
    // safe from concurrent modification because of how concurrentEntries() is implemented on
    // IdentityMap
    for (Map.Entry<Object, EntityEntry> me : persistenceContext.reentrantSafeEntityEntries()) {
      //		for ( Map.Entry me : IdentityMap.concurrentEntries( persistenceContext.getEntityEntries()
      // ) ) {
      EntityEntry entry = (EntityEntry) me.getValue();
      Status status = entry.getStatus();
      if (status == Status.MANAGED || status == Status.SAVING || status == Status.READ_ONLY) {
        cascadeOnFlush(session, entry.getPersister(), me.getKey(), anything);
      }
    }
  }
  /**
   * process cascade save/update at the start of a flush to discover any newly referenced entity
   * that must be passed to saveOrUpdate(), and also apply orphan delete
   */
  private void prepareEntityFlushes(EventSource session) throws HibernateException {

    LOG.debugf("Processing flush-time cascades");

    final Map.Entry[] list =
        IdentityMap.concurrentEntries(session.getPersistenceContext().getEntityEntries());
    // safe from concurrent modification because of how entryList() is implemented on IdentityMap
    final int size = list.length;
    final Object anything = getAnything();
    for (int i = 0; i < size; i++) {
      Map.Entry me = list[i];
      EntityEntry entry = (EntityEntry) me.getValue();
      Status status = entry.getStatus();
      if (status == Status.MANAGED || status == Status.SAVING || status == Status.READ_ONLY) {
        cascadeOnFlush(session, entry.getPersister(), me.getKey(), anything);
      }
    }
  }
  /**
   * Handle the given delete event. This is the cascaded form.
   *
   * @param event The delete event.
   * @param transientEntities The cache of entities already deleted
   * @throws HibernateException
   */
  public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {

    final EventSource source = event.getSession();

    final PersistenceContext persistenceContext = source.getPersistenceContext();
    Object entity = persistenceContext.unproxyAndReassociate(event.getObject());

    EntityEntry entityEntry = persistenceContext.getEntry(entity);
    final EntityPersister persister;
    final Serializable id;
    final Object version;

    if (entityEntry == null) {
      LOG.trace("Entity was not persistent in delete processing");

      persister = source.getEntityPersister(event.getEntityName(), entity);

      if (ForeignKeys.isTransient(persister.getEntityName(), entity, null, source)) {
        deleteTransientEntity(
            source, entity, event.isCascadeDeleteEnabled(), persister, transientEntities);
        // EARLY EXIT!!!
        return;
      }
      performDetachedEntityDeletionCheck(event);

      id = persister.getIdentifier(entity, source);

      if (id == null) {
        throw new TransientObjectException(
            "the detached instance passed to delete() had a null identifier");
      }

      final EntityKey key = source.generateEntityKey(id, persister);

      persistenceContext.checkUniqueness(key, entity);

      new OnUpdateVisitor(source, id, entity).process(entity, persister);

      version = persister.getVersion(entity);

      entityEntry =
          persistenceContext.addEntity(
              entity,
              (persister.isMutable() ? Status.MANAGED : Status.READ_ONLY),
              persister.getPropertyValues(entity),
              key,
              version,
              LockMode.NONE,
              true,
              persister,
              false);
    } else {
      LOG.trace("Deleting a persistent instance");

      if (entityEntry.getStatus() == Status.DELETED || entityEntry.getStatus() == Status.GONE) {
        LOG.trace("Object was already deleted");
        return;
      }
      persister = entityEntry.getPersister();
      id = entityEntry.getId();
      version = entityEntry.getVersion();
    }

    /*if ( !persister.isMutable() ) {
    	throw new HibernateException(
    			"attempted to delete an object of immutable class: " +
    			MessageHelper.infoString(persister)
    		);
    }*/

    if (invokeDeleteLifecycle(source, entity, persister)) {
      return;
    }

    deleteEntity(
        source,
        entity,
        entityEntry,
        event.isCascadeDeleteEnabled(),
        event.isOrphanRemovalBeforeUpdates(),
        persister,
        transientEntities);

    if (source.getFactory().getSettings().isIdentifierRollbackEnabled()) {
      persister.resetIdentifier(entity, id, version, source);
    }
  }