org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork Maven / Gradle / Ivy
/*
* 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
// Gordon Yorke - VM managed entity detachment
// 07/16/2009-2.0 Guy Pelletier
// - 277039: JPA 2.0 Cache Usage Settings
// 07/15/2011-2.2.1 Guy Pelletier
// - 349424: persists during an preCalculateUnitOfWorkChangeSet event are lost
package org.eclipse.persistence.internal.sessions;
import java.util.*;
import org.eclipse.persistence.config.FlushClearCache;
import org.eclipse.persistence.config.ReferenceMode;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.changetracking.AttributeChangeTrackingPolicy;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.helper.IdentityHashSet;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.OptimisticLockException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.queries.ObjectBuildingQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.sessions.IdentityMapAccessor;
public class RepeatableWriteUnitOfWork extends UnitOfWorkImpl {
/** Used to store the final UnitOfWorkChangeSet for merge into the shared cache */
protected UnitOfWorkChangeSet cumulativeUOWChangeSet;
/**
* Used to determine if UnitOfWork should commit and rollback transactions.
* This is used when an EntityTransaction is controlling the transaction.
*/
protected boolean shouldTerminateTransaction;
/**
* Used to determine if we should bypass any merge into the cache. This is
* a JPA flag and is true when the cacheStoreMode property is set to BYPASS.
* Otherwise, EclipseLink behaves as it usually would.
*/
protected boolean shouldStoreBypassCache;
/**
* The FlashClearCache mode to be used.
* Initialized by setUnitOfWorkChangeSet method in case it's null;
* commitAndResume sets this attribute back to null.
* Relevant only in case call to flush method followed by call to clear method.
* @see org.eclipse.persistence.config.FlushClearCache
*/
protected transient String flushClearCache;
/**
* Track whether we are already in a flush().
*/
protected boolean isWithinFlush;
/** Contains classes that should be invalidated in the shared cache on commit.
* Used only in case fushClearCache == FlushClearCache.DropInvalidate:
* clear method copies contents of updatedObjectsClasses to this set,
* adding classes of deleted objects, too;
* on commit the classes contained here are invalidated in the shared cache
* and the set is cleared.
* Relevant only in case call to flush method followed by call to clear method.
* Works together with flushClearCache.
*/
protected transient Set classesToBeInvalidated;
/**
* Alters the behaviour of the RWUOW commit to function like the UOW with respect to Entity lifecycle
*/
protected boolean discoverUnregisteredNewObjectsWithoutPersist;
public RepeatableWriteUnitOfWork() {
}
public RepeatableWriteUnitOfWork(org.eclipse.persistence.internal.sessions.AbstractSession parentSession, ReferenceMode referenceMode){
super(parentSession, referenceMode);
this.shouldTerminateTransaction = true;
this.shouldNewObjectsBeCached = true;
this.isWithinFlush = false;
this.discoverUnregisteredNewObjectsWithoutPersist = false;
}
/**
* @return the discoverUnregisteredNewObjectsWithoutPersist
*/
public boolean shouldDiscoverUnregisteredNewObjectsWithoutPersist() {
return discoverUnregisteredNewObjectsWithoutPersist;
}
/**
* @param discoverUnregisteredNewObjectsWithoutPersist the discoverUnregisteredNewObjectsWithoutPersist to set
*/
public void setDiscoverUnregisteredNewObjectsWithoutPersist(boolean discoverUnregisteredNewObjectsWithoutPersist) {
this.discoverUnregisteredNewObjectsWithoutPersist = discoverUnregisteredNewObjectsWithoutPersist;
}
/**
* INTERNAL:
* This method will clear all registered objects from this UnitOfWork.
* If parameter value is 'true' then the cache(s) are cleared, too.
*/
public void clear(boolean shouldClearCache) {
super.clear(shouldClearCache);
if (this.cumulativeUOWChangeSet != null) {
if (this.flushClearCache == FlushClearCache.Drop) {
this.cumulativeUOWChangeSet = null;
this.unregisteredDeletedObjectsCloneToBackupAndOriginal = null;
} else if (this.flushClearCache == FlushClearCache.DropInvalidate) {
// classes of the updated objects should be invalidated in the shared cache on commit.
Set updatedObjectsClasses = this.cumulativeUOWChangeSet.findUpdatedObjectsClasses();
if (updatedObjectsClasses != null) {
if (this.classesToBeInvalidated == null) {
this.classesToBeInvalidated = updatedObjectsClasses;
} else {
this.classesToBeInvalidated.addAll(updatedObjectsClasses);
}
}
if ((this.unregisteredDeletedObjectsCloneToBackupAndOriginal != null) && !this.unregisteredDeletedObjectsCloneToBackupAndOriginal.isEmpty()) {
if (this.classesToBeInvalidated == null) {
this.classesToBeInvalidated = new HashSet();
}
Iterator enumDeleted = this.unregisteredDeletedObjectsCloneToBackupAndOriginal.keySet().iterator();
// classes of the deleted objects should be invalidated in the shared cache
while (enumDeleted.hasNext()) {
this.classesToBeInvalidated.add(getDescriptor(enumDeleted.next().getClass()));
}
}
this.cumulativeUOWChangeSet = null;
this.unregisteredDeletedObjectsCloneToBackupAndOriginal = null;
}
}
}
/**
* INTERNAL:
* Call this method if the uow will no longer used for committing transactions:
* all the changes sets will be dereferenced, and (optionally) the cache cleared.
* If the uow is not released, but rather kept around for ValueHolders, then identity maps shouldn't be cleared:
* the parameter value should be 'false'. The lifecycle set to Birth so that uow ValueHolder still could be used.
* Alternatively, if called from release method then everything should go and therefore parameter value should be 'true'.
* In this case lifecycle won't change - uow.release (optionally) calls this method when it (uow) is already dead.
* The reason for calling this method from release is to free maximum memory right away:
* the uow might still be referenced by objects using UOWValueHolders (though they shouldn't be around
* they still might).
*/
public void clearForClose(boolean shouldClearCache){
this.cumulativeUOWChangeSet = null;
this.unregisteredDeletedObjectsCloneToBackupAndOriginal = null;
super.clearForClose(shouldClearCache);
}
/**
* INTERNAL:
* Return classes that should be invalidated in the shared cache on commit.
* Used only in case fushClearCache == FlushClearCache.DropInvalidate:
* clear method copies contents of updatedObjectsClasses to this set,
* adding classes of deleted objects, too;
* on commit the classes contained here are invalidated in the shared cache
* and the set is cleared.
* Relevant only in case call to flush method followed by call to clear method.
* Works together with flushClearCache.
*/
public Set getClassesToBeInvalidated(){
return classesToBeInvalidated;
}
/**
* INTERNAL:
* Get the final UnitOfWorkChangeSet for merge into the shared cache.
*/
public UnitOfWorkChangeSet getCumulativeUOWChangeSet() {
return cumulativeUOWChangeSet;
}
/**
* INTERNAL:
* Set the final UnitOfWorkChangeSet for merge into the shared cache.
*/
public void setCumulativeUOWChangeSet(UnitOfWorkChangeSet cumulativeUOWChangeSet) {
this.cumulativeUOWChangeSet = cumulativeUOWChangeSet;
}
/**
* INTERNAL:
* Calculate whether we should read directly from the database to the UOW.
* This will be necessary, if a flush and a clear have been called on this unit of work
* In that case, there will be changes in the database that are not in the shared cache,
* so a read in this UOW should get info directly form the DB
*/
@Override
public boolean shouldForceReadFromDB(ObjectBuildingQuery query, Object primaryKey){
if (this.wasTransactionBegunPrematurely() && query.getDescriptor() != null){
// if the saved change set for this UOW contains any changes to the class that is being queried for,
// we should build from the DB
if (this.getFlushClearCache().equals(FlushClearCache.Merge) && this.getCumulativeUOWChangeSet() != null){
Map changeSetMap = this.getCumulativeUOWChangeSet().getObjectChanges().get(query.getDescriptor().getJavaClass());
Object lookupPrimaryKey = null;
if (primaryKey == null && query.isReadObjectQuery()){
lookupPrimaryKey = ((ReadObjectQuery)query).getSelectionId();
}
if (changeSetMap != null ){
if (lookupPrimaryKey == null){
return true;
} else {
// this change set is simply used to do a lookup in the map. The hashcode method just needs the key and the descriptor
ObjectChangeSet lookupChangeSet = new ObjectChangeSet(lookupPrimaryKey, query.getDescriptor(), null, null, false);
if (changeSetMap.get(lookupChangeSet) != null){
return true;
}
}
}
// if the invalidation list for this UOW contains any changes to the class being queried for
// we should build directly from the DB
} else if (this.getFlushClearCache().equals(FlushClearCache.DropInvalidate) && this.getClassesToBeInvalidated() != null){
if (this.getClassesToBeInvalidated().contains(query.getDescriptor())){
return true;
}
}
}
return false;
}
/**
* INTERNAL:
* Indicates whether clearForClose method should be called by release method.
*/
public boolean shouldClearForCloseOnRelease() {
return true;
}
/**
* INTERNAL:
* Returns true if the UOW should bypass any updated to the shared cache
* during the merge.
*/
@Override
public boolean shouldStoreBypassCache() {
return shouldStoreBypassCache;
}
/**
* Check to see if the descriptor of a superclass can be used to describe this class
*
* By default, in JPA, classes must have specific descriptors to be considered entities
* In this implementation, we check whether the inheritance policy has been configured to allow
* superclass descriptors to describe subclasses that do not have a descriptor themselves
*
* @param Class
* @return ClassDescriptor
*/
protected ClassDescriptor checkHierarchyForDescriptor(Class theClass){
ClassDescriptor descriptor = getDescriptor(theClass.getSuperclass());
if (descriptor != null && descriptor.hasInheritance() && descriptor.getInheritancePolicy().getDescribesNonPersistentSubclasses()){
return descriptor;
}
return null;
}
/**
* INTERNAL:
* Commit the changes to any objects to the parent.
*/
public void commitRootUnitOfWork() throws DatabaseException, OptimisticLockException {
commitToDatabaseWithChangeSet(false);
// unit of work has been committed so it's ok to set the cumulative into the UOW for merge
if (this.cumulativeUOWChangeSet != null) {
this.cumulativeUOWChangeSet.mergeUnitOfWorkChangeSet((UnitOfWorkChangeSet)this.getUnitOfWorkChangeSet(), this, true);
setUnitOfWorkChangeSet(this.cumulativeUOWChangeSet);
}
commitTransactionAfterWriteChanges(); // this method will commit the
// transaction
// and set the transaction
// flags appropriately
// Merge after commit
mergeChangesIntoParent();
}
/**
* INTERNAL:
* Traverse the object to find references to objects not registered in this unit of work.
* Any unregistered new objects found will be persisted or an error will be thrown depending on the mapping's cascade persist.
* References to deleted objects will also currently cause them to be undeleted.
*/
public void discoverUnregisteredNewObjects(Map clones, Map newObjects, Map unregisteredExistingObjects, Map visitedObjects) {
if (this.discoverUnregisteredNewObjectsWithoutPersist){
super.discoverUnregisteredNewObjects(clones, newObjects, unregisteredExistingObjects, visitedObjects);
}else{
//Bug#438193 : Replace HashSet with IdentityHashSet below for cascadePersistErrors so that the comparison will be by reference and
//not by equals() which invokes hashCode()
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy