/**
   * Handle the given lock event.
   *
   * @param event The lock event to be handled.
   * @throws HibernateException
   */
  public void onLock(LockEvent event) throws HibernateException {

    if (event.getObject() == null) {
      throw new NullPointerException("attempted to lock null");
    }

    if (event.getLockMode() == LockMode.WRITE) {
      throw new HibernateException("Invalid lock mode for lock()");
    }

    SessionImplementor source = event.getSession();

    Object entity = source.getPersistenceContext().unproxyAndReassociate(event.getObject());
    // TODO: if object was an uninitialized proxy, this is inefficient,
    //      resulting in two SQL selects

    EntityEntry entry = source.getPersistenceContext().getEntry(entity);
    if (entry == null) {
      final EntityPersister persister = source.getEntityPersister(event.getEntityName(), entity);
      final Serializable id = persister.getIdentifier(entity, source.getEntityMode());
      if (!ForeignKeys.isNotTransient(event.getEntityName(), entity, Boolean.FALSE, source)) {
        throw new TransientObjectException(
            "cannot lock an unsaved transient instance: " + persister.getEntityName());
      }

      entry = reassociate(event, entity, id, persister);

      cascadeOnLock(event, persister, entity);
    }

    upgradeLock(entity, entry, event.getLockMode(), source);
  }
  /** make sure user didn't mangle the id */
  public void checkId(
      Object object,
      EntityPersister persister,
      Serializable id,
      EntityMode entityMode,
      SessionImplementor session)
      throws HibernateException {

    if (id != null && id instanceof DelayedPostInsertIdentifier) {
      // this is a situation where the entity id is assigned by a post-insert generator
      // and was saved outside the transaction forcing it to be delayed
      return;
    }

    if (persister.canExtractIdOutOfEntity()) {

      Serializable oid = persister.getIdentifier(object, session);
      if (id == null) {
        throw new AssertionFailure(
            "null id in "
                + persister.getEntityName()
                + " entry (don't flush the Session after an exception occurs)");
      }
      if (!persister.getIdentifierType().isEqual(id, oid, entityMode, session.getFactory())) {
        throw new HibernateException(
            "identifier of an instance of "
                + persister.getEntityName()
                + " was altered from "
                + id
                + " to "
                + oid);
      }
    }
  }
  /**
   * Reads the entity hosting the association from the datastore and applies any property changes
   * from the server side.
   */
  private void updateHostingEntityIfRequired() {
    if (hostingEntity != null && hostingEntityRequiresReadAfterUpdate()) {
      EntityPersister entityPersister = getHostingEntityPersister();

      entityPersister.processUpdateGeneratedProperties(
          entityPersister.getIdentifier(hostingEntity, session),
          hostingEntity,
          new Object[entityPersister.getPropertyNames().length],
          session);
    }
  }
 /**
  * Determine the id to use for updating.
  *
  * @param entity The entity.
  * @param persister The entity persister
  * @param requestedId The requested identifier
  * @param session The session
  * @return The id.
  * @throws TransientObjectException If the entity is considered transient.
  */
 protected Serializable getUpdateId(
     Object entity,
     EntityPersister persister,
     Serializable requestedId,
     SessionImplementor session) {
   // use the id assigned to the instance
   Serializable id = persister.getIdentifier(entity, session);
   if (id == null) {
     // assume this is a newly instantiated transient object
     // which should be saved rather than updated
     throw new TransientObjectException(
         "The given object has a null identifier: " + persister.getEntityName());
   } else {
     return id;
   }
 }
  /**
   * 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);
    }
  }