org.eclipse.persistence.internal.sessions.remote.RemoteUnitOfWork Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.persistence.core Show documentation
Show all versions of org.eclipse.persistence.core Show documentation
EclipseLink build based upon Git transaction ecdf3c32c4
/*
* Copyright (c) 1998, 2018 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
// 07/15/2011-2.2.1 Guy Pelletier
// - 349424: persists during an preCalculateUnitOfWorkChangeSet event are lost
package org.eclipse.persistence.internal.sessions.remote;
import java.util.*;
import org.eclipse.persistence.config.ReferenceMode;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorQueryManager;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.internal.sessions.*;
import org.eclipse.persistence.internal.databaseaccess.Platform;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.platform.database.DatabasePlatform;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.sessions.SessionProfiler;
import org.eclipse.persistence.sessions.remote.*;
import org.eclipse.persistence.logging.SessionLog;
/**
* Counter part of the unit of work which exists on the client side.
*/
public class RemoteUnitOfWork extends RepeatableWriteUnitOfWork {
protected List newObjectsCache;
protected List unregisteredNewObjectsCache;
protected boolean isOnClient;
protected transient RemoteSessionController parentSessionController;
protected boolean isFlush;
public RemoteUnitOfWork() {
}
public RemoteUnitOfWork(RemoteUnitOfWork parent) {
this(parent, null);
}
public RemoteUnitOfWork(DistributedSession parent) {
this(parent, null);
}
public RemoteUnitOfWork(RemoteUnitOfWork parent, ReferenceMode referenceMode) {
super(parent, referenceMode);
this.isOnClient = true;
this.discoverUnregisteredNewObjectsWithoutPersist = true;
}
public RemoteUnitOfWork(DistributedSession parent, ReferenceMode referenceMode) {
super(parent, referenceMode);
this.isOnClient = true;
this.discoverUnregisteredNewObjectsWithoutPersist = true;
}
public boolean isFlush() {
return isFlush;
}
public void setIsFlush(boolean isFlush) {
this.isFlush = isFlush;
}
/**
* PUBLIC:
* Tell the unit of work to begin a transaction now.
* By default the unit of work will begin a transaction at commit time.
* The default is the recommended approach, however sometimes it is
* necessary to start the transaction before commit time. When the
* unit of work commits, this transaction will be committed.
*
* @see #commit()
* @see #release()
*/
public void beginEarlyTransaction() throws DatabaseException {
// Acquire the mutex so session knows it is in a transaction.
getParent().getTransactionMutex().acquire();
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
// This needs a special call for remote, to ensure subsequent queries isolate their data to a unit of work on the server.
((DistributedSession)getParent()).getRemoteConnection().beginEarlyTransaction();
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
setWasTransactionBegunPrematurely(true);
}
/**
* The nested unit of work must also be remote.
*/
@Override
public UnitOfWorkImpl acquireUnitOfWork() {
return acquireUnitOfWork(null);
}
/**
* The nested unit of work must also be remote.
*/
@Override
public UnitOfWorkImpl acquireUnitOfWork(ReferenceMode referenceMode) {
log(SessionLog.FINER, SessionLog.TRANSACTION, "acquire_unit_of_work");
setNumberOfActiveUnitsOfWork(getNumberOfActiveUnitsOfWork() + 1);
RemoteUnitOfWork ruow = new RemoteUnitOfWork(this, referenceMode);
ruow.discoverAllUnregisteredNewObjectsInParent();
return ruow;
}
/**
* This is done to maintain correspondence between local new objects and returned new objects from serialization.
* Object correspondence is maintained by comparing primary keys but for new objects it is possible that primary
* key value is null as it is still not inserted. The returned new objects from serialization will have primary
* key value which will be inserted into corresponding local new objects.
*/
protected List collectNewObjects() {
if ((this.newObjectsCloneToOriginal == null) || this.newObjectsCloneToOriginal.isEmpty()) {
return null;
}
List newObjects = new ArrayList(this.newObjectsCloneToOriginal.size());
for (Object newObject : this.newObjectsCloneToOriginal.keySet()) {
newObjects.add(newObject);
}
return newObjects;
}
/**
* This is done to maintain correspondence between local unregistered new objects and returned unregistered new
* objects from serialization. Object correspondence is maintained by comparing primary keys but for unregistered
* new objects it is possible that primary key value is null as it is still not inserted. The returned unregistered
* new objects from serialization will have primary key value which will be inserted into corresponding local new
* objects.
*/
protected List collectUnregisteredNewObjects() {
discoverAllUnregisteredNewObjects();
return new ArrayList(getUnregisteredNewObjects().values());
}
/**
* The remote unit of work returned after its commit on the server is merged with remote unit of work
* on the remote side.
*/
protected void commitIntoRemoteUnitOfWork() {
UnitOfWorkImpl parent = ((UnitOfWorkImpl)getParent());
// Must merge the transaction flag.
parent.setWasTransactionBegunPrematurely(wasTransactionBegunPrematurely());
MergeManager manager = new MergeManager(this);
manager.mergeWorkingCopyIntoRemote();
// Must clone the clone mapping because entries can be added to it during the merging,
// and that can lead to concurrency problems.
Iterator cloneIterator = new IdentityHashMap(getCloneMapping()).keySet().iterator();
Map clones = new IdentityHashMap(this.cloneMapping.size());
// Iterate over each clone and let the object build merge to clones into the originals.
while (cloneIterator.hasNext()) {
Object remoteClone = cloneIterator.next();
manager.mergeChanges(remoteClone, null, this);
Object clone = manager.getTargetVersionOfSourceObject(remoteClone, parent.getDescriptor(remoteClone), parent);
clones.put(remoteClone, clone);
}
// Reset the remote change set to be the local one, need to reset all clones to local copy,
// and reset transient variables.
parent.setUnitOfWorkChangeSet(this.unitOfWorkChangeSet);
fixRemoteChangeSet(this.unitOfWorkChangeSet, clones, parent);
((RemoteUnitOfWork)parent).setCumulativeUOWChangeSet(this.cumulativeUOWChangeSet);
fixRemoteChangeSet(this.cumulativeUOWChangeSet, clones, parent);
// Set the deleted objects into the parent.
if (this.objectsDeletedDuringCommit != null) {
Map newDeletedObjects = new IdentityHashMap();
for (Object deletedObject : this.objectsDeletedDuringCommit.keySet()) {
Object primaryKey = getId(deletedObject);
Object clone = clones.get(deletedObject);
if (clone == null) {
clone = parent.getIdentityMapAccessor().getFromIdentityMap(primaryKey, deletedObject.getClass());
if (clone == null) {
clone = deletedObject;
}
}
newDeletedObjects.put(clone, primaryKey);
parent.getIdentityMapAccessor().removeFromIdentityMap(primaryKey, clone.getClass());
}
parent.setObjectsDeletedDuringCommit(newDeletedObjects);
}
}
/**
* Simulate a flush, current just begins a transaction and commits.
*/
@Override
public void writeChanges() {
if (!isOnClient()) {
super.writeChanges();
return;
}
// Check for a nested flush and return early if we are in one
if (this.isWithinFlush()) {
log(SessionLog.WARNING, SessionLog.TRANSACTION, "nested_entity_manager_flush_not_executed_pre_query_changes_may_be_pending", getClass().getSimpleName());
return;
}
log(SessionLog.FINER, SessionLog.TRANSACTION, "begin_unit_of_work_flush");
// PERF: If this is an empty unit of work, do nothing (but still may need to commit SQL changes).
boolean hasChanges = (this.unitOfWorkChangeSet != null) || hasCloneMapping() || hasDeletedObjects() || hasModifyAllQueries() || hasDeferredModifyAllQueries();
if (hasChanges) {
// The change set may already exist if using change tracking.
if (this.unitOfWorkChangeSet == null) {
this.unitOfWorkChangeSet = new UnitOfWorkChangeSet(this);
}
calculateChanges(getCloneMapping(), this.unitOfWorkChangeSet, true, true);
hasChanges = hasModifications();
}
if (!hasChanges) {
log(SessionLog.FINER, SessionLog.TRANSACTION, "end_unit_of_work_flush");
return;
}
if (!wasTransactionBegunPrematurely()) {
beginEarlyTransaction();
}
// New objects cache is created to maintain the correspondence when they are returned back as different copy
setNewObjectsCache(collectNewObjects());
// Unregistered new objects cache is created to maintain the correspondence when they are returned back as different copy
setUnregisteredNewObjectsCache(collectUnregisteredNewObjects());
// Commit on the server
RemoteUnitOfWork remoteUnitOfWork;
try {
setIsFlush(true);
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
remoteUnitOfWork = ((DistributedSession)getParent()).getRemoteConnection().commitRootUnitOfWork(this);
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
} finally {
setIsFlush(false);
}
// Make the returned remote unit of work a nested unit of work and merge it with the local remote unit of work
remoteUnitOfWork.setParent(this);
remoteUnitOfWork.setProject(getProject());
remoteUnitOfWork.prepareForMergeIntoRemoteUnitOfWork();
remoteUnitOfWork.commitIntoRemoteUnitOfWork();
log(SessionLog.FINER, SessionLog.TRANSACTION, "end_unit_of_work_flush");
resumeUnitOfWork();
log(SessionLog.FINER, SessionLog.TRANSACTION, "resume_unit_of_work");
}
/**
* Starts committing the remote unit of work.
* This must serialize the unit of work across to the server,
* commit the unit of work on the server,
* serialize it back and merge any server-side changes (such as sequence numbers) it into itself,
* then merge into the parent remote session.
*/
@Override
public void commitRootUnitOfWork() {
if (!isOnClient()) {
if (isSynchronized()) {
// If we started the JTS transaction then we have to commit it as well.
if (getParent().wasJTSTransactionInternallyStarted()) {
commitInternallyStartedExternalTransaction();
}
// Do not commit until the JTS wants to.
return;
}
if (this.eventManager != null) {
this.eventManager.preCommitUnitOfWork();
}
super.commitRootUnitOfWork(); // On the server the normal commit is done.
if (this.eventManager != null) {
this.eventManager.postCommitUnitOfWork();
}
return;
}
// PERF: If this is an empty unit of work, do nothing (but still may need to commit SQL changes).
boolean hasChanges = (this.unitOfWorkChangeSet != null) || hasCloneMapping() || hasDeletedObjects() || hasModifyAllQueries() || hasDeferredModifyAllQueries();
if (hasChanges) {
// The change set may already exist if using change tracking.
if (this.unitOfWorkChangeSet == null) {
this.unitOfWorkChangeSet = new UnitOfWorkChangeSet(this);
}
calculateChanges(getCloneMapping(), this.unitOfWorkChangeSet, true, true);
hasChanges = hasModifications();
}
if (!hasChanges && (this.cumulativeUOWChangeSet == null) && (this.classesToBeInvalidated == null)) {
// If no changes, avoid the remote commit, just return.
// CR#... need to commit the transaction if begun early.
if (wasTransactionBegunPrematurely()) {
// Must be set to false for release to know not to rollback.
setWasTransactionBegunPrematurely(false);
setWasNonObjectLevelModifyQueryExecuted(false);
try {
commitTransaction();
} catch (RuntimeException commitFailed) {
try {
rollbackTransaction();
} catch (RuntimeException ignore) {
// Ignore
}
throw commitFailed;
} catch (Error error) {
try {
rollbackTransaction();
} catch (RuntimeException ignore) {
// Ignore
}
throw error;
}
}
return;
}
// New objects cache is created to maintain the correspondence when they are returned back as different copy
setNewObjectsCache(collectNewObjects());
// Unregistered new objects cache is created to maintain the correspondence when they are returned back as different copy
setUnregisteredNewObjectsCache(collectUnregisteredNewObjects());
// Commit on the server
RemoteUnitOfWork remoteUnitOfWork;
try {
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
remoteUnitOfWork = ((DistributedSession)getParent()).getRemoteConnection().commitRootUnitOfWork(this);
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
} catch (RuntimeException exception) {
// Must ensure remote session transaction mutex is correct.
if (wasTransactionBegunPrematurely()) {
getParent().getTransactionMutex().release();
}
// If an exception occurred, the unit of work will have rolledback the early transaction
// so must record this incase the unit of work re-commits.
setWasTransactionBegunPrematurely(false);
throw exception;
}
// Must ensure remote session transaction mutex is correct.
if (wasTransactionBegunPrematurely()) {
setWasTransactionBegunPrematurely(false);
setWasNonObjectLevelModifyQueryExecuted(false);
getParent().getTransactionMutex().release();
}
// Make the returned remote unit of work a nested unit of work and merge it with the local remote unit of work
remoteUnitOfWork.setParent(this);
remoteUnitOfWork.setProject(getProject());
remoteUnitOfWork.prepareForMergeIntoRemoteUnitOfWork();
remoteUnitOfWork.commitIntoRemoteUnitOfWork();
// Now commit this unit of work to the parent remote session
commitRootUnitOfWorkOnClient();
}
/**
* INTERNAL:
* Changes are calculated on the client, so avoid recalculating them on the server.
*/
@Override
public UnitOfWorkChangeSet calculateChanges(Map registeredObjects, UnitOfWorkChangeSet changeSet, boolean assignSequences, boolean shouldCloneMap) {
if (!this.isOnClient) {
// The changes are calculated on the client, so don't do them again on the server.
return changeSet;
}
return super.calculateChanges(registeredObjects, changeSet, assignSequences, shouldCloneMap);
}
/**
* INTERNAL:
* Resume is not required on the server.
*/
@Override
public void resumeUnitOfWork() {
if (!this.isOnClient) {
// Avoid the resume on the server, only the client should resume.
return;
}
super.resumeUnitOfWork();
}
/**
* Merges remote unit of work to parent remote session.
*/
protected void commitRootUnitOfWorkOnClient() {
collectAndPrepareObjectsForNestedMerge();
//calculate the change set here as we have special behavior for remote
// in that the new changesets must be updated within the UOWChangeSet as the
// primary keys have already been assigned. This was modified for updating
// new object change set behavior.
UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)getUnitOfWorkChangeSet();
if (uowChangeSet == null) {
//may be using the old commit process usesOldCommit()
uowChangeSet = new UnitOfWorkChangeSet(this);
setUnitOfWorkChangeSet(uowChangeSet);
calculateChanges(getCloneMapping(), uowChangeSet, false, true);
this.allClones = null;
}
for (Map newList : uowChangeSet.getNewObjectChangeSets().values()) {
Iterator newChangeSets = new IdentityHashMap(newList).keySet().iterator();
while (newChangeSets.hasNext()) {
uowChangeSet.putNewObjectInChangesList((ObjectChangeSet)newChangeSets.next(), this);
}
}
//add the deleted objects
if (this.objectsDeletedDuringCommit != null) {
for (Object deletedObject : this.objectsDeletedDuringCommit.keySet()) {
uowChangeSet.addDeletedObject(deletedObject, this);
}
}
mergeChangesIntoParent();
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The named query can be defined on the remote session or the server-side session.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName) throws DatabaseException {
return executeQuery(queryName, new Vector(1));
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
* The query is executed on the server-side session.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass) throws DatabaseException {
return executeQuery(queryName, domainClass, new Vector(1));
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
* The class is the descriptor in which the query was pre-defined.
*
* @see DescriptorQueryManager#addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Class domainClass, Vector argumentValues) throws DatabaseException {
DistributedSession remoteSession = null;
if (getParent().isRemoteSession()) {
remoteSession = (DistributedSession)getParent();
} else {//must be remote unit of work
RemoteUnitOfWork uow = (RemoteUnitOfWork)getParent();
while (uow.getParent().isRemoteUnitOfWork()) {
uow = (RemoteUnitOfWork)uow.getParent();
}
remoteSession = (DistributedSession)uow.getParent();
}
startOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
Transporter transporter = remoteSession.getRemoteConnection().remoteExecuteNamedQuery(queryName, domainClass, argumentValues);
endOperationProfile(SessionProfiler.Remote, null, SessionProfiler.ALL);
transporter.getQuery().setSession(this);
return transporter.getQuery().extractRemoteResult(transporter);
}
/**
* PUBLIC:
* Execute the pre-defined query by name and return the result.
* Queries can be pre-defined and named to allow for their reuse.
*
* @see #addQuery(String, DatabaseQuery)
*/
@Override
public Object executeQuery(String queryName, Vector argumentValues) throws DatabaseException {
if (containsQuery(queryName)) {
return super.executeQuery(queryName, argumentValues);
}
return executeQuery(queryName, null, argumentValues);
}
/**
* Return the table descriptor specified for the class.
*/
@Override
public ClassDescriptor getDescriptor(Class domainClass) {
return getParent().getDescriptor(domainClass);
}
/**
* Return the table descriptor specified for the class.
*/
@Override
public ClassDescriptor getDescriptorForAlias(String alias) {
return getParent().getDescriptorForAlias(alias);
}
/**
* Returns a new object cache
*/
public List getNewObjectsCache() {
return newObjectsCache;
}
/**
* INTERNAL:
* Method returns the parent RemoteSessionController for this Remote UnitOfWork
* Used to retrieve Valueholders that were used on the client
*/
public RemoteSessionController getParentSessionController() {
return this.parentSessionController;
}
/**
* INTERNAL:
* Return the database platform currently connected to.
* The platform is used for database specific behavior.
*/
@Override
public DatabasePlatform getPlatform() {
return getParent().getPlatform();
}
/**
* INTERNAL:
* Return the database platform currently connected to.
* The platform is used for database specific behavior.
*/
@Override
public Platform getDatasourcePlatform() {
return getParent().getDatasourcePlatform();
}
/**
* Returns an unregistered new object cache
*/
public List getUnregisteredNewObjectsCache() {
return unregisteredNewObjectsCache;
}
/**
* INTERNAL:
* Return the results from exeucting the database query.
* the arguments should be a database row with raw data values.
*/
@Override
public Object internalExecuteQuery(DatabaseQuery query, AbstractRecord Record) throws DatabaseException, QueryException {
if (isOnClient()) {
//assert !getCommitManager().isActive();
// This will either throw an exception or do a logic only operation
// (i.e. mark for later deletion if a deleteObjet query).
boolean objectLevelRead = (query.isObjectLevelReadQuery() && !query.isReportQuery() && query.shouldMaintainCache());
if (objectLevelRead) {
ObjectLevelReadQuery readQuery = (ObjectLevelReadQuery)query;
if (isAfterWriteChangesButBeforeCommit()) {
throw ValidationException.illegalOperationForUnitOfWorkLifecycle(getLifecycle(), "executeQuery(ObjectLevelReadQuery)");
}
Object result = readQuery.checkEarlyReturn(this, Record);
if (result != null) {
if (result == InvalidObject.instance) {
return null;
}
return result;
}
// Must use the uow connection in these cases.
// can be certain that commit manager not active as on client.
if (readQuery.isLockQuery(this) && !wasTransactionBegunPrematurely()) {
beginEarlyTransaction();
}
} else if (query.isObjectLevelModifyQuery()) {
// Delete object queries must be processed locally.
return query.executeInUnitOfWork(this, Record);
}
// Starting a transaction early when on a remote UnitOfWork starts
// a transaction on the server side client session, and all queries
// when they arrive they will go down the write connection.
/* Fix to allow executing non-selecting SQL in a UnitOfWork. - RB */
if ((!getCommitManager().isActive()) && query.isModifyQuery()) {
if (!wasTransactionBegunPrematurely()) {
beginEarlyTransaction();
}
}
Object result = getParent().executeQuery(query, Record);
if (objectLevelRead) {
result = ((ObjectLevelReadQuery)query).registerResultInUnitOfWork(result, this, Record, false);
}
if (query.isModifyAllQuery()) {
storeModifyAllQuery(query);
}
return result;
}
return query.executeInUnitOfWork(this, Record);
}
protected boolean isOnClient() {
return isOnClient;
}
/**
* Return if this session is a unit of work.
*/
@Override
public boolean isRemoteUnitOfWork() {
return true;
}
/**
* The returned remote unit of work from the server is prepared to merge with local remote unit of work.
*/
protected void prepareForMergeIntoRemoteUnitOfWork() {
if (this.newObjectsCache == null) {
return;
}
int size = this.newObjectsCache.size();
if (size == 0) {
return;
}
Map originalToClone = new IdentityHashMap(size);
Map cloneToOriginal = new IdentityHashMap(size);
// For new and unregistered objects the clone from the parent remote unit of work is picked and store as original
// in the remote unit of work. This is done so that changes are merged into the clone of the parent.
List remoteNewObjects = ((RemoteUnitOfWork)this.parent).getNewObjectsCache();
for (int index = 0; index < size; index++) {
Object cloneFromParent = remoteNewObjects.get(index);
Object cloneFromSelf = this.newObjectsCache.get(index);
if (cloneFromSelf != null) {
originalToClone.put(cloneFromParent, cloneFromSelf);
cloneToOriginal.put(cloneFromSelf, cloneFromParent);
}
}
List remoteUnregisteredObjects = ((RemoteUnitOfWork)this.parent).getUnregisteredNewObjectsCache();
size = remoteUnregisteredObjects.size();
for (int index = 0; index < size; index++) {
Object cloneFromParent = ((RemoteUnitOfWork)getParent()).getUnregisteredNewObjects().get(remoteUnregisteredObjects.get(index));
Object cloneFromSelf = getUnregisteredNewObjects().get(this.unregisteredNewObjectsCache.get(index));
originalToClone.put(cloneFromParent, cloneFromSelf);
cloneToOriginal.put(cloneFromSelf, cloneFromParent);
}
this.newObjectsOriginalToClone = originalToClone;
this.newObjectsCloneToOriginal = cloneToOriginal;
}
/**
* INTERNAL:
* Re-initialize for the server-side session.
* This is done when the uow is passed back to the server for committing.
*/
public void reinitializeForSession(AbstractSession session, RemoteSessionController parentSessionController) {
// If a server, acquire a client to commit into as client store connection for commit.
if (session.isServerSession()) {
session = ((org.eclipse.persistence.sessions.server.ServerSession)session).acquireClientSession();
}
setIsOnClient(false);
setParentSessionController(parentSessionController);
setParent(session);
setProject(session.getProject());
setProfiler(session.getProfiler());
if (session.hasEventManager()) {
setEventManager(session.getEventManager().clone(this));
}
// setShouldLogMessages(session.shouldLogMessages());
setSessionLog(session.getSessionLog());
setLog(session.getLog());
// These are transient so must be reset.
setCommitManager(new CommitManager(this));
setTransactionMutex(new ConcurrencyManager());
getCommitManager().setCommitOrder(session.getCommitManager().getCommitOrder());
if (session.hasExternalTransactionController()) {
session.getExternalTransactionController().registerSynchronizationListener(this, session);
}
if (this.unitOfWorkChangeSet != null) {
fixRemoteChangeSet(this.unitOfWorkChangeSet, null, this);
}
if (this.cumulativeUOWChangeSet != null) {
fixRemoteChangeSet(this.cumulativeUOWChangeSet, null, this);
}
}
/**
* INTERNAL:
* Fix the transient fields in the serialized change set.
*/
protected void fixRemoteChangeSet(UnitOfWorkChangeSet uowChangeSet, Map cloneMap, AbstractSession session) {
if (uowChangeSet == null) {
return;
}
uowChangeSet.setSession(session);
for (Map.Entry> entry : uowChangeSet.getObjectChanges().entrySet()) {
ClassDescriptor descriptor = getDescriptor(entry.getKey());
for (ObjectChangeSet changeSet : entry.getValue().values()) {
changeSet.setDescriptor(descriptor);
changeSet.setClassType(entry.getKey());
}
}
for (Map.Entry> entry : uowChangeSet.getNewObjectChangeSets().entrySet()) {
ClassDescriptor descriptor = getDescriptor(entry.getKey());
for (ObjectChangeSet changeSet : entry.getValue().values()) {
changeSet.setDescriptor(descriptor);
changeSet.setClassType(entry.getKey());
}
}
if (cloneMap == null) {
for (Map.Entry