use of org.eclipse.persistence.internal.descriptors.PersistenceEntity in project eclipselink by eclipse-ee4j.
the class UnitOfWorkImpl method registerExistingObject.
/**
* INTERNAL:
* Register the existing object with the unit of work.
* This is a advanced API that can be used if the application can guarantee the object exists on the database.
* When registerObject is called the unit of work determines existence through the descriptor's doesExist setting.
*
* @return The clone of the original object, the return value must be used for editing.
* Editing the original is not allowed in the unit of work.
*/
public Object registerExistingObject(Object objectToRegister, ClassDescriptor descriptor, Object queryPrimaryKey, boolean isFromSharedCache) {
if (this.isClassReadOnly(descriptor.getJavaClass(), descriptor)) {
return objectToRegister;
}
if (isAfterWriteChangesButBeforeCommit()) {
throw ValidationException.illegalOperationForUnitOfWorkLifecycle(this.lifecycle, "registerExistingObject");
}
if (descriptor.isDescriptorTypeAggregate()) {
throw ValidationException.cannotRegisterAggregateObjectInUnitOfWork(objectToRegister.getClass());
}
CacheKey cacheKey = null;
Object objectToRegisterId = null;
Thread currentThread = Thread.currentThread();
if (project.allowExtendedCacheLogging()) {
// Not null if objectToRegister exist in cache
Session rootSession = this.getRootSession(null).getParent() == null ? this.getRootSession(null) : this.getRootSession(null).getParent();
cacheKey = ((org.eclipse.persistence.internal.sessions.IdentityMapAccessor) rootSession.getIdentityMapAccessor()).getCacheKeyForObject(objectToRegister);
objectToRegisterId = this.getId(objectToRegister);
if (cacheKey != null) {
log(SessionLog.FINEST, SessionLog.CACHE, "cache_hit", new Object[] { objectToRegister.getClass(), objectToRegisterId });
} else {
log(SessionLog.FINEST, SessionLog.CACHE, "cache_miss", new Object[] { objectToRegister.getClass(), objectToRegisterId });
}
if (cacheKey != null && currentThread.hashCode() != cacheKey.CREATION_THREAD_HASHCODE) {
log(SessionLog.FINEST, SessionLog.CACHE, "cache_thread_info", new Object[] { objectToRegister.getClass(), objectToRegisterId, cacheKey.CREATION_THREAD_ID, cacheKey.CREATION_THREAD_NAME, currentThread.getId(), currentThread.getName() });
}
}
if (project.allowExtendedThreadLogging()) {
if (this.CREATION_THREAD_HASHCODE != currentThread.hashCode()) {
log(SessionLog.SEVERE, SessionLog.THREAD, "unit_of_work_thread_info", new Object[] { this.getName(), this.CREATION_THREAD_ID, this.CREATION_THREAD_NAME, currentThread.getId(), currentThread.getName() });
if (project.allowExtendedThreadLoggingThreadDump()) {
log(SessionLog.SEVERE, SessionLog.THREAD, "unit_of_work_thread_info_thread_dump", new Object[] { this.CREATION_THREAD_ID, this.CREATION_THREAD_NAME, this.creationThreadStackTrace, currentThread.getId(), currentThread.getName(), ConcurrencyUtil.SINGLETON.enrichGenerateThreadDumpForCurrentThread() });
}
}
}
// CR#2272
logDebugMessage(objectToRegister, "register_existing");
Object registeredObject;
try {
startOperationProfile(SessionProfiler.Register);
registeredObject = checkIfAlreadyRegistered(objectToRegister, descriptor);
if (registeredObject == null) {
// Check if object is existing, if it is it must be cloned into the unit of work
// otherwise it is a new object
Object primaryKey = queryPrimaryKey;
if (primaryKey == null) {
primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(objectToRegister, this, true);
}
if (descriptor.shouldLockForClone() || !isFromSharedCache || (descriptor.isProtectedIsolation() && !(objectToRegister instanceof PersistenceEntity))) {
// The primary key may be null for a new object in a nested unit of work (is existing in nested, new in parent).
if (primaryKey != null) {
// Always check the cache first.
registeredObject = getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, objectToRegister, objectToRegister.getClass(), true, descriptor);
}
} else {
// perform a check of the UOW identitymap. This would be done by getFromIdentityMap
// but that method also calls back up to the shared cache in case it is not found locally.
// and we wish to avoid checking the shared cache twice.
CacheKey localCacheKey = getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, objectToRegister.getClass(), descriptor, false);
if (localCacheKey != null) {
registeredObject = localCacheKey.getObject();
}
}
if (registeredObject == null) {
// This is a case where the object is not in the session cache, or the session lookup has been bypassed
// check object for cachekey otherwise
// a new cache-key is used as there is no original to use for locking.
// It read time must be set to avoid it being invalidated.
cacheKey = null;
if (objectToRegister instanceof PersistenceEntity) {
cacheKey = ((PersistenceEntity) objectToRegister)._persistence_getCacheKey();
}
if (cacheKey == null) {
cacheKey = new CacheKey(primaryKey);
cacheKey.setReadTime(System.currentTimeMillis());
// if the cache does not have a version then this must be built from the supplied version
cacheKey.setIsolated(true);
}
registeredObject = cloneAndRegisterObject(objectToRegister, cacheKey, descriptor);
}
}
// fetch group manager control fetch group support
if (descriptor.hasFetchGroupManager()) {
// if the object is already registered in uow, but it's partially fetched (fetch group case)
if (descriptor.getFetchGroupManager().shouldWriteInto(objectToRegister, registeredObject)) {
// there might be cases when reverting/refreshing clone is needed.
descriptor.getFetchGroupManager().writePartialIntoClones(objectToRegister, registeredObject, this.getBackupClone(registeredObject, descriptor), this);
}
}
} finally {
endOperationProfile(SessionProfiler.Register);
}
return registeredObject;
}
use of org.eclipse.persistence.internal.descriptors.PersistenceEntity in project eclipselink by eclipse-ee4j.
the class IsolatedClientSessionIdentityMapAccessor method getAndCloneCacheKeyFromParent.
protected Object getAndCloneCacheKeyFromParent(Object primaryKey, Object objectToClone, Class<?> theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) {
CacheKey cacheKey = null;
if (objectToClone != null && objectToClone instanceof PersistenceEntity) {
cacheKey = ((PersistenceEntity) objectToClone)._persistence_getCacheKey();
}
if (cacheKey == null || cacheKey.isIsolated() || cacheKey.getOwningMap() == null) {
org.eclipse.persistence.internal.sessions.IdentityMapAccessor parentIdentityMapAccessor = session.getParent().getIdentityMapAccessorInstance();
cacheKey = parentIdentityMapAccessor.getCacheKeyForObject(primaryKey, theClass, descriptor, false);
}
Object objectFromCache = null;
// as well we must inspect the cacheKey without locking on it.
if ((cacheKey != null) && (shouldReturnInvalidatedObjects || !descriptor.getCacheInvalidationPolicy().isInvalidated(cacheKey))) {
synchronized (cacheKey) {
// if the object in the cachekey is null but the key is acquired then
// someone must be rebuilding it or creating a new one. Sleep until
// it's finished. A plain wait here would be more efficient but we may not
// get notified for quite some time (ie deadlock) if the other thread
// is building the object. Must wait and not sleep in order for the monitor to be released
objectFromCache = cacheKey.getObject();
try {
while (cacheKey.isAcquired() && (objectFromCache == null)) {
cacheKey.wait(5);
}
} catch (InterruptedException ex) {
}
if (objectFromCache == null) {
return null;
}
}
} else {
return null;
}
ClassDescriptor concreteDescriptor = descriptor;
// Ensure correct subclass descriptor.
if (objectFromCache.getClass() != descriptor.getJavaClass()) {
concreteDescriptor = session.getDescriptor(objectFromCache);
}
ObjectBuilder builder = concreteDescriptor.getObjectBuilder();
Object workingClone = null;
// The cache/objects being registered must first be locked to ensure
// that a merge or refresh does not occur on the object while being cloned to
// avoid cloning a partially merged/refreshed object.
// If a cache isolation level is used, then lock the entire cache.
// otherwise lock the object and it related objects (not using indirection) as a unit.
// If just a simple object (all indirection) a simple read-lock can be used.
// PERF: Cache if check to write is required.
org.eclipse.persistence.internal.sessions.IdentityMapAccessor parentIdentityMapAccessor = session.getParent().getIdentityMapAccessorInstance();
boolean identityMapLocked = parentIdentityMapAccessor.acquireWriteLock();
boolean rootOfCloneRecursion = false;
if (identityMapLocked) {
session.checkAndRefreshInvalidObject(objectFromCache, cacheKey, descriptor);
} else {
// Check if we have locked all required objects already.
if (this.objectsLockedForClone == null) {
// PERF: If a simple object just acquire a simple read-lock.
if (concreteDescriptor.shouldAcquireCascadedLocks()) {
this.objectsLockedForClone = parentIdentityMapAccessor.getWriteLockManager().acquireLocksForClone(objectFromCache, concreteDescriptor, cacheKey, session);
} else {
session.checkAndRefreshInvalidObject(objectFromCache, cacheKey, descriptor);
cacheKey.acquireReadLock();
}
rootOfCloneRecursion = true;
}
}
try {
// bug:6167576 Must acquire the lock before cloning.
workingClone = builder.instantiateWorkingCopyClone(objectFromCache, session);
// PERF: Cache the primary key if implements PersistenceEntity.
if (workingClone instanceof PersistenceEntity) {
((PersistenceEntity) workingClone)._persistence_setId(cacheKey.getKey());
}
CacheKey localCacheKey = acquireLock(primaryKey, theClass, descriptor, false);
try {
localCacheKey.setObject(workingClone);
localCacheKey.setReadTime(cacheKey.getReadTime());
localCacheKey.setWriteLockValue(cacheKey.getWriteLockValue());
builder.populateAttributesForClone(objectFromCache, cacheKey, workingClone, null, session);
} finally {
localCacheKey.release();
}
// also clone the fetch group reference if applied
if (concreteDescriptor.hasFetchGroupManager()) {
concreteDescriptor.getFetchGroupManager().copyFetchGroupInto(objectFromCache, workingClone, session);
}
} finally {
// otherwise release the entire set of locks for related objects if this was the root.
if (identityMapLocked) {
parentIdentityMapAccessor.releaseWriteLock();
} else {
if (rootOfCloneRecursion) {
if (this.objectsLockedForClone == null) {
cacheKey.releaseReadLock();
} else {
for (Iterator iterator = this.objectsLockedForClone.values().iterator(); iterator.hasNext(); ) {
((CacheKey) iterator.next()).releaseReadLock();
}
this.objectsLockedForClone = null;
}
session.executeDeferredEvents();
}
}
}
concreteDescriptor.getObjectBuilder().instantiateEagerMappings(workingClone, session);
return workingClone;
}
use of org.eclipse.persistence.internal.descriptors.PersistenceEntity in project eclipselink by eclipse-ee4j.
the class MergeManager method mergeChangesOfWorkingCopyIntoOriginal.
/**
* Recursively merge to clone into the original in its parent.
* The map is used to resolve recursion.
* This is used to merge objects from the unit of work into the shared (or isolated) cache.
*/
protected CacheKey mergeChangesOfWorkingCopyIntoOriginal(Object clone, ObjectChangeSet objectChangeSet, ClassDescriptor descriptor, AbstractSession targetSession, UnitOfWorkImpl unitOfWork) {
/**
* This is the merge used by the unit of work on commit.
*** This is a very complex method that handles several different use cases of the unit of work. ***
* These include:
* #1 - normal merge of new and changed objects with pre-acquired merge locks
* #2 - old merge without merge locks
* #3 - nested units of work
* #4 - no identity map, cache identity map, cleared identity map
* #5 - objects read or merged into uow not in shared cache
* #6 - merging references to objects not in shared cache
* #7 - merging references to detached objects
* #8 - merging into protected cache
* #9 - grid cache
*/
ObjectBuilder objectBuilder = descriptor.getObjectBuilder();
// This always finds an original different from the clone, even if it has to create one.
// This must be done after special cases have been computed because it registers unregistered new objects.
// First check the cache key.
Object original = null;
CacheKey cacheKey = null;
// The client session check is for the protected cache support, the active cache key is from the server session, so protected cache cannot use it
if ((!targetSession.isClientSession() || !descriptor.getCachePolicy().isProtectedIsolation()) && (objectChangeSet != null)) {
// #1 - Normal case, cache key is already locked.
cacheKey = objectChangeSet.getActiveCacheKey();
if (cacheKey != null) {
original = cacheKey.getObject();
}
}
ObjectBuilder builder = descriptor.getObjectBuilder();
Object implementation = builder.unwrapObject(clone, unitOfWork);
// This occurs in the old merge, or if a new or changed object references an existing object that needs to be merged.
if (cacheKey == null) {
// #2 - old merge, #3 - nested, #6 referenced objects, #7 detached objects, #8 protected cache
cacheKey = targetSession.getCacheKeyFromTargetSessionForMerge(implementation, builder, descriptor, this);
if (cacheKey != null) {
original = cacheKey.getObject();
}
}
// The original will be null for new objects, objects merged or read into the uow, or referenced objects that are not in the cache.
if (original == null) {
// #1, #2, #3 new objects
original = unitOfWork.getOriginalVersionOfObjectOrNull(clone, objectChangeSet, descriptor, targetSession);
// Original was not in cache. Make sure it is placed in the cache.
if (original != null) {
if (cacheKey == null) {
// #2, 4, 6, 7 - This occurs if the object was removed from the cache, or No/CacheIdentityMap, and using old merge, or a referenced object.
cacheKey = targetSession.getIdentityMapAccessorInstance().getWriteLockManager().appendLock(descriptor.getObjectBuilder().extractPrimaryKeyFromObject(clone, targetSession), original, descriptor, this, targetSession);
} else {
if (cacheKey.getObject() != null) {
original = cacheKey.getObject();
} else {
cacheKey.setObject(original);
}
}
} else {
// #1 No original, there is only an original if registerObject is used, registerNewObject or JPA never have an original.
}
}
// Always merge into the original.
try {
if (original == null) {
// #1, 2, 3, 9, 4, 5, 6
// If original does not exist then we must merge the entire object.
// This occurs when an object is new, was merged or read into the unit of work and, or referenced objects is not in the shared cache.
original = unitOfWork.buildOriginal(clone);
if (objectChangeSet == null) {
// #6 - references to uncached objects
// No changeset so this would not have been locked as part of the unit of work acquireLocks, so must append a lock.
// This should only occur when a changed or new object references another object that is not in the cache.
cacheKey = targetSession.getIdentityMapAccessorInstance().getWriteLockManager().appendLock(descriptor.getObjectBuilder().extractPrimaryKeyFromObject(clone, targetSession), original, descriptor, this, targetSession);
if (cacheKey.getObject() != null) {
original = cacheKey.getObject();
} else {
cacheKey.setObject(original);
}
objectBuilder.mergeIntoObject(original, null, true, clone, this, targetSession, false, !descriptor.getCopyPolicy().buildsNewInstance(), true);
if (!unitOfWork.isObjectRegistered(clone)) {
// mark the instance in the cache as invalid as we may have just merged a stub if
// a detached stub was referenced by a managed entity
cacheKey.setInvalidationState(CacheKey.CACHE_KEY_INVALID);
}
} else {
// #1, 2, 3, new objects, #9 grid
if (cacheKey == null) {
// #2 - The cache key should only be null for the old merge.
cacheKey = targetSession.getIdentityMapAccessorInstance().getWriteLockManager().appendLock(objectChangeSet.getId(), original, descriptor, this, targetSession);
}
if (cacheKey.getObject() != null) {
original = cacheKey.getObject();
} else {
// #1, 2, 3, new objects
cacheKey.setObject(original);
}
if (!objectChangeSet.isNew()) {
// #5 read in uow, #9 grid
// Bug#465051 : fetchGroupManager needs to be set with fetchGroup so that subsequent access can determine if lazy basics were fetched
objectBuilder.mergeIntoObject(original, objectChangeSet, true, clone, this, targetSession, false, !descriptor.getCopyPolicy().buildsNewInstance(), true);
if (!unitOfWork.isObjectRegistered(clone)) {
// mark the instance in the cache as invalid as we may have just merged a stub if
// a detached stub was referenced by a managed entity
cacheKey.setInvalidationState(CacheKey.CACHE_KEY_INVALID);
}
} else {
objectBuilder.mergeChangesIntoObject(original, objectChangeSet, clone, this, targetSession, !descriptor.getCopyPolicy().buildsNewInstance(), true);
// PERF: If PersistenceEntity is caching the primary key this must be cleared as the primary key may have changed in new objects.
}
}
if (original instanceof PersistenceEntity) {
Object pk = cacheKey.getKey();
objectBuilder.updateCachedAttributes((PersistenceEntity) original, cacheKey, pk);
}
updateCacheKeyProperties(unitOfWork, cacheKey, original, clone, objectChangeSet, descriptor);
} else if (objectChangeSet == null) {
// #6, 7 - referenced objects
// PERF: If we have no change set and it has an original, then no merging is required, just use the original object.
} else if (descriptor.getFullyMergeEntity() && objectChangeSet.hasChanges()) {
objectBuilder.mergeIntoObject(original, objectChangeSet, false, clone, this, targetSession, false, false, true);
} else {
// mappings would be set to null when they should be set to an object.
if (objectChangeSet.hasChanges()) {
// writeLockValue when we do not own the lock.
if (!objectChangeSet.isNew()) {
if (objectChangeSet.shouldInvalidateObject(original, targetSession) && (!unitOfWork.isNestedUnitOfWork())) {
// Invalidate any object that was marked invalid during the change calculation, even if it was new as multiple flushes
// and custom SQL could still produce invalid new objects. ? This seems to contradict the new check?
targetSession.getIdentityMapAccessor().invalidateObject(original);
// no need to update cacheKey properties here
}
} else {
// PERF: If PersistenceEntity is caching the primary key this must be cleared as the primary key may have changed in new objects.
if (original instanceof PersistenceEntity) {
Object pk = null;
if (cacheKey == null) {
pk = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(original, unitOfWork);
} else {
pk = cacheKey.getKey();
}
objectBuilder.updateCachedAttributes((PersistenceEntity) original, cacheKey, pk);
}
}
// #1, 2, 3, merge from the change set into the existing cached object, or new original
// Note for new objects the change set may be empty, and object builder may merge from the clone into the original.
objectBuilder.mergeChangesIntoObject(original, objectChangeSet, clone, this, targetSession, false, objectChangeSet.isNew());
updateCacheKeyProperties(unitOfWork, cacheKey, original, clone, objectChangeSet, descriptor);
} else {
// #6, 7 - reference object, but object was in shared cache, and had no changed, so just use the reference.
// What if the original object feel out of the cache? i.e. #4 case, the object being put back in may have detached references.
// If there are no changes then we just need a reference to the object so skip the merge
// saves trying to lock related objects after the fact producing deadlocks
}
}
} catch (QueryException exception) {
// but is still valid, so this error must be ignored.
if (unitOfWork.shouldPerformNoValidation() || (descriptor.hasWrapperPolicy())) {
if ((exception.getErrorCode() != QueryException.BACKUP_CLONE_DELETED) && (exception.getErrorCode() != QueryException.BACKUP_CLONE_IS_ORIGINAL_FROM_PARENT) && (exception.getErrorCode() != QueryException.BACKUP_CLONE_IS_ORIGINAL_FROM_SELF)) {
throw exception;
}
return cacheKey;
} else {
throw exception;
}
}
return cacheKey;
}
use of org.eclipse.persistence.internal.descriptors.PersistenceEntity in project eclipselink by eclipse-ee4j.
the class IdHelper method buildObjectShell.
/**
* build a shell of an object based on a primary key. The object shell will be an instance of the object with
* only primary key populated
*/
public static Object buildObjectShell(PersistenceContext context, String entityType, Object id) {
ClassDescriptor descriptor = context.getDescriptor(entityType);
List<DatabaseMapping> pkMappings = descriptor.getObjectBuilder().getPrimaryKeyMappings();
Object entity = null;
if (descriptor.hasCMPPolicy()) {
CMP3Policy policy = (CMP3Policy) descriptor.getCMPPolicy();
entity = policy.createBeanUsingKey(id, (AbstractSession) context.getServerSession());
} else if (DynamicEntity.class.isAssignableFrom(descriptor.getJavaClass())) {
DynamicEntityImpl dynamicEntity = (DynamicEntityImpl) context.newEntity(entityType);
// represents the value of that mapping
if (pkMappings.size() == 1) {
dynamicEntity.set(pkMappings.get(0).getAttributeName(), id);
} else {
// If there are more that one PK, we assume an array as produced
// by buildId() above with the keys
// based on a sorted order of PK fields
List<SortableKey> pkIndices = new ArrayList<>();
int index = 0;
for (DatabaseMapping mapping : pkMappings) {
pkIndices.add(new SortableKey(mapping, index));
index++;
}
Collections.sort(pkIndices);
Object[] keyElements = (Object[]) id;
for (SortableKey key : pkIndices) {
dynamicEntity.set(key.getMapping().getAttributeName(), keyElements[key.getIndex()]);
}
}
entity = dynamicEntity;
} else {
throw new RuntimeException("Could not create shell for entity.");
}
if (entity instanceof PersistenceEntity) {
((PersistenceEntity) entity)._persistence_setId(id);
}
if (entity instanceof FetchGroupTracker) {
((FetchGroupTracker) entity)._persistence_setSession(JpaHelper.getDatabaseSession(context.getEmf()));
}
return entity;
}
Aggregations