blob: 58237f572af44e16564d252e574d14668bc4f344 [file] [log] [blame]
/*
* 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
package org.eclipse.persistence.descriptors.changetracking;
import java.beans.PropertyChangeListener;
import java.util.*;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.descriptors.changetracking.*;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.ChangeRecord;
import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.internal.descriptors.*;
import org.eclipse.persistence.mappings.*;
import org.eclipse.persistence.queries.FetchGroup;
/**
* PUBLIC:
* An AttributeChangeTrackingPolicy allows change tracking at the attribute level of an
* object by implementing ChangeTracker. Objects with changed attributes will
* be processed in the UnitOfWork commit process to include any changes in the results of the
* commit. Unchanged objects will be ignored.
* @see DeferredChangeDetectionPolicy
* @see ObjectChangeTrackingPolicy
* @see ChangeTracker
*/
public class AttributeChangeTrackingPolicy extends ObjectChangeTrackingPolicy {
/**
* INTERNAL:
* PERF: Calculate change for the existing object, avoids check for new since already know.
* Avoid backup clone, as not used.
*/
@Override
public ObjectChangeSet calculateChangesForExistingObject(Object clone, UnitOfWorkChangeSet changeSet, UnitOfWorkImpl unitOfWork, ClassDescriptor descriptor, boolean shouldRaiseEvent) {
return calculateChanges(clone, null, false, changeSet, unitOfWork, descriptor, shouldRaiseEvent);
}
/**
* INTERNAL:
* Create ObjectChangeSet
*/
@Override
public ObjectChangeSet createObjectChangeSet(Object clone, Object backUp, org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet changeSet, boolean isNew, AbstractSession session, ClassDescriptor descriptor) {
ObjectChangeSet changes = null;
if (!isNew) {
AttributeChangeListener listener = (AttributeChangeListener)((ChangeTracker)clone)._persistence_getPropertyChangeListener();
if (listener != null){
changes = listener.getObjectChangeSet();
}
// The changes can be null if forceUpdate is used in CMP, so an empty change must be created.
if (changes != null) {
// PERF: Only merge the change set if merging into a new uow change set.
// merge the changeSet locally (ie the UOW's copy not the tracking policies copy) ; the local changeset will be returned.
if (changes.getUOWChangeSet() != changeSet) {
changes = changeSet.mergeObjectChanges(changes, (org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet)changes.getUOWChangeSet());
}
// check for deferred changes
if (changes.hasDeferredAttributes()){
//need to calculate the changes for these attributes.
for (Iterator<String> iterator = changes.getDeferredSet().iterator(); iterator.hasNext();){
DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(iterator.next());
mapping.calculateDeferredChanges((ChangeRecord)changes.getChangesForAttributeNamed(mapping.getAttributeName()), session);
}
changes.getDeferredSet().clear();
}
} else {
changes = descriptor.getObjectBuilder().createObjectChangeSet(clone, changeSet, isNew, session);
}
} else {
changes = descriptor.getObjectBuilder().createObjectChangeSet(clone, changeSet, isNew, true, session);
// PERF: Do not create change records for new objects.
if (descriptor.shouldUseFullChangeSetsForNewObjects() || descriptor.isAggregateDescriptor()) {
FetchGroup fetchGroup = null;
if(descriptor.hasFetchGroupManager()) {
fetchGroup = descriptor.getFetchGroupManager().getObjectFetchGroup(clone);
}
List<DatabaseMapping> mappings = descriptor.getMappings();
int size = mappings.size();
for (int index = 0; index < size; index++) {
DatabaseMapping mapping = mappings.get(index);
if ((fetchGroup == null) || fetchGroup.containsAttributeInternal(mapping.getAttributeName())) {
changes.addChange(mapping.compareForChange(clone, null, changes, session));
}
}
}
}
// The following code deals with reads that force changes to the flag associated with optimistic locking.
if ((descriptor.usesOptimisticLocking()) && (changes.getId() != null)) {
changes.setOptimisticLockingPolicyAndInitialWriteLockValue(descriptor.getOptimisticLockingPolicy(), session);
}
return changes;
}
/**
* Used to track instances of the change policies without doing an instance of check
*/
@Override
public boolean isAttributeChangeTrackingPolicy(){
return true;
}
/**
* INTERNAL:
* Clear the change set in the change event listener.
*/
@Override
public void updateWithChanges(Object object, ObjectChangeSet changeSet, UnitOfWorkImpl uow, ClassDescriptor descriptor) {
clearChanges(object, uow, descriptor, false);
}
/**
* INTERNAL:
* In cases where a relationship with detached or new entities is merged into itself previous changes may have been recorded for
* the detached/new entity that need to be updated.
*/
@Override
public void updateListenerForSelfMerge(ObjectChangeListener listener, ForeignReferenceMapping mapping, Object source, Object target, UnitOfWorkImpl unitOfWork){
ChangeRecord record = (ChangeRecord) ((AttributeChangeListener)listener).getObjectChangeSet().getChangesForAttributeNamed(mapping.getAttributeName());
mapping.updateChangeRecordForSelfMerge(record, source, target, (UnitOfWorkChangeSet) ((AttributeChangeListener)listener).getObjectChangeSet().getUOWChangeSet(), unitOfWork);
}
/**
* INTERNAL:
* Clear the change set in the change event listener.
*/
@Override
public void revertChanges(Object clone, ClassDescriptor descriptor, UnitOfWorkImpl uow, Map cloneMapping, boolean forRefresh) {
clearChanges(clone, uow, descriptor, forRefresh);
cloneMapping.put(clone, clone);
}
/**
* INTERNAL:
* Assign ChangeListener to an aggregate object
*/
@Override
public void setAggregateChangeListener(Object parent, Object aggregate, UnitOfWorkImpl uow, ClassDescriptor descriptor, String mappingAttribute){
((ChangeTracker)aggregate)._persistence_setPropertyChangeListener(new AggregateAttributeChangeListener(descriptor, uow, (AttributeChangeListener)((ChangeTracker)parent)._persistence_getPropertyChangeListener(), mappingAttribute, aggregate));
// set change trackers for nested aggregates
Iterator<DatabaseMapping> i = descriptor.getMappings().iterator();
while (i.hasNext()){
DatabaseMapping mapping = i.next();
if (mapping.isAggregateObjectMapping()){
AggregateObjectMapping aggregateMapping = (AggregateObjectMapping)mapping;
Object nestedAggregate = aggregateMapping.getAttributeValueFromObject(aggregate);
if (nestedAggregate != null && nestedAggregate instanceof ChangeTracker){
descriptor.getObjectChangePolicy().setAggregateChangeListener(aggregate, nestedAggregate, uow, aggregateMapping.getReferenceDescriptor(), aggregateMapping.getAttributeName());
}
}
}
}
/**
* INTERNAL:
* Assign AttributeChangeListener to PropertyChangeListener
*/
@Override
public PropertyChangeListener setChangeListener(Object clone, UnitOfWorkImpl uow, ClassDescriptor descriptor) {
AttributeChangeListener listener = new AttributeChangeListener(descriptor, uow, clone);
((ChangeTracker)clone)._persistence_setPropertyChangeListener(listener);
return listener;
}
/**
* INTERNAL:
* Set the ObjectChangeSet on the Listener, initially used for aggregate support
*/
@Override
public void setChangeSetOnListener(ObjectChangeSet objectChangeSet, Object clone){
((AttributeChangeListener)((ChangeTracker)clone)._persistence_getPropertyChangeListener()).setObjectChangeSet(objectChangeSet);
}
/**
* INTERNAL:
* Only build backup clone
*/
@Override
public Object buildBackupClone(Object clone, ObjectBuilder builder, UnitOfWorkImpl uow) {
return clone;
}
}