| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 09/12/2018 - Will Dazey |
| // - 391279: Add support for Unidirectional OneToMany mappings with non-nullable values |
| package org.eclipse.persistence.internal.sessions; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.IdentityHashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeMap; |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.exceptions.DatabaseException; |
| import org.eclipse.persistence.exceptions.OptimisticLockException; |
| import org.eclipse.persistence.internal.databaseaccess.DatasourceCall; |
| import org.eclipse.persistence.internal.helper.DatabaseTable; |
| import org.eclipse.persistence.internal.helper.DescriptorCompare; |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.internal.localization.ToStringLocalization; |
| import org.eclipse.persistence.internal.queries.DatabaseQueryMechanism; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.queries.DeleteObjectQuery; |
| import org.eclipse.persistence.queries.InsertObjectQuery; |
| import org.eclipse.persistence.queries.UpdateObjectQuery; |
| import org.eclipse.persistence.queries.WriteObjectQuery; |
| import org.eclipse.persistence.sessions.UnitOfWork.CommitOrderType; |
| |
| /** |
| * This class maintains a commit stack and resolves circular references. |
| */ |
| public class CommitManager { |
| /** Order based on mapping foreign key constraints on how to insert objects by class. */ |
| protected List<Class<?>> commitOrder; |
| |
| /** |
| * This tracks the commit state for the objects, PENDING, PRE, POST, COMPLETE. |
| * The key is the object and the value is the state. |
| */ |
| protected Map<Object, Integer> commitState; |
| |
| /** The commit is in progress, but the row has not been written. */ |
| protected static final Integer PRE = 1; |
| /** The commit is in progress, and the row has been written. */ |
| protected static final Integer POST = 2; |
| /** The commit is complete for the object. */ |
| protected static final Integer COMPLETE = 3; |
| /** This object should be ignored. */ |
| protected static final Integer IGNORE = 4; |
| |
| /** Set of objects that had partial row written to resolve constraints. */ |
| protected Map shallowCommits; |
| |
| protected AbstractSession session; |
| |
| /** The commit manager is active while writing a set of objects (UOW), it is not active when writing a single object (DB session). */ |
| protected boolean isActive; |
| |
| /** Map of modification events used to defer insertion into m-m, dc, join tables. */ |
| protected Map<DatabaseMapping, List<Object[]>> dataModifications; |
| |
| /** |
| * Map of deferred calls groups by their table. |
| * This is used to defer multiple table writes for batching and deadlock avoidance. |
| */ |
| protected Map<DatabaseTable, List<Object[]>> deferredCalls; |
| |
| /** List of orphaned objects pending deletion. */ |
| protected List objectsToDelete; |
| |
| /** Counter used to keep track of commit depth for non-UOW writes. */ |
| protected int commitDepth; |
| |
| /** |
| * Create the commit manager on the session. |
| * It must be initialized later on after the descriptors have been added. |
| */ |
| public CommitManager(AbstractSession session) { |
| this.session = session; |
| } |
| |
| /** |
| * Add the data query to be performed at the end of the commit. |
| * This is done to decrease dependencies and avoid deadlock. |
| */ |
| public void addDataModificationEvent(DatabaseMapping mapping, Object[] event) { |
| if (!getDataModifications().containsKey(mapping)) { |
| this.dataModifications.put(mapping, new ArrayList()); |
| } |
| this.dataModifications.get(mapping).add(event); |
| } |
| |
| /** |
| * Add the data query to be performed at the end of the commit. |
| * This is done to decrease dependencies and avoid deadlock. |
| */ |
| public void addDeferredCall(DatabaseTable table, DatasourceCall call, DatabaseQueryMechanism mechanism) { |
| if (!getDeferredCalls().containsKey(table)) { |
| this.deferredCalls.put(table, new ArrayList()); |
| } |
| Object[] arguments = new Object[2]; |
| arguments[0] = call; |
| arguments[1] = mechanism; |
| this.deferredCalls.get(table).add(arguments); |
| } |
| |
| /** |
| * Deletion are cached until the end. |
| */ |
| public void addObjectToDelete(Object objectToDelete) { |
| getObjectsToDelete().add(objectToDelete); |
| } |
| |
| /** |
| * Commit all of the objects as a single transaction. |
| * This should commit the object in the correct order to maintain referential integrity. |
| */ |
| public void commitAllObjectsWithChangeSet(UnitOfWorkChangeSet uowChangeSet) throws RuntimeException, DatabaseException, OptimisticLockException { |
| reinitialize(); |
| this.isActive = true; |
| this.session.beginTransaction(); |
| try { |
| // PERF: if the number of classes in the project is large this loop can be a perf issue. |
| // If only one class types changed, then avoid loop. |
| if ((uowChangeSet.getObjectChanges().size() + uowChangeSet.getNewObjectChangeSets().size()) <= 1) { |
| Iterator<Class<?>> classes = uowChangeSet.getNewObjectChangeSets().keySet().iterator(); |
| if (classes.hasNext()) { |
| Class<?> theClass = classes.next(); |
| commitNewObjectsForClassWithChangeSet(uowChangeSet, theClass); |
| } |
| classes = uowChangeSet.getObjectChanges().keySet().iterator(); |
| if (classes.hasNext()) { |
| Class<?> theClass = classes.next(); |
| commitChangedObjectsForClassWithChangeSet(uowChangeSet, theClass); |
| } |
| } else { |
| // The commit order is all of the classes ordered by dependencies, this is done for deadlock avoidance. |
| List<Class<?>> commitOrder = getCommitOrder(); |
| int size = commitOrder.size(); |
| for (int index = 0; index < size; index++) { |
| Class<?> theClass = commitOrder.get(index); |
| commitAllObjectsForClassWithChangeSet(uowChangeSet, theClass); |
| } |
| } |
| |
| if (hasDeferredCalls()) { |
| // Perform all batched up calls, done to avoid dependencies. |
| for (List<Object[]> calls: this.deferredCalls.values()) { |
| for (Object[] argument : calls) { |
| ((DatabaseQueryMechanism)argument[1]).executeDeferredCall((DatasourceCall)argument[0]); |
| } |
| } |
| } |
| |
| if (hasDataModifications()) { |
| // Perform all batched up data modifications, done to avoid dependencies. |
| for (Map.Entry<DatabaseMapping, List<Object[]>> entry: this.dataModifications.entrySet()) { |
| List<Object[]> events = entry.getValue(); |
| int size = events.size(); |
| DatabaseMapping mapping = entry.getKey(); |
| for (int index = 0; index < size; index++) { |
| Object[] event = events.get(index); |
| mapping.performDataModificationEvent(event, getSession()); |
| } |
| } |
| } |
| |
| if (hasObjectsToDelete()) { |
| // These are orphaned objects, to be deleted from private ownership updates. |
| // TODO: These should be added to the unit of work deleted so they are deleted in the correct order. |
| List objects = getObjectsToDelete(); |
| int size = objects.size(); |
| reinitialize(); |
| for (int index = 0; index < size; index++) { |
| this.session.deleteObject(objects.get(index)); |
| } |
| } |
| |
| this.session.commitTransaction(); |
| } catch (RuntimeException exception) { |
| this.session.rollbackTransaction(); |
| throw exception; |
| } finally { |
| reinitialize(); |
| this.isActive = false; |
| } |
| } |
| |
| /** |
| * Commit all of the objects of the class type in the change set. |
| * This allows for the order of the classes to be processed optimally. |
| */ |
| protected void commitAllObjectsForClassWithChangeSet(UnitOfWorkChangeSet uowChangeSet, Class<?> theClass) { |
| // Although new objects should be first, there is an issue that new objects get added to non-new after the insert, |
| // so the object would be written twice. |
| commitChangedObjectsForClassWithChangeSet(uowChangeSet, theClass); |
| commitNewObjectsForClassWithChangeSet(uowChangeSet, theClass); |
| } |
| |
| /** |
| * Commit all of the objects of the class type in the change set. |
| * This allows for the order of the classes to be processed optimally. |
| */ |
| protected void commitNewObjectsForClassWithChangeSet(UnitOfWorkChangeSet uowChangeSet, Class<?> theClass) { |
| Map<ObjectChangeSet, ObjectChangeSet> newObjectChangesList = uowChangeSet.getNewObjectChangeSets().get(theClass); |
| if (newObjectChangesList != null) { // may be no changes for that class type. |
| AbstractSession session = getSession(); |
| ClassDescriptor descriptor = session.getDescriptor(theClass); |
| List<ObjectChangeSet> newChangeSets = new ArrayList(newObjectChangesList.values()); |
| int size = newChangeSets.size(); |
| for (int index = 0; index < size; index++) { |
| ObjectChangeSet changeSetToWrite = newChangeSets.get(index); |
| Object objectToWrite = changeSetToWrite.getUnitOfWorkClone(); |
| if (!isProcessedCommit(objectToWrite)) { |
| // PERF: Get the descriptor query, to avoid extra query creation. |
| InsertObjectQuery commitQuery = descriptor.getQueryManager().getInsertQuery(); |
| if (commitQuery == null) { |
| commitQuery = new InsertObjectQuery(); |
| commitQuery.setDescriptor(descriptor); |
| } else { |
| // Ensure original query has been prepared. |
| commitQuery.checkPrepare(session, commitQuery.getTranslationRow()); |
| commitQuery = (InsertObjectQuery)commitQuery.clone(); |
| } |
| commitQuery.setIsExecutionClone(true); |
| commitQuery.setObjectChangeSet(changeSetToWrite); |
| commitQuery.setObject(objectToWrite); |
| commitQuery.cascadeOnlyDependentParts(); |
| commitQuery.setModifyRow(null); |
| session.executeQuery(commitQuery); |
| } |
| uowChangeSet.putNewObjectInChangesList(changeSetToWrite, session); |
| } |
| } |
| } |
| |
| /** |
| * Commit changed of the objects of the class type in the change set. |
| * This allows for the order of the classes to be processed optimally. |
| */ |
| protected void commitChangedObjectsForClassWithChangeSet(UnitOfWorkChangeSet uowChangeSet, Class<?> theClass) { |
| Map<ObjectChangeSet, ObjectChangeSet> objectChangesList = uowChangeSet.getObjectChanges().get(theClass); |
| if (objectChangesList != null) {// may be no changes for that class type. |
| ClassDescriptor descriptor = null; |
| AbstractSession session = getSession(); |
| Collection<ObjectChangeSet> changes = objectChangesList.values(); |
| CommitOrderType order = ((UnitOfWorkImpl)session).getCommitOrder(); |
| if (order != CommitOrderType.NONE) { |
| changes = new ArrayList(objectChangesList.values()); |
| if (order == CommitOrderType.CHANGES) { |
| Collections.sort((List)changes, new ObjectChangeSet.ObjectChangeSetComparator()); |
| } else { |
| Collections.sort((List)changes); |
| } |
| } |
| for (ObjectChangeSet changeSetToWrite : changes) { |
| Object objectToWrite = changeSetToWrite.getUnitOfWorkClone(); |
| if (descriptor == null) { |
| descriptor = session.getDescriptor(objectToWrite); |
| } |
| if (!isProcessedCommit(objectToWrite)) { |
| // Commit and resume on failure can cause a new change set to be in existing, so need to check here. |
| WriteObjectQuery commitQuery = null; |
| if (changeSetToWrite.isNew()) { |
| commitQuery = new InsertObjectQuery(); |
| } else { |
| commitQuery = new UpdateObjectQuery(); |
| } |
| commitQuery.setIsExecutionClone(true); |
| commitQuery.setDescriptor(descriptor); |
| commitQuery.setObjectChangeSet(changeSetToWrite); |
| commitQuery.setObject(objectToWrite); |
| commitQuery.cascadeOnlyDependentParts(); |
| // removed checking session type to set cascade level |
| // will always be a unitOfWork so we need to cascade dependent parts |
| session.executeQuery(commitQuery); |
| } |
| } |
| } |
| } |
| |
| /** |
| * delete all of the objects as a single transaction. |
| * This should delete the object in the correct order to maintain referential integrity. |
| */ |
| public void deleteAllObjects(List objects) throws RuntimeException, DatabaseException, OptimisticLockException { |
| this.isActive = true; |
| AbstractSession session = getSession(); |
| session.beginTransaction(); |
| |
| try { |
| // PERF: Optimize single object case. |
| if (objects.size() == 1) { |
| deleteAllObjects(objects.get(0).getClass(), objects, session); |
| } else { |
| List<Class<?>> commitOrder = getCommitOrder(); |
| for (int orderIndex = commitOrder.size() - 1; orderIndex >= 0; orderIndex--) { |
| Class<?> theClass = commitOrder.get(orderIndex); |
| deleteAllObjects(theClass, objects, session); |
| } |
| } |
| |
| session.commitTransaction(); |
| } catch (RuntimeException exception) { |
| try { |
| session.rollbackTransaction(); |
| } catch (Exception ignore) { |
| } |
| throw exception; |
| } finally { |
| reinitialize(); |
| this.isActive = false; |
| } |
| } |
| |
| /** |
| * Delete all of the objects with the matching class. |
| */ |
| public void deleteAllObjects(Class<?> theClass, List objects, AbstractSession session) { |
| ClassDescriptor descriptor = null; |
| |
| if (((UnitOfWorkImpl)session).getCommitOrder() != CommitOrderType.NONE) {// bug 331064 - Sort the delete order |
| objects = sort(theClass, objects); |
| } |
| |
| int size = objects.size(); |
| for (int index = 0; index < size; index++) { |
| Object objectToDelete = objects.get(index); |
| if (objectToDelete.getClass() == theClass) { |
| if (descriptor == null) { |
| descriptor = session.getDescriptor(theClass); |
| } |
| // PERF: Get the descriptor query, to avoid extra query creation. |
| DeleteObjectQuery deleteQuery = descriptor.getQueryManager().getDeleteQuery(); |
| if (deleteQuery == null) { |
| deleteQuery = new DeleteObjectQuery(); |
| deleteQuery.setDescriptor(descriptor); |
| } else { |
| // Ensure original query has been prepared. |
| deleteQuery.checkPrepare(session, deleteQuery.getTranslationRow()); |
| deleteQuery = (DeleteObjectQuery)deleteQuery.clone(); |
| } |
| deleteQuery.setIsExecutionClone(true); |
| deleteQuery.setObject(objectToDelete); |
| session.executeQuery(deleteQuery); |
| } |
| } |
| } |
| |
| /** |
| * Sort the objects based on PK. |
| */ |
| // bug 331064 - Sort the delete order based on PKs. |
| private List sort (Class<?> theClass, List objects) { |
| ClassDescriptor descriptor = session.getDescriptor(theClass); |
| org.eclipse.persistence.internal.descriptors.ObjectBuilder objectBuilder = descriptor.getObjectBuilder(); |
| int size = objects.size(); |
| TreeMap sortedObjects = new TreeMap(); |
| for (int index = 0; index < size; index++) { |
| Object objectToDelete = objects.get(index); |
| if (objectToDelete.getClass() == theClass) { |
| sortedObjects.put(objectBuilder.extractPrimaryKeyFromObject(objectToDelete, session), objectToDelete); |
| } |
| } |
| return new ArrayList(sortedObjects.values()); |
| } |
| |
| /** |
| * Return the order in which objects should be committed to the database. |
| * This order is based on ownership in the descriptors and is require for referential integrity. |
| * The commit order is a vector of vectors, |
| * where the first vector is all root level classes, the second is classes owned by roots and so on. |
| */ |
| public List<Class<?>> getCommitOrder() { |
| if (this.commitOrder == null) { |
| this.commitOrder = new ArrayList(); |
| } |
| return this.commitOrder; |
| } |
| |
| /** |
| * Return the map of states of the objects being committed. |
| * The states are defined as static Integers (PENDING, PRE, POST, COMPLETE). |
| */ |
| protected Map<Object, Integer> getCommitState() { |
| if (this.commitState == null) { |
| // 2612538 - the default size of Map (32) is appropriate |
| this.commitState = new IdentityHashMap(); |
| } |
| return this.commitState; |
| } |
| |
| protected boolean hasDataModifications() { |
| return ((this.dataModifications != null) && (!this.dataModifications.isEmpty())); |
| } |
| |
| /** |
| * Used to store data queries to be performed at the end of the commit. |
| * This is done to decrease dependencies and avoid deadlock. |
| */ |
| protected Map<DatabaseMapping, List<Object[]>> getDataModifications() { |
| if (dataModifications == null) { |
| dataModifications = new LinkedHashMap(); |
| } |
| return dataModifications; |
| } |
| |
| protected boolean hasDeferredCalls() { |
| return ((this.deferredCalls != null) && (!this.deferredCalls.isEmpty())); |
| } |
| |
| /** |
| * Used to store calls to be performed at the end of the commit. |
| * This is done for multiple table descriptors to allow batching and avoid deadlock. |
| */ |
| protected Map<DatabaseTable, List<Object[]>> getDeferredCalls() { |
| if (this.deferredCalls == null) { |
| this.deferredCalls = new LinkedHashMap(); |
| } |
| return this.deferredCalls; |
| } |
| |
| protected boolean hasObjectsToDelete() { |
| return ((objectsToDelete != null) && (!objectsToDelete.isEmpty())); |
| } |
| |
| /** |
| * Deletion are cached until the end. |
| */ |
| public List getObjectsToDelete() { |
| if (objectsToDelete == null) { |
| objectsToDelete = new ArrayList(); |
| } |
| return objectsToDelete; |
| } |
| |
| /** |
| * Return the session that this is managing commits for. |
| */ |
| protected AbstractSession getSession() { |
| return this.session; |
| } |
| |
| /** |
| * Return any objects that have been shallow committed during this commit process. |
| */ |
| protected Map getShallowCommits() { |
| if (this.shallowCommits == null) { |
| // 2612538 - the default size of Map (32) is appropriate |
| this.shallowCommits = new IdentityHashMap(); |
| } |
| return this.shallowCommits; |
| } |
| |
| /** |
| * Reset the commit order from the session's descriptors. |
| * This uses the constraint dependencies in the descriptor's mappings, |
| * to decide which descriptors are dependent on which other descriptors. |
| * Multiple computations of the commit order should produce the same ordering. |
| * This is done to improve performance on unit of work writes through decreasing the |
| * stack size, and acts as a deadlock avoidance mechanism. |
| */ |
| public void initializeCommitOrder() { |
| Vector<ClassDescriptor> descriptors = Helper.buildVectorFromMapElements(getSession().getDescriptors()); |
| |
| // Must ensure uniqueness, some descriptor my be register twice for interfaces. |
| descriptors = Helper.addAllUniqueToVector(new Vector<>(descriptors.size()), descriptors); |
| ClassDescriptor[] descriptorsArray = new ClassDescriptor[descriptors.size()]; |
| for (int index = 0; index < descriptors.size(); index++) { |
| descriptorsArray[index] = descriptors.elementAt(index); |
| } |
| Arrays.sort(descriptorsArray, new DescriptorCompare()); |
| descriptors = new Vector<>(descriptors.size()); |
| for (int index = 0; index < descriptorsArray.length; index++) { |
| descriptors.addElement(descriptorsArray[index]); |
| } |
| |
| CommitOrderCalculator calculator = new CommitOrderCalculator(getSession()); |
| calculator.addNodes(descriptors); |
| calculator.calculateMappingDependencies(); |
| calculator.orderCommits(); |
| descriptors = calculator.getOrderedDescriptors(); |
| |
| calculator = new CommitOrderCalculator(getSession()); |
| calculator.addNodes(descriptors); |
| calculator.calculateSpecifiedDependencies(); |
| calculator.orderCommits(); |
| |
| setCommitOrder(calculator.getOrderedClasses()); |
| } |
| |
| /** |
| * Return if the commit manager is active. |
| */ |
| public boolean isActive() { |
| return isActive; |
| } |
| |
| /** |
| * Return if the object has been processed. |
| * This should be called by any query that is writing an object, |
| * if true the query should not write the object. |
| */ |
| public boolean isProcessedCommit(Object object) { |
| return getCommitState().get(object) != null; |
| } |
| |
| /** |
| * Return if the object has been committed. |
| * This should be called by any query that is writing an object, |
| * if true the query should not write the object. |
| */ |
| public boolean isCommitCompleted(Object object) { |
| return getCommitState().get(object) == COMPLETE; |
| } |
| |
| /** |
| * Return if the object has been committed. |
| * This should be called by any query that is writing an object, |
| * if true the query should not write the object. |
| */ |
| public boolean isCommitCompletedInPostOrIgnore(Object object) { |
| Integer state = getCommitState().get(object); |
| return (state == COMPLETE) || (state == POST) || (state == IGNORE); |
| } |
| |
| /** |
| * Return if the object is being in progress of being post modify commit. |
| * This should be called by any query that is writing an object. |
| */ |
| public boolean isCommitInPostModify(Object object) { |
| return getCommitState().get(object) == POST; |
| } |
| |
| /** |
| * Return if the object is being in progress of being pre modify commit. |
| * This should be called by any query that is writing an object, |
| * if true the query must force a shallow insert of the object if it is new. |
| */ |
| public boolean isCommitInPreModify(Object objectOrChangeSet) { |
| return getCommitState().get(objectOrChangeSet) == PRE; |
| } |
| |
| /** |
| * Return if the object is shallow committed. |
| * This is required to resolve bidirectional references. |
| */ |
| public boolean isShallowCommitted(Object object) { |
| if (this.shallowCommits == null) { |
| return false; |
| } |
| return this.shallowCommits.containsKey(object); |
| } |
| |
| /** |
| * Mark the commit of the object as being fully completed. |
| * This should be called by any query that has finished writing an object. |
| */ |
| public void markCommitCompleted(Object object) { |
| this.commitDepth --; |
| getCommitState().put(object, COMPLETE); |
| // If not in a unit of work commit and the commit of this object is done reset the commit manager. |
| if ((!this.isActive) && (this.commitDepth == 0)) { |
| reinitialize(); |
| return; |
| } |
| } |
| |
| public void markIgnoreCommit(Object object){ |
| getCommitState().put(object, IGNORE); |
| } |
| |
| /** |
| * Add an object as being in progress of being committed. |
| * This should be called by any query that is writing an object. |
| */ |
| public void markPostModifyCommitInProgress(Object object) { |
| getCommitState().put(object, POST); |
| } |
| |
| /** |
| * Add an object as being in progress of being committed. |
| * This should be called by any query that is writing an object. |
| */ |
| public void markPreModifyCommitInProgress(Object object) { |
| this.commitDepth ++; |
| getCommitState().put(object, PRE); |
| } |
| |
| /** |
| * Mark the object as shallow committed. |
| * This is required to resolve bidirectional references. |
| */ |
| public void markShallowCommit(Object object) { |
| getShallowCommits().put(object, object); // Use as set. |
| } |
| |
| /** |
| * Reset the commits. |
| * This must be done before a new commit process is begun. |
| */ |
| public void reinitialize() { |
| this.commitState = null; |
| this.commitDepth = 0; |
| this.shallowCommits = null; |
| this.objectsToDelete = null; |
| this.dataModifications = null; |
| this.deferredCalls = null; |
| } |
| |
| /** |
| * Set the order in which objects should be committed to the database. |
| * This order is based on ownership in the descriptors and is require for referential integrity. |
| * The commit order is a vector of vectors, |
| * where the first vector is all root level classes, the second is classes owned by roots and so on. |
| */ |
| public void setCommitOrder(List commitOrder) { |
| this.commitOrder = commitOrder; |
| } |
| |
| /** |
| * Used to store data queries to be performed at the end of the commit. |
| * This is done to decrease dependencies and avoid deadlock. |
| */ |
| protected void setDataModifications(Map<DatabaseMapping, List<Object[]>> dataModifications) { |
| this.dataModifications = dataModifications; |
| } |
| |
| /** |
| * Set if the commit manager is active. |
| */ |
| public void setIsActive(boolean isActive) { |
| this.isActive = isActive; |
| } |
| |
| /** |
| * Deletion are cached until the end. |
| */ |
| protected void setObjectsToDelete(List objectsToDelete) { |
| this.objectsToDelete = objectsToDelete; |
| } |
| |
| /** |
| * Set the session that this is managing commits for. |
| */ |
| protected void setSession(AbstractSession session) { |
| this.session = session; |
| } |
| |
| /** |
| * Set any objects that have been shallow committed during this commit process. |
| */ |
| protected void setShallowCommits(Map shallowCommits) { |
| this.shallowCommits = shallowCommits; |
| } |
| |
| /** |
| * Print the in progress depth. |
| */ |
| @Override |
| public String toString() { |
| Object[] args = {this.commitDepth}; |
| return Helper.getShortClassName(getClass()) + ToStringLocalization.buildMessage("commit_depth", args); |
| } |
| } |