| /* |
| * 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; |
| } |
| } |