/**
  * INTERNAL: Execute the query building the objects directly from the database result-set.
  *
  * @exception DatabaseException - an error has occurred on the database
  * @return object - the first object found or null if none.
  */
 protected Object executeObjectLevelReadQueryFromResultSet() throws DatabaseException {
   UnitOfWorkImpl unitOfWork = (UnitOfWorkImpl) getSession();
   DatabaseAccessor accessor = (DatabaseAccessor) unitOfWork.getAccessor();
   DatabasePlatform platform = accessor.getPlatform();
   DatabaseCall call = (DatabaseCall) getCall().clone();
   call.setQuery(this);
   call.translate(this.translationRow, null, unitOfWork);
   Statement statement = null;
   ResultSet resultSet = null;
   boolean exceptionOccured = false;
   try {
     accessor.incrementCallCount(unitOfWork);
     statement = call.prepareStatement(accessor, this.translationRow, unitOfWork);
     resultSet = accessor.executeSelect(call, statement, unitOfWork);
     ResultSetMetaData metaData = resultSet.getMetaData();
     Vector results = new Vector();
     ObjectBuilder builder = this.descriptor.getObjectBuilder();
     while (resultSet.next()) {
       results.add(
           builder.buildWorkingCopyCloneFromResultSet(
               this,
               this.joinedAttributeManager,
               resultSet,
               unitOfWork,
               accessor,
               metaData,
               platform));
     }
     return results;
   } catch (SQLException exception) {
     exceptionOccured = true;
     DatabaseException commException =
         accessor.processExceptionForCommError(session, exception, call);
     if (commException != null) throw commException;
     throw DatabaseException.sqlException(exception, call, accessor, unitOfWork, false);
   } finally {
     try {
       if (resultSet != null) {
         resultSet.close();
       }
       if (statement != null) {
         accessor.releaseStatement(statement, call.getSQLString(), call, unitOfWork);
       }
     } catch (SQLException exception) {
       if (!exceptionOccured) {
         // in the case of an external connection pool the connection may be null after the
         // statement release
         // if it is null we will be unable to check the connection for a comm error and
         // therefore must return as if it was not a comm error.
         DatabaseException commException =
             accessor.processExceptionForCommError(session, exception, call);
         if (commException != null) throw commException;
         throw DatabaseException.sqlException(exception, call, accessor, session, false);
       }
     }
   }
 }
 @Override
 public void preUpdate(DescriptorEvent event) {
   Object source = event.getSource();
   UnitOfWorkImpl unitOfWork = (UnitOfWorkImpl) event.getSession();
   // preUpdate is also generated for deleted objects that were modified in this UOW.
   // Do not perform preUpdate validation for such objects as preRemove would have already been
   // called.
   if (!unitOfWork.isObjectDeleted(source)) {
     validateOnCallbackEvent(event, "preUpdate", groupPreUpdate);
   }
 }
 /**
  * Triggers UnitOfWork valueholders directly without triggering the wrapped valueholder (this).
  *
  * <p>When in transaction and/or for pessimistic locking the UnitOfWorkValueHolder needs to be
  * triggered directly without triggering the wrapped valueholder. However only the wrapped
  * valueholder knows how to trigger the indirection, i.e. it may be a batchValueHolder, and it
  * stores all the info like the row and the query. Note: This method is not thread-safe. It must
  * be used in a synchronized manner. The batch value holder must use a batch query relative to the
  * unit of work, as the batch is local to the unit of work.
  */
 public Object instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder unitOfWorkValueHolder) {
   UnitOfWorkImpl unitOfWork = unitOfWorkValueHolder.getUnitOfWork();
   ReadQuery localQuery = unitOfWork.getBatchQueries().get(this.query);
   if (localQuery == null) {
     localQuery = (ReadQuery) this.query.clone();
     unitOfWork.getBatchQueries().put(this.query, localQuery);
   }
   return this.mapping.extractResultFromBatchQuery(
       localQuery,
       this.parentCacheKey,
       this.row,
       unitOfWorkValueHolder.getUnitOfWork(),
       this.originalQuery);
 }
  /**
   * INTERNAL: This method marks the object as changed. This method is only called by EclipseLink
   */
  public void internalPropertyChange(PropertyChangeEvent evt) {
    if (evt.getNewValue() == evt.getOldValue()) {
      return;
    }

    DatabaseMapping mapping =
        descriptor.getObjectBuilder().getMappingForAttributeName(evt.getPropertyName());
    // Bug#4127952 Throw an exception indicating there is no mapping for the property name.
    if (mapping == null) {
      throw ValidationException.wrongPropertyNameInChangeEvent(
          owner.getClass(), evt.getPropertyName());
    }
    if (mapping instanceof AbstractDirectMapping
        || mapping instanceof AbstractTransformationMapping) {
      // If both newValue and oldValue are null, or newValue is not null and newValue equals
      // oldValue, don't build ChangeRecord
      if (((evt.getNewValue() == null) && (evt.getOldValue() == null))
          || ((evt.getNewValue() != null) && (evt.getNewValue()).equals(evt.getOldValue()))) {
        return;
      }
    }

    super.internalPropertyChange(evt);

    if (uow.getUnitOfWorkChangeSet() == null) {
      uow.setUnitOfWorkChangeSet(new UnitOfWorkChangeSet(uow));
    }
    if (objectChangeSet == null) { // only null if new or if in a new UOW
      // add to tracker list to prevent GC of clone if using weak references
      // put it in here so that it only occurs on the 1st change for a particular UOW
      uow.addToChangeTrackedHardList(owner);
      objectChangeSet =
          getDescriptor()
              .getObjectBuilder()
              .createObjectChangeSet(
                  owner, (UnitOfWorkChangeSet) uow.getUnitOfWorkChangeSet(), false, uow);
    }

    if (evt.getClass().equals(ClassConstants.PropertyChangeEvent_Class)) {
      mapping.updateChangeRecord(
          evt.getSource(), evt.getNewValue(), evt.getOldValue(), objectChangeSet, getUnitOfWork());
    } else if (evt.getClass().equals(ClassConstants.CollectionChangeEvent_Class)
        || (evt.getClass().equals(ClassConstants.MapChangeEvent_Class))) {
      mapping.updateCollectionChangeRecord(
          (CollectionChangeEvent) evt, objectChangeSet, getUnitOfWork());
    } else {
      throw ValidationException.wrongChangeEvent(evt.getClass());
    }
  }
 /**
  * INTERNAL: Code was moved from UnitOfWork.internalExecuteQuery
  *
  * @param unitOfWork
  * @param translationRow
  * @return
  * @throws org.eclipse.persistence.exceptions.DatabaseException
  * @throws org.eclipse.persistence.exceptions.OptimisticLockException
  */
 protected Object executeInUnitOfWorkObjectLevelModifyQuery(
     UnitOfWorkImpl unitOfWork, AbstractRecord translationRow)
     throws DatabaseException, OptimisticLockException {
   Object result = unitOfWork.processDeleteObjectQuery(this);
   if (result != null) {
     // if the above method returned something then the unit of work
     // was not writing so the object has been stored to delete later
     // so return the object.  See the above method for the cases
     // where this object will be returned.
     return result;
   }
   return super.executeInUnitOfWorkObjectLevelModifyQuery(unitOfWork, translationRow);
 }
  /**
   * INTERNAL This method iterates through a collection and gets the values from the objects to
   * conform in an in-memory query. Creation date: (1/19/01 1:18:27 PM)
   */
  public Object valuesFromCollection(
      Object object, AbstractSession session, int valueHolderPolicy, boolean isObjectUnregistered) {
    // in case the mapping is null - this can happen if a query key is being used
    // In this case, check for the query key and find it's mapping.
    boolean readMappingFromQueryKey = false;
    if (getMapping() == null) {
      getMappingFromQueryKey();
      readMappingFromQueryKey = true;
    }

    // For bug 2780817 get the mapping directly from the object.  In EJB 2.0
    // inheritance, each child must override mappings defined in an abstract
    // class with its own.
    DatabaseMapping mapping = this.mapping;
    ClassDescriptor descriptor = mapping.getDescriptor();
    if (descriptor.hasInheritance() && (descriptor.getJavaClass() != object.getClass())) {
      mapping =
          session
              .getDescriptor(object.getClass())
              .getObjectBuilder()
              .getMappingForAttributeName(getName());
      descriptor = mapping.getDescriptor();
    }

    // fetch group support
    if (descriptor.hasFetchGroupManager()) {
      FetchGroupManager fetchGroupManager = descriptor.getFetchGroupManager();
      if (fetchGroupManager.isPartialObject(object)
          && (!fetchGroupManager.isAttributeFetched(object, mapping.getAttributeName()))) {
        // the conforming attribute is not fetched, simply throw exception
        throw QueryException.cannotConformUnfetchedAttribute(mapping.getAttributeName());
      }
    }

    if (mapping.isDirectToFieldMapping()) {
      return ((AbstractDirectMapping) mapping).valueFromObject(object, mapping.getField(), session);
    } else if (mapping.isForeignReferenceMapping()) {
      // CR 3677 integration of a ValueHolderPolicy
      Object valueFromMapping = mapping.getAttributeValueFromObject(object);
      if (!((ForeignReferenceMapping) mapping)
          .getIndirectionPolicy()
          .objectIsInstantiated(valueFromMapping)) {
        if (valueHolderPolicy != InMemoryQueryIndirectionPolicy.SHOULD_TRIGGER_INDIRECTION) {
          // If the client wishes us to trigger the indirection then we should do so,
          // Other wise throw the exception
          throw QueryException
              .mustInstantiateValueholders(); // you should instantiate the valueholder for this to
                                              // work
        }

        // maybe we should throw this exception from the start, to save time
      }
      Object valueToIterate = mapping.getRealAttributeValueFromObject(object, session);
      UnitOfWorkImpl uow = isObjectUnregistered ? (UnitOfWorkImpl) session : null;

      // First check that object in fact is unregistered.
      // toDo: ?? Why is this commented out? Why are we supporting the unregistered thing at all?
      // Does not seem to be any public API for this, nor every used internally?
      // if (isObjectUnregistered) {
      //	isObjectUnregistered = !uow.getCloneMapping().containsKey(object);
      // }
      if (mapping.isCollectionMapping() && (valueToIterate != null)) {
        // For bug 2766379 must use the correct version of vectorFor to
        // unwrap the result same time.
        valueToIterate = mapping.getContainerPolicy().vectorFor(valueToIterate, session);

        // toDo: If the value is empty, need to support correct inner/outer join filtering
        // symantics.
        // For CR 2612601, try to partially replace the result with already
        // registered objects.
        if (isObjectUnregistered && (uow.getCloneMapping().get(object) == null)) {
          Vector objectValues = (Vector) valueToIterate;
          for (int i = 0; i < objectValues.size(); i++) {
            Object original = objectValues.elementAt(i);
            Object clone =
                uow.getIdentityMapAccessorInstance()
                    .getIdentityMapManager()
                    .getFromIdentityMap(original);
            if (clone != null) {
              objectValues.setElementAt(clone, i);
            }
          }
        }

        // For CR 2612601, conforming without registering, a query could be
        // bob.get("address").get("city").equal("Ottawa"); where the address
        // has been registered and modified in the UOW, but bob has not.  Thus
        // even though bob does not point to the modified address now, it will
        // as soon as it is registered, so should point to it here.
      } else if (isObjectUnregistered && (uow.getCloneMapping().get(object) == null)) {
        Object clone =
            uow.getIdentityMapAccessorInstance()
                .getIdentityMapManager()
                .getFromIdentityMap(valueToIterate);
        if (clone != null) {
          valueToIterate = clone;
        }
      }
      return valueToIterate;
    } else if (mapping.isAggregateMapping()) {
      Object aggregateValue = mapping.getAttributeValueFromObject(object);
      // Bug 3995468 - if this query key is to a mapping in an aggregate object, get the object from
      // actual mapping rather than the aggregate mapping
      while (readMappingFromQueryKey
          && mapping.isAggregateObjectMapping()
          && !((AggregateObjectMapping) mapping)
              .getReferenceClass()
              .equals(queryKey.getDescriptor().getJavaClass())) {
        mapping =
            mapping
                .getReferenceDescriptor()
                .getObjectBuilder()
                .getMappingForField(((DirectQueryKey) queryKey).getField());
        aggregateValue = mapping.getRealAttributeValueFromObject(aggregateValue, session);
      }
      return aggregateValue;
    } else {
      throw QueryException.cannotConformExpression();
    }
  }
  /**
   * INTERNAL: All objects queried via a UnitOfWork get registered here. If the query went to the
   * database.
   *
   * <p>Involves registering the query result individually and in totality, and hence refreshing /
   * conforming is done here.
   *
   * @param result may be collection (read all) or an object (read one), or even a cursor. If in
   *     transaction the shared cache will be bypassed, meaning the result may not be originals from
   *     the parent but raw database rows.
   * @param unitOfWork the unitOfWork the result is being registered in.
   * @param arguments the original arguments/parameters passed to the query execution. Used by
   *     conforming
   * @param buildDirectlyFromRows If in transaction must construct a registered result from raw
   *     database rows.
   * @return the final (conformed, refreshed, wrapped) UnitOfWork query result
   */
  public Object registerResultInUnitOfWork(
      Object result,
      UnitOfWorkImpl unitOfWork,
      AbstractRecord arguments,
      boolean buildDirectlyFromRows) {
    // For bug 2612366: Conforming results in UOW extremely slow.
    // Replacing results with registered versions in the UOW is a part of
    // conforming and is now done while conforming to maximize performance.
    if (shouldConformResultsInUnitOfWork()
        || this.descriptor.shouldAlwaysConformResultsInUnitOfWork()) {
      return conformResult(result, unitOfWork, arguments, buildDirectlyFromRows);
    }

    // When building directly from rows, one of the performance benefits
    // is that we no longer have to wrap and then unwrap the originals.
    // result is just a vector, not a collection of wrapped originals.
    // Also for cursors the initial connection is automatically registered.
    if (buildDirectlyFromRows) {
      List<AbstractRecord> rows = (List<AbstractRecord>) result;
      ContainerPolicy cp = getContainerPolicy();
      int size = rows.size();
      Object clones = cp.containerInstance(size);
      for (int index = 0; index < size; index++) {
        AbstractRecord row = rows.get(index);

        // null is placed in the row collection for 1-m joining to filter duplicate rows.
        if (row != null) {
          Object clone = buildObject(row);
          cp.addInto(clone, clones, unitOfWork, row, this);
        }
      }
      return clones;
    }

    ContainerPolicy cp;
    Cursor cursor = null;

    // If the query is redirected then the collection returned might no longer
    // correspond to the original container policy.  CR#2342-S.M.
    if (getRedirector() != null) {
      cp = ContainerPolicy.buildPolicyFor(result.getClass());
    } else {
      cp = getContainerPolicy();
    }

    // In the case of cursors just register the initially read collection.
    if (cp.isCursorPolicy()) {
      cursor = (Cursor) result;
      // In nested UnitOfWork session might have been session of the parent.
      cursor.setSession(unitOfWork);
      cp = ContainerPolicy.buildPolicyFor(ClassConstants.Vector_class);
      result = cursor.getObjectCollection();
    }

    Object clones = cp.containerInstance(cp.sizeFor(result));
    AbstractSession sessionToUse = unitOfWork.getParent();
    for (Object iter = cp.iteratorFor(result); cp.hasNext(iter); ) {
      Object object = cp.next(iter, sessionToUse);
      Object clone = registerIndividualResult(object, unitOfWork, this.joinedAttributeManager);
      cp.addInto(clone, clones, unitOfWork);
    }
    if (cursor != null) {
      cursor.setObjectCollection((Vector) clones);
      return cursor;
    } else {
      return clones;
    }
  }
  /** INTERNAL: Conform the result if specified. */
  protected Object conformResult(
      Object result,
      UnitOfWorkImpl unitOfWork,
      AbstractRecord arguments,
      boolean buildDirectlyFromRows) {
    if (getSelectionCriteria() != null) {
      ExpressionBuilder builder = getSelectionCriteria().getBuilder();
      builder.setSession(unitOfWork.getRootSession(null));
      builder.setQueryClass(getReferenceClass());
    }

    // If the query is redirected then the collection returned might no longer
    // correspond to the original container policy.  CR#2342-S.M.
    ContainerPolicy cp;
    if (getRedirector() != null) {
      cp = ContainerPolicy.buildPolicyFor(result.getClass());
    } else {
      cp = getContainerPolicy();
    }

    // This code is now a great deal different...  For one, registration is done
    // as part of conforming.  Also, this should only be called if one actually
    // is conforming.
    // First scan the UnitOfWork for conforming instances.
    // This will walk through the entire cache of registered objects.
    // Let p be objects from result not in the cache.
    // Let c be objects from cache.
    // Presently p intersect c = empty set, but later p subset c.
    // By checking cache now doesConform will be called p fewer times.
    Map indexedInterimResult =
        unitOfWork.scanForConformingInstances(
            getSelectionCriteria(), getReferenceClass(), arguments, this);

    Cursor cursor = null;
    // In the case of cursors just conform/register the initially read collection.
    if (cp.isCursorPolicy()) {
      cursor = (Cursor) result;
      cp = ContainerPolicy.buildPolicyFor(ClassConstants.Vector_class);
      // In nested UnitOfWork session might have been session of the parent.
      cursor.setSession(unitOfWork);
      result = cursor.getObjectCollection();
      // for later incremental conforming...
      cursor.setInitiallyConformingIndex(indexedInterimResult);
      cursor.setSelectionCriteriaClone(getSelectionCriteria());
      cursor.setTranslationRow(arguments);
    }

    // Now conform the result from the database.
    // Remove any deleted or changed objects that no longer conform.
    // Deletes will only work for simple queries, queries with or's or anyof's may not return
    // correct results when untriggered indirection is in the model.
    Vector fromDatabase = null;

    // When building directly from rows, one of the performance benefits
    // is that we no longer have to wrap and then unwrap the originals.
    // result is just a vector, not a container of wrapped originals.
    if (buildDirectlyFromRows) {
      Vector rows = (Vector) result;
      fromDatabase = new Vector(rows.size());
      for (int i = 0; i < rows.size(); i++) {
        Object object = rows.elementAt(i);
        // null is placed in the row collection for 1-m joining to filter duplicate rows.
        if (object != null) {
          Object clone =
              conformIndividualResult(
                  object,
                  unitOfWork,
                  arguments,
                  getSelectionCriteria(),
                  indexedInterimResult,
                  buildDirectlyFromRows);
          if (clone != null) {
            fromDatabase.addElement(clone);
          }
        }
      }
    } else {
      fromDatabase = new Vector(cp.sizeFor(result));
      AbstractSession sessionToUse = unitOfWork.getParent();
      for (Object iter = cp.iteratorFor(result); cp.hasNext(iter); ) {
        Object object = cp.next(iter, sessionToUse);
        Object clone =
            conformIndividualResult(
                object,
                unitOfWork,
                arguments,
                getSelectionCriteria(),
                indexedInterimResult,
                buildDirectlyFromRows);
        if (clone != null) {
          fromDatabase.addElement(clone);
        }
      }
    }

    // Now add the unwrapped conforming instances into an appropriate container.
    // Wrapping is done automatically.
    // Make sure a vector of exactly the right size is returned.
    Object conformedResult =
        cp.containerInstance(indexedInterimResult.size() + fromDatabase.size());
    Object eachClone;
    for (Iterator enumtr = indexedInterimResult.values().iterator(); enumtr.hasNext(); ) {
      eachClone = enumtr.next();
      cp.addInto(eachClone, conformedResult, unitOfWork);
    }
    for (Enumeration enumtr = fromDatabase.elements(); enumtr.hasMoreElements(); ) {
      eachClone = enumtr.nextElement();
      cp.addInto(eachClone, conformedResult, unitOfWork);
    }

    if (cursor != null) {
      cursor.setObjectCollection((Vector) conformedResult);

      // For nested UOW must copy all in object collection to
      // initiallyConformingIndex, as some of these could have been from
      // the parent UnitOfWork.
      if (unitOfWork.isNestedUnitOfWork()) {
        for (Enumeration enumtr = cursor.getObjectCollection().elements();
            enumtr.hasMoreElements(); ) {
          Object clone = enumtr.nextElement();
          indexedInterimResult.put(clone, clone);
        }
      }
      return cursor;
    } else {
      return conformedResult;
    }
  }
  /**
   * INTERNAL: Perform the work to delete an object.
   *
   * @return object - the object being deleted.
   */
  public Object executeDatabaseQuery() throws DatabaseException, OptimisticLockException {
    AbstractSession session = getSession();
    CommitManager commitManager = session.getCommitManager();
    Object object = getObject();
    boolean isUnitOfWork = session.isUnitOfWork();
    try {
      // Check if the object has already been committed, then no work is required
      if (commitManager.isProcessedCommit(object)) {
        return object;
      }
      commitManager.markPreModifyCommitInProgress(getObject());
      if (!isUnitOfWork) {
        session.beginTransaction();
      }
      ClassDescriptor descriptor = this.descriptor;
      DescriptorEventManager eventManager = descriptor.getEventManager();
      // PERF: Avoid events if no listeners.
      if (eventManager.hasAnyEventListeners()) {
        // Need to run pre-delete selector if available
        eventManager.executeEvent(new DescriptorEvent(DescriptorEventManager.PreDeleteEvent, this));
      }

      // Verify if deep shallow modify is turned on
      if (shouldCascadeParts()) {
        descriptor.getQueryManager().preDelete(this);
      }

      // CR#2660080 missing aboutToDelete event.
      // PERF: Avoid events if no listeners.
      if (eventManager.hasAnyEventListeners()) {
        DescriptorEvent event =
            new DescriptorEvent(DescriptorEventManager.AboutToDeleteEvent, this);
        event.setRecord(getModifyRow());
        eventManager.executeEvent(event);
      }

      if (QueryMonitor.shouldMonitor()) {
        QueryMonitor.incrementDelete(this);
      }
      int rowCount = 0;
      // If the object was/will be deleted from a cascade delete constraint, ignore it.
      if (isUnitOfWork
          && ((UnitOfWorkImpl) session).hasCascadeDeleteObjects()
          && ((UnitOfWorkImpl) session).getCascadeDeleteObjects().contains(object)) {
        // Cascade delete does not check optimistic lock, assume ok.
        rowCount = 1;
      } else {
        rowCount = getQueryMechanism().deleteObject().intValue();
      }

      if (rowCount < 1) {
        if (session.hasEventManager()) {
          session.getEventManager().noRowsModified(this, object);
        }
      }

      if (descriptor.usesOptimisticLocking()) {
        descriptor.getOptimisticLockingPolicy().validateDelete(rowCount, object, this);
      }

      commitManager.markPostModifyCommitInProgress(getObject());
      // Verify if deep shallow modify is turned on
      if (shouldCascadeParts()) {
        descriptor.getQueryManager().postDelete(this);
      }

      if ((descriptor.getHistoryPolicy() != null)
          && descriptor.getHistoryPolicy().shouldHandleWrites()) {
        descriptor.getHistoryPolicy().postDelete(this);
      }

      // PERF: Avoid events if no listeners.
      if (eventManager.hasAnyEventListeners()) {
        // Need to run post-delete selector if available
        eventManager.executeEvent(
            new DescriptorEvent(DescriptorEventManager.PostDeleteEvent, this));
      }

      if (!isUnitOfWork) {
        session.commitTransaction();
      }
      commitManager.markCommitCompleted(object);

      // CR3510313, avoid removing aggregate collections from cache (maintain cache is false).
      if (shouldMaintainCache()) {
        if (isUnitOfWork) {
          ((UnitOfWorkImpl) session).addObjectDeletedDuringCommit(object, descriptor);
        } else {
          session
              .getIdentityMapAccessorInstance()
              .removeFromIdentityMap(
                  getPrimaryKey(), descriptor.getJavaClass(), descriptor, object);
        }
      }
      return object;

    } catch (RuntimeException exception) {
      if (!isUnitOfWork) {
        session.rollbackTransaction();
      }
      commitManager.markCommitCompleted(object);
      throw exception;
    }
  }
  /**
   * INTERNAL: Perform the work to delete an object.
   *
   * @return object - the object being deleted.
   */
  public Object executeDatabaseQuery() throws DatabaseException, OptimisticLockException {
    AbstractSession session = getSession();
    CommitManager commitManager = session.getCommitManager();
    Object object = getObject();
    boolean isUnitOfWork = session.isUnitOfWork();
    try {
      // Check if the object has already been deleted, then no work is required.
      if (commitManager.isCommitCompletedInPostOrIgnore(object)) {
        return object;
      }
      ClassDescriptor descriptor = this.descriptor;
      // Check whether the object is already being deleted,
      // if it is, then there is a cycle, and the foreign keys must be nulled.
      if (commitManager.isCommitInPreModify(object)) {
        if (!commitManager.isShallowCommitted(object)) {
          getQueryMechanism().updateForeignKeyFieldBeforeDelete();
          if ((descriptor.getHistoryPolicy() != null)
              && descriptor.getHistoryPolicy().shouldHandleWrites()) {
            descriptor.getHistoryPolicy().postUpdate(this);
          }
          commitManager.markShallowCommit(object);
        }
        return object;
      }
      commitManager.markPreModifyCommitInProgress(getObject());
      if (!isUnitOfWork) {
        session.beginTransaction();
      }
      DescriptorEventManager eventManager = descriptor.getEventManager();
      // PERF: Avoid events if no listeners.
      if (eventManager.hasAnyEventListeners()) {
        // Need to run pre-delete selector if available
        eventManager.executeEvent(new DescriptorEvent(DescriptorEventManager.PreDeleteEvent, this));
      }

      // Verify if deep shallow modify is turned on
      if (shouldCascadeParts()) {
        descriptor.getQueryManager().preDelete(this);
      }

      // Check for deletion dependencies.
      if (isUnitOfWork) {
        Set dependencies = ((UnitOfWorkImpl) session).getDeletionDependencies(object);
        if (dependencies != null) {
          for (Object dependency : dependencies) {
            if (!commitManager.isCommitCompletedInPostOrIgnore(dependency)) {
              ClassDescriptor dependencyDescriptor = session.getDescriptor(dependency);
              // PERF: Get the descriptor query, to avoid extra query creation.
              DeleteObjectQuery deleteQuery =
                  dependencyDescriptor.getQueryManager().getDeleteQuery();
              if (deleteQuery == null) {
                deleteQuery = new DeleteObjectQuery();
                deleteQuery.setDescriptor(dependencyDescriptor);
              } else {
                // Ensure original query has been prepared.
                deleteQuery.checkPrepare(session, deleteQuery.getTranslationRow());
                deleteQuery = (DeleteObjectQuery) deleteQuery.clone();
              }
              deleteQuery.setIsExecutionClone(true);
              deleteQuery.setObject(dependency);
              session.executeQuery(deleteQuery);
            }
          }
        }
      }

      // CR#2660080 missing aboutToDelete event.
      // PERF: Avoid events if no listeners.
      if (eventManager.hasAnyEventListeners()) {
        DescriptorEvent event =
            new DescriptorEvent(DescriptorEventManager.AboutToDeleteEvent, this);
        event.setRecord(getModifyRow());
        eventManager.executeEvent(event);
      }

      if (QueryMonitor.shouldMonitor()) {
        QueryMonitor.incrementDelete(this);
      }
      int rowCount = 0;
      // If the object was/will be deleted from a cascade delete constraint, ignore it.
      if (isUnitOfWork
          && ((UnitOfWorkImpl) session).hasCascadeDeleteObjects()
          && ((UnitOfWorkImpl) session).getCascadeDeleteObjects().contains(object)) {
        // Cascade delete does not check optimistic lock, assume ok.
        rowCount = 1;
      } else {
        rowCount = getQueryMechanism().deleteObject().intValue();
      }

      if (rowCount < 1) {
        if (session.hasEventManager()) {
          session.getEventManager().noRowsModified(this, object);
        }
      }

      if (this.usesOptimisticLocking) {
        descriptor.getOptimisticLockingPolicy().validateDelete(rowCount, object, this);
      }

      commitManager.markPostModifyCommitInProgress(getObject());
      // Verify if deep shallow modify is turned on
      if (shouldCascadeParts()) {
        descriptor.getQueryManager().postDelete(this);
      }

      if ((descriptor.getHistoryPolicy() != null)
          && descriptor.getHistoryPolicy().shouldHandleWrites()) {
        descriptor.getHistoryPolicy().postDelete(this);
      }

      // PERF: Avoid events if no listeners.
      if (eventManager.hasAnyEventListeners()) {
        // Need to run post-delete selector if available
        eventManager.executeEvent(
            new DescriptorEvent(DescriptorEventManager.PostDeleteEvent, this));
      }

      if (!isUnitOfWork) {
        session.commitTransaction();
      }
      commitManager.markCommitCompleted(object);

      // CR3510313, avoid removing aggregate collections from cache (maintain cache is false).
      if (shouldMaintainCache()) {
        if (isUnitOfWork) {
          ((UnitOfWorkImpl) session).addObjectDeletedDuringCommit(object, descriptor);
        } else {
          session
              .getIdentityMapAccessorInstance()
              .removeFromIdentityMap(
                  getPrimaryKey(), descriptor.getJavaClass(), descriptor, object);
        }
      }
      return object;

    } catch (RuntimeException exception) {
      if (!isUnitOfWork) {
        session.rollbackTransaction();
      }
      commitManager.markCommitCompleted(object);
      throw exception;
    }
  }