All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.camunda.bpm.engine.impl.db.entitymanager.operation.DbOperationManager Maven / Gradle / Ivy

There is a newer version: 7.22.0-alpha1
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.camunda.bpm.engine.impl.db.entitymanager.operation;

import static org.camunda.bpm.engine.impl.db.entitymanager.operation.DbOperationType.DELETE;
import static org.camunda.bpm.engine.impl.db.entitymanager.operation.DbOperationType.INSERT;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.camunda.bpm.engine.impl.db.DbEntity;
import org.camunda.bpm.engine.impl.db.HasDbReferences;
import org.camunda.bpm.engine.impl.db.entitymanager.operation.comparator.DbBulkOperationComparator;
import org.camunda.bpm.engine.impl.db.entitymanager.operation.comparator.DbEntityOperationComparator;
import org.camunda.bpm.engine.impl.db.entitymanager.operation.comparator.EntityTypeComparatorForInserts;
import org.camunda.bpm.engine.impl.db.entitymanager.operation.comparator.EntityTypeComparatorForModifications;

/**
 * Manages a set of {@link DbOperation database operations}.
 *
 * @author Daniel Meyer
 *
 */
public class DbOperationManager {

  // comparators ////////////////

  public static Comparator> INSERT_TYPE_COMPARATOR = new EntityTypeComparatorForInserts();
  public static Comparator> MODIFICATION_TYPE_COMPARATOR = new EntityTypeComparatorForModifications();
  public static Comparator INSERT_OPERATION_COMPARATOR = new DbEntityOperationComparator();
  public static Comparator MODIFICATION_OPERATION_COMPARATOR  = new DbEntityOperationComparator();
  public static Comparator BULK_OPERATION_COMPARATOR = new DbBulkOperationComparator();

  // pre-sorted operation maps //////////////

  /** INSERTs */
  public SortedMap, SortedSet> inserts = new TreeMap, SortedSet>(INSERT_TYPE_COMPARATOR);

  /** UPDATEs of a single entity */
  public SortedMap, SortedSet> updates = new TreeMap, SortedSet>(MODIFICATION_TYPE_COMPARATOR);

  /** DELETEs of a single entity */
  public SortedMap, SortedSet> deletes = new TreeMap, SortedSet>(MODIFICATION_TYPE_COMPARATOR);

  /** bulk modifications (DELETE, UPDATE) on an entity collection */
  public SortedMap, SortedSet> bulkOperations = new TreeMap, SortedSet>(MODIFICATION_TYPE_COMPARATOR);

  /** bulk modifications (DELETE, UPDATE) for which order of execution is important */
  public LinkedHashSet bulkOperationsInsertionOrder = new LinkedHashSet();

  public boolean addOperation(DbEntityOperation newOperation) {
    if(newOperation.getOperationType() == INSERT) {
      return getInsertsForType(newOperation.getEntityType(), true)
          .add(newOperation);

    } else if(newOperation.getOperationType() == DELETE) {
      return getDeletesByType(newOperation.getEntityType(), true)
          .add(newOperation);

    } else { // UPDATE
      return getUpdatesByType(newOperation.getEntityType(), true)
          .add(newOperation);

    }
  }

  protected SortedSet getDeletesByType(Class type, boolean create) {
    SortedSet deletesByType = deletes.get(type);
    if(deletesByType == null && create) {
      deletesByType = new TreeSet(MODIFICATION_OPERATION_COMPARATOR);
      deletes.put(type, deletesByType);
    }
    return deletesByType;
  }

  protected SortedSet getUpdatesByType(Class type, boolean create) {
    SortedSet updatesByType = updates.get(type);
    if(updatesByType == null && create) {
      updatesByType = new TreeSet(MODIFICATION_OPERATION_COMPARATOR);
      updates.put(type, updatesByType);
    }
    return updatesByType;
  }

  protected SortedSet getInsertsForType(Class type, boolean create) {
    SortedSet insertsByType = inserts.get(type);
    if(insertsByType == null && create) {
      insertsByType = new TreeSet(INSERT_OPERATION_COMPARATOR);
      inserts.put(type, insertsByType);
    }
    return insertsByType;
  }

  public boolean addOperation(DbBulkOperation newOperation) {
    SortedSet bulksByType = bulkOperations.get(newOperation.getEntityType());
    if(bulksByType == null) {
      bulksByType = new TreeSet(BULK_OPERATION_COMPARATOR);
      bulkOperations.put(newOperation.getEntityType(), bulksByType);
    }

    return bulksByType.add(newOperation);
  }

  public boolean addOperationPreserveOrder(DbBulkOperation newOperation) {
    return bulkOperationsInsertionOrder.add(newOperation);
  }

  public List calculateFlush() {
    List flush = new ArrayList();
    // first INSERTs
    addSortedInserts(flush);
    // then UPDATEs + DELETEs
    addSortedModifications(flush);
    
    determineDependencies(flush);
    return flush;
  }

  /** Adds the insert operations to the flush (in correct order).
   * @param operationsForFlush */
  protected void addSortedInserts(List flush) {
    for (Entry, SortedSet> operationsForType : inserts.entrySet()) {

      // add inserts to flush
      if(HasDbReferences.class.isAssignableFrom(operationsForType.getKey())) {
        // if this type has self references, we need to resolve the reference order
        flush.addAll(sortByReferences(operationsForType.getValue()));
      } else {
        flush.addAll(operationsForType.getValue());
      }
    }
  }

  /** Adds a correctly ordered list of UPDATE and DELETE operations to the flush.
   * @param flush */
  protected void addSortedModifications(List flush) {

    // calculate sorted set of all modified entity types
    SortedSet> modifiedEntityTypes = new TreeSet>(MODIFICATION_TYPE_COMPARATOR);
    modifiedEntityTypes.addAll(updates.keySet());
    modifiedEntityTypes.addAll(deletes.keySet());
    modifiedEntityTypes.addAll(bulkOperations.keySet());

    for (Class type : modifiedEntityTypes) {
      // first perform entity UPDATES
      addSortedModificationsForType(type, updates.get(type), flush);
      // next perform entity DELETES
      addSortedModificationsForType(type, deletes.get(type), flush);
      // last perform bulk operations
      SortedSet bulkOperationsForType = bulkOperations.get(type);
      if(bulkOperationsForType != null) {
        flush.addAll(bulkOperationsForType);
      }
    }

    //the very last perform bulk operations for which the order is important
    if(bulkOperationsInsertionOrder != null) {
      flush.addAll(bulkOperationsInsertionOrder);
    }
  }

  protected void addSortedModificationsForType(Class type, SortedSet preSortedOperations, List flush) {
    if(preSortedOperations != null) {
      if(HasDbReferences.class.isAssignableFrom(type)) {
        // if this type has self references, we need to resolve the reference order
        flush.addAll(sortByReferences(preSortedOperations));
      } else {
        flush.addAll(preSortedOperations);
      }
    }
  }


  /**
   * Assumptions:
   * a) all operations in the set work on entities such that the entities implement {@link HasDbReferences}.
   * b) all operations in the set work on the same type (ie. all operations are INSERTs or DELETEs).
   *
   */
  protected List sortByReferences(SortedSet preSorted) {
    // copy the pre-sorted set and apply final sorting to list
    List opList = new ArrayList(preSorted);

    for (int i = 0; i < opList.size(); i++) {

      DbEntityOperation currentOperation = opList.get(i);
      DbEntity currentEntity = currentOperation.getEntity();
      Set currentReferences = currentOperation.getFlushRelevantEntityReferences();

      // check whether this operation must be placed after another operation
      int moveTo = i;
      for(int k = i+1; k < opList.size(); k++) {
        DbEntityOperation otherOperation = opList.get(k);
        DbEntity otherEntity = otherOperation.getEntity();
        Set otherReferences = otherOperation.getFlushRelevantEntityReferences();

        if(currentOperation.getOperationType() == INSERT) {


          // if we reference the other entity, we need to be inserted after that entity
          if(currentReferences != null && currentReferences.contains(otherEntity.getId())) {
            moveTo = k;
            break; // we can only reference a single entity
          }

        } else { // UPDATE or DELETE

          // if the other entity has a reference to us, we must be placed after the other entity
          if(otherReferences != null && otherReferences.contains(currentEntity.getId())) {
            moveTo = k;
            // cannot break, there may be another entity further to the right which also references us
          }

        }
      }

      if(moveTo > i) {
        opList.remove(i);
        opList.add(moveTo, currentOperation);
        i--;
      }
    }

    return opList;
  }

  protected void determineDependencies(List flush) {
    TreeSet defaultValue = new TreeSet();
    for (DbOperation operation : flush) {
      if (operation instanceof DbEntityOperation) {
        DbEntity entity = ((DbEntityOperation) operation).getEntity();
        if (entity instanceof HasDbReferences) {
          Map dependentEntities = ((HasDbReferences) entity).getDependentEntities();

          if (dependentEntities != null) {
            dependentEntities.forEach((id, type) -> {

              deletes.getOrDefault(type, defaultValue).forEach(o -> {
                if (id.equals(o.getEntity().getId())) {
                  o.setDependency(operation);
                }
              });
            });
          }

        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy