@SuppressWarnings("unchecked")
 public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
   Object entity = event.getObject();
   boolean newEntity = !event.getSession().contains(entity);
   if (newEntity) {
     if (beforeInsertCaller != null) {
       beforeInsertCaller.call(entity);
       if (event.getSession().contains(entity)) {
         EntityEntry entry = event.getEntry();
         if (entry != null) {
           Object[] state = entry.getLoadedState();
           synchronizePersisterState(entity, entry.getPersister(), state);
         }
       }
     }
     if (shouldTimestamp) {
       long time = System.currentTimeMillis();
       if (dateCreatedProperty != null && newEntity) {
         Object now =
             DefaultGroovyMethods.newInstance(dateCreatedProperty.getType(), new Object[] {time});
         dateCreatedProperty.setProperty(entity, now);
       }
       if (lastUpdatedProperty != null) {
         Object now =
             DefaultGroovyMethods.newInstance(lastUpdatedProperty.getType(), new Object[] {time});
         lastUpdatedProperty.setProperty(entity, now);
       }
     }
   }
 }
  @SuppressWarnings("unchecked")
  @Override
  /**
   * 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 = source.getEntityPersister(event.getEntityName(), entity);
    final Object version;

    if (persister.isVersioned()) {
      version = persister.getVersion(entity, source.getEntityMode());
      // Make sure version has not changed on deleted entities
      if ((entity instanceof TimelineEntity) && !((TimelineEntity) entity).isNew()) {
        if (!persister.getVersionType().isEqual(version, entityEntry.getVersion())) {
          throw new StaleObjectStateException(persister.getEntityName(), entityEntry.getId());
        }
      }
    }
    super.onDelete(event, transientEntities);
  }
  /** Convience method to retreive an entities next version value */
  private Object getNextVersion(FlushEntityEvent event) throws HibernateException {

    EntityEntry entry = event.getEntityEntry();
    EntityPersister persister = entry.getPersister();
    if (persister.isVersioned()) {

      Object[] values = event.getPropertyValues();

      if (entry.isBeingReplicated()) {
        return Versioning.getVersion(values, persister);
      } else {
        int[] dirtyProperties = event.getDirtyProperties();

        final boolean isVersionIncrementRequired =
            isVersionIncrementRequired(event, entry, persister, dirtyProperties);

        final Object nextVersion =
            isVersionIncrementRequired
                ? Versioning.increment(
                    entry.getVersion(), persister.getVersionType(), event.getSession())
                : entry.getVersion(); // use the current version

        Versioning.setVersion(values, nextVersion, persister);

        return nextVersion;
      }
    } else {
      return null;
    }
  }
 private void checkNaturalId(
     EntityPersister persister,
     EntityEntry entry,
     Object[] current,
     Object[] loaded,
     EntityMode entityMode,
     SessionImplementor session) {
   if (persister.hasNaturalIdentifier() && entry.getStatus() != Status.READ_ONLY) {
     Object[] snapshot = null;
     Type[] types = persister.getPropertyTypes();
     int[] props = persister.getNaturalIdentifierProperties();
     boolean[] updateable = persister.getPropertyUpdateability();
     for (int i = 0; i < props.length; i++) {
       int prop = props[i];
       if (!updateable[prop]) {
         Object loadedVal;
         if (loaded == null) {
           if (snapshot == null) {
             snapshot =
                 session.getPersistenceContext().getNaturalIdSnapshot(entry.getId(), persister);
           }
           loadedVal = snapshot[i];
         } else {
           loadedVal = loaded[prop];
         }
         if (!types[prop].isEqual(current[prop], loadedVal, entityMode)) {
           throw new HibernateException(
               "immutable natural identifier of an instance of "
                   + persister.getEntityName()
                   + " was altered");
         }
       }
     }
   }
 }
  private Object[] getValues(
      Object entity,
      EntityEntry entry,
      EntityMode entityMode,
      boolean mightBeDirty,
      SessionImplementor session) {
    final Object[] loadedState = entry.getLoadedState();
    final Status status = entry.getStatus();
    final EntityPersister persister = entry.getPersister();

    final Object[] values;
    if (status == Status.DELETED) {
      // grab its state saved at deletion
      values = entry.getDeletedState();
    } else if (!mightBeDirty && loadedState != null) {
      values = loadedState;
    } else {
      checkId(entity, persister, entry.getId(), entityMode, session);

      // grab its current state
      values = persister.getPropertyValues(entity, entityMode);

      checkNaturalId(persister, entry, values, loadedState, entityMode, session);
    }
    return values;
  }
  /**
   * Flushes a single entity's state to the database, by scheduling an update action, if necessary
   */
  public void onFlushEntity(FlushEntityEvent event) throws HibernateException {
    final Object entity = event.getEntity();
    final EntityEntry entry = event.getEntityEntry();
    final EventSource session = event.getSession();
    final EntityPersister persister = entry.getPersister();
    final Status status = entry.getStatus();
    final EntityMode entityMode = session.getEntityMode();
    final Type[] types = persister.getPropertyTypes();

    final boolean mightBeDirty = entry.requiresDirtyCheck(entity);

    final Object[] values = getValues(entity, entry, entityMode, mightBeDirty, session);

    event.setPropertyValues(values);

    // TODO: avoid this for non-new instances where mightBeDirty==false
    boolean substitute = wrapCollections(session, persister, types, values);

    if (isUpdateNecessary(event, mightBeDirty)) {
      substitute = scheduleUpdate(event) || substitute;
    }

    if (status != Status.DELETED) {
      // now update the object .. has to be outside the main if block above (because of collections)
      if (substitute) persister.setPropertyValues(entity, values, entityMode);

      // Search for collections by reachability, updating their role.
      // We don't want to touch collections reachable from a deleted object
      if (persister.hasCollections()) {
        new FlushVisitor(session, entity).processEntityPropertyValues(values, types);
      }
    }
  }
 private void throwObjectDeletedIfNecessary(LoadEvent event, EntityEntry oldEntry) {
   Status status = oldEntry.getStatus();
   if (status == Status.DELETED || status == Status.GONE) {
     throw new ObjectDeletedException(
         "The object with that id was deleted", event.getEntityId(), event.getEntityClassName());
   }
 }
 protected boolean invokeInterceptor(
     SessionImplementor session,
     Object entity,
     EntityEntry entry,
     final Object[] values,
     EntityPersister persister) {
   return session
       .getInterceptor()
       .onFlushDirty(
           entity,
           entry.getId(),
           values,
           entry.getLoadedState(),
           persister.getPropertyNames(),
           persister.getPropertyTypes());
 }
 private boolean isVersionIncrementRequired(
     FlushEntityEvent event, EntityEntry entry, EntityPersister persister, int[] dirtyProperties) {
   final boolean isVersionIncrementRequired =
       entry.getStatus() != Status.DELETED
           && (dirtyProperties == null
               || Versioning.isVersionIncrementRequired(
                   dirtyProperties,
                   event.hasDirtyCollection(),
                   persister.getPropertyVersionability()));
   return isVersionIncrementRequired;
 }
  /**
   * The given save-update event named a transient entity.
   *
   * <p>Here, we will perform the save processing.
   *
   * @param event The save event to be handled.
   * @return The entity's identifier after saving.
   */
  protected Serializable entityIsTransient(SaveOrUpdateEvent event) {

    LOG.trace("Saving transient instance");

    final EventSource source = event.getSession();

    EntityEntry entityEntry = event.getEntry();
    if (entityEntry != null) {
      if (entityEntry.getStatus() == Status.DELETED) {
        source.forceFlush(entityEntry);
      } else {
        throw new AssertionFailure("entity was persistent");
      }
    }

    Serializable id = saveWithGeneratedOrRequestedId(event);

    source.getPersistenceContext().reassociateProxy(event.getObject(), id);

    return id;
  }
 @Override
 protected boolean invokeInterceptor(
     SessionImplementor session,
     Object entity,
     EntityEntry entry,
     Object[] values,
     EntityPersister persister) {
   boolean isDirty = false;
   if (entry.getStatus() != Status.DELETED) {
     if (callbackHandler.preUpdate(entity)) {
       isDirty = copyState(entity, persister.getPropertyTypes(), values, session.getFactory());
     }
   }
   return super.invokeInterceptor(session, entity, entry, values, persister) || isDirty;
 }
  protected boolean handleInterception(FlushEntityEvent event) {
    SessionImplementor session = event.getSession();
    EntityEntry entry = event.getEntityEntry();
    EntityPersister persister = entry.getPersister();
    Object entity = event.getEntity();

    // give the Interceptor a chance to modify property values
    final Object[] values = event.getPropertyValues();
    final boolean intercepted = invokeInterceptor(session, entity, entry, values, persister);

    // now we might need to recalculate the dirtyProperties array
    if (intercepted && event.isDirtyCheckPossible() && !event.isDirtyCheckHandledByInterceptor()) {
      int[] dirtyProperties;
      if (event.hasDatabaseSnapshot()) {
        dirtyProperties =
            persister.findModified(event.getDatabaseSnapshot(), values, entity, session);
      } else {
        dirtyProperties = persister.findDirty(values, entry.getLoadedState(), entity, session);
      }
      event.setDirtyProperties(dirtyProperties);
    }

    return intercepted;
  }
  protected Serializable entityIsPersistent(SaveOrUpdateEvent event) throws HibernateException {
    LOG.trace("Ignoring persistent instance");

    EntityEntry entityEntry = event.getEntry();
    if (entityEntry == null) {
      throw new AssertionFailure("entity was transient or detached");
    } else {

      if (entityEntry.getStatus() == Status.DELETED) {
        throw new AssertionFailure("entity was deleted");
      }

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

      Serializable requestedId = event.getRequestedId();

      Serializable savedId;
      if (requestedId == null) {
        savedId = entityEntry.getId();
      } else {

        final boolean isEqual =
            !entityEntry
                .getPersister()
                .getIdentifierType()
                .isEqual(
                    requestedId, entityEntry.getId(), event.getSession().getEntityMode(), factory);

        if (isEqual) {
          throw new PersistentObjectException(
              "object passed to save() was already persistent: "
                  + MessageHelper.infoString(entityEntry.getPersister(), requestedId, factory));
        }

        savedId = requestedId;
      }

      if (LOG.isTraceEnabled())
        LOG.trace(
            "Object already associated with session: "
                + MessageHelper.infoString(entityEntry.getPersister(), savedId, factory));

      return savedId;
    }
  }
  public void execute() throws HibernateException {
    Serializable id = getId();
    EntityPersister persister = getPersister();
    SessionImplementor session = getSession();
    Object instance = getInstance();

    boolean veto = preDelete();

    Object version = this.version;
    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
      version = persister.getVersion(instance, session.getEntityMode());
    }

    final CacheKey ck;
    if (persister.hasCache()) {
      ck =
          new CacheKey(
              id,
              persister.getIdentifierType(),
              persister.getRootEntityName(),
              session.getEntityMode(),
              session.getFactory());
      lock = persister.getCacheAccessStrategy().lockItem(ck, version);
    } else {
      ck = null;
    }

    if (!isCascadeDeleteEnabled && !veto) {
      persister.delete(id, version, instance, session);
    }

    // postDelete:
    // After actually deleting a row, record the fact that the instance no longer
    // exists on the database (needed for identity-column key generation), and
    // remove it from the session cache
    final PersistenceContext persistenceContext = session.getPersistenceContext();
    EntityEntry entry = persistenceContext.removeEntry(instance);
    if (entry == null) {
      throw new AssertionFailure("possible nonthreadsafe access to session");
    }
    entry.postDelete();

    persistenceContext.removeEntity(entry.getEntityKey());
    persistenceContext.removeProxy(entry.getEntityKey());

    if (persister.hasCache()) {
      persister.getCacheAccessStrategy().remove(ck);
    }

    postDelete();

    if (getSession().getFactory().getStatistics().isStatisticsEnabled() && !veto) {
      getSession()
          .getFactory()
          .getStatisticsImplementor()
          .deleteEntity(getPersister().getEntityName());
    }
  }
  /** Perform a dirty check, and attach the results to the event */
  protected void dirtyCheck(FlushEntityEvent event) throws HibernateException {

    final Object entity = event.getEntity();
    final Object[] values = event.getPropertyValues();
    final SessionImplementor session = event.getSession();
    final EntityEntry entry = event.getEntityEntry();
    final EntityPersister persister = entry.getPersister();
    final Serializable id = entry.getId();
    final Object[] loadedState = entry.getLoadedState();

    int[] dirtyProperties =
        session
            .getInterceptor()
            .findDirty(
                entity,
                id,
                values,
                loadedState,
                persister.getPropertyNames(),
                persister.getPropertyTypes());

    event.setDatabaseSnapshot(null);

    final boolean interceptorHandledDirtyCheck;
    boolean cannotDirtyCheck;

    if (dirtyProperties == null) {
      // Interceptor returned null, so do the dirtycheck ourself, if possible
      interceptorHandledDirtyCheck = false;

      cannotDirtyCheck = loadedState == null; // object loaded by update()
      if (!cannotDirtyCheck) {
        // dirty check against the usual snapshot of the entity
        dirtyProperties = persister.findDirty(values, loadedState, entity, session);
      } else if (entry.getStatus() == Status.DELETED
          && !event.getEntityEntry().isModifiableEntity()) {
        // A non-modifiable (e.g., read-only or immutable) entity needs to be have
        // references to transient entities set to null before being deleted. No other
        // fields should be updated.
        if (values != entry.getDeletedState()) {
          throw new IllegalStateException(
              "Entity has status Status.DELETED but values != entry.getDeletedState");
        }
        // Even if loadedState == null, we can dirty-check by comparing currentState and
        // entry.getDeletedState() because the only fields to be updated are those that
        // refer to transient entities that are being set to null.
        // - currentState contains the entity's current property values.
        // - entry.getDeletedState() contains the entity's current property values with
        //   references to transient entities set to null.
        // - dirtyProperties will only contain properties that refer to transient entities
        final Object[] currentState =
            persister.getPropertyValues(event.getEntity(), event.getSession().getEntityMode());
        dirtyProperties =
            persister.findDirty(entry.getDeletedState(), currentState, entity, session);
        cannotDirtyCheck = false;
      } else {
        // dirty check against the database snapshot, if possible/necessary
        final Object[] databaseSnapshot = getDatabaseSnapshot(session, persister, id);
        if (databaseSnapshot != null) {
          dirtyProperties = persister.findModified(databaseSnapshot, values, entity, session);
          cannotDirtyCheck = false;
          event.setDatabaseSnapshot(databaseSnapshot);
        }
      }
    } else {
      // the Interceptor handled the dirty checking
      cannotDirtyCheck = false;
      interceptorHandledDirtyCheck = true;
    }

    logDirtyProperties(id, dirtyProperties, persister);

    event.setDirtyProperties(dirtyProperties);
    event.setDirtyCheckHandledByInterceptor(interceptorHandledDirtyCheck);
    event.setDirtyCheckPossible(!cannotDirtyCheck);
  }
  private boolean scheduleUpdate(final FlushEntityEvent event) {

    final EntityEntry entry = event.getEntityEntry();
    final EventSource session = event.getSession();
    final Object entity = event.getEntity();
    final Status status = entry.getStatus();
    final EntityMode entityMode = session.getEntityMode();
    final EntityPersister persister = entry.getPersister();
    final Object[] values = event.getPropertyValues();

    if (LOG.isTraceEnabled()) {
      if (status == Status.DELETED) {
        if (!persister.isMutable())
          LOG.trace(
              "Updating immutable, deleted entity: "
                  + MessageHelper.infoString(persister, entry.getId(), session.getFactory()));
        else if (!entry.isModifiableEntity())
          LOG.trace(
              "Updating non-modifiable, deleted entity: "
                  + MessageHelper.infoString(persister, entry.getId(), session.getFactory()));
        else
          LOG.trace(
              "Updating deleted entity: "
                  + MessageHelper.infoString(persister, entry.getId(), session.getFactory()));
      } else
        LOG.trace(
            "Updating entity: "
                + MessageHelper.infoString(persister, entry.getId(), session.getFactory()));
    }

    final boolean intercepted = !entry.isBeingReplicated() && handleInterception(event);

    // increment the version number (if necessary)
    final Object nextVersion = getNextVersion(event);

    // if it was dirtied by a collection only
    int[] dirtyProperties = event.getDirtyProperties();
    if (event.isDirtyCheckPossible() && dirtyProperties == null) {
      if (!intercepted && !event.hasDirtyCollection()) {
        throw new AssertionFailure("dirty, but no dirty properties");
      }
      dirtyProperties = ArrayHelper.EMPTY_INT_ARRAY;
    }

    // check nullability but do not doAfterTransactionCompletion command execute
    // we'll use scheduled updates for that.
    new Nullability(session).checkNullability(values, persister, true);

    // schedule the update
    // note that we intentionally do _not_ pass in currentPersistentState!
    session
        .getActionQueue()
        .addAction(
            new EntityUpdateAction(
                entry.getId(),
                values,
                dirtyProperties,
                event.hasDirtyCollection(),
                (status == Status.DELETED && !entry.isModifiableEntity()
                    ? persister.getPropertyValues(entity, entityMode)
                    : entry.getLoadedState()),
                entry.getVersion(),
                nextVersion,
                entity,
                entry.getRowId(),
                persister,
                session));

    return intercepted;
  }