Search in sources :

Example 1 with DirectCollectionChangeRecord

use of org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method compareForChange.

/**
 * INTERNAL:
 * This method compares the changes between two direct collections.  Comparisons are made on equality
 * not identity.
 */
@Override
public ChangeRecord compareForChange(Object clone, Object backUp, ObjectChangeSet owner, AbstractSession session) {
    Object cloneAttribute = getAttributeValueFromObject(clone);
    Object backUpAttribute = null;
    if ((cloneAttribute != null) && (!getIndirectionPolicy().objectIsInstantiated(cloneAttribute))) {
        return null;
    }
    Object cloneObjectCollection = getRealCollectionAttributeValueFromObject(clone, session);
    Object backUpCollection = null;
    if (!owner.isNew()) {
        backUpAttribute = getAttributeValueFromObject(backUp);
        if ((backUpAttribute == null) && (cloneAttribute == null)) {
            return null;
        }
        backUpCollection = getRealCollectionAttributeValueFromObject(backUp, session);
    }
    DirectCollectionChangeRecord changeRecord = new DirectCollectionChangeRecord(owner);
    changeRecord.setAttribute(getAttributeName());
    changeRecord.setMapping(this);
    if (this.listOrderField != null) {
        changeRecord.setLatestCollection(cloneObjectCollection);
    }
    compareCollectionsForChange(backUpCollection, cloneObjectCollection, changeRecord, session);
    if (changeRecord.hasChanges()) {
        changeRecord.setOriginalCollection(backUpCollection);
        return changeRecord;
    }
    return null;
}
Also used : DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord)

Example 2 with DirectCollectionChangeRecord

use of org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method postUpdateWithChangeSetListOrder.

/**
 * INTERNAL:
 * Update private owned part.
 */
protected void postUpdateWithChangeSetListOrder(WriteObjectQuery writeQuery) throws DatabaseException {
    ObjectChangeSet changeSet = writeQuery.getObjectChangeSet();
    DirectCollectionChangeRecord changeRecord = (DirectCollectionChangeRecord) changeSet.getChangesForAttributeNamed(this.getAttributeName());
    if (changeRecord == null) {
        return;
    }
    for (int index = 0; index < getReferenceKeyFields().size(); index++) {
        DatabaseField referenceKey = getReferenceKeyFields().get(index);
        DatabaseField sourceKey = getSourceKeyFields().get(index);
        Object sourceKeyValue = writeQuery.getTranslationRow().get(sourceKey);
        writeQuery.getTranslationRow().put(referenceKey, sourceKeyValue);
    }
    boolean shouldRepairOrder = false;
    if (changeRecord.getLatestCollection() instanceof IndirectList) {
        shouldRepairOrder = ((IndirectList) changeRecord.getLatestCollection()).isListOrderBrokenInDb();
    }
    if (shouldRepairOrder) {
        // delete all members of collection
        DeleteObjectQuery deleteQuery = new DeleteObjectQuery();
        deleteQuery.setObject(writeQuery.getObject());
        deleteQuery.setSession(writeQuery.getSession());
        deleteQuery.setTranslationRow(writeQuery.getTranslationRow());
        // Hey I might actually want to use an inner class here... ok array for now.
        Object[] eventDeleteAll = new Object[2];
        eventDeleteAll[0] = DeleteAll;
        eventDeleteAll[1] = deleteQuery;
        writeQuery.getSession().getCommitManager().addDataModificationEvent(this, eventDeleteAll);
        // re-insert them back
        for (int i = 0; i < ((List) changeRecord.getLatestCollection()).size(); i++) {
            Object value = ((List) changeRecord.getLatestCollection()).get(i);
            value = getFieldValue(value, writeQuery.getSession());
            AbstractRecord insertRow = writeQuery.getTranslationRow().clone();
            insertRow.add(getDirectField(), value);
            insertRow.add(this.listOrderField, i);
            // Hey I might actually want to use an inner class here... ok array for now.
            Object[] event = new Object[3];
            event[0] = Insert;
            event[1] = getInsertQuery();
            event[2] = insertRow;
            writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
        }
        ((IndirectList) changeRecord.getLatestCollection()).setIsListOrderBrokenInDb(false);
        changeRecord.setOrderHasBeenRepaired(true);
        return;
    }
    if (changeRecord.getChangedIndexes() == null) {
        compareListsForChange((List) changeRecord.getOriginalCollection(), (List) changeRecord.getLatestCollection(), changeRecord, writeQuery.getSession());
    }
    Iterator<Map.Entry<Object, Set[]>> it = changeRecord.getChangedIndexes().entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Object, Set[]> entry = it.next();
        Object value = entry.getKey();
        if (getValueConverter() != null) {
            value = getValueConverter().convertObjectValueToDataValue(value, writeQuery.getSession());
        }
        Set[] indexes = entry.getValue();
        Set indexesBefore = indexes[0];
        Set indexesAfter = indexes[1];
        if (indexesAfter == null) {
            // All copies of the target object deleted - don't need to verify order field contents.
            AbstractRecord deleteRow = writeQuery.getTranslationRow().clone();
            // Hey I might actually want to use an inner class here... ok array for now.
            Object[] event = new Object[3];
            event[0] = Delete;
            if (value == null) {
                // Bug 306075 - for deleting a null value from a collection
                event[1] = getDeleteNullQuery();
            } else {
                deleteRow.add(getDirectField(), value);
                event[1] = getDeleteQuery();
            }
            event[2] = deleteRow;
            writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
        } else if (indexesAfter.isEmpty()) {
            // Some copies of the target objects should be deleted, some left in the db
            Iterator<Integer> itBefore = indexesBefore.iterator();
            while (itBefore.hasNext()) {
                AbstractRecord deleteAtIndexRow = writeQuery.getTranslationRow().clone();
                deleteAtIndexRow.add(getDirectField(), value);
                deleteAtIndexRow.add(this.listOrderField, itBefore.next());
                // Hey I might actually want to use an inner class here... ok array for now.
                Object[] event = new Object[3];
                event[0] = DeleteAtIndex;
                event[1] = deleteAtIndexQuery;
                event[2] = deleteAtIndexRow;
                writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
            }
        } else {
            if (indexesBefore == null || indexesBefore.isEmpty()) {
                // insert the object for each index in indexesAfter
                Iterator<Integer> itAfter = indexesAfter.iterator();
                while (itAfter.hasNext()) {
                    AbstractRecord insertRow = writeQuery.getTranslationRow().clone();
                    insertRow.add(getDirectField(), value);
                    insertRow.add(this.listOrderField, itAfter.next());
                    // Hey I might actually want to use an inner class here... ok array for now.
                    Object[] event = new Object[3];
                    event[0] = Insert;
                    event[1] = getInsertQuery();
                    event[2] = insertRow;
                    writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
                }
            } else {
                Iterator<Integer> itBefore = indexesBefore.iterator();
                Iterator<Integer> itAfter = indexesAfter.iterator();
                while (itBefore.hasNext() || itAfter.hasNext()) {
                    if (itBefore.hasNext()) {
                        if (itAfter.hasNext()) {
                            // update the object changing index from indexBefore to indexAfter
                            AbstractRecord updateAtIndexRow = writeQuery.getTranslationRow().clone();
                            updateAtIndexRow.add(getDirectField(), value);
                            updateAtIndexRow.add(this.listOrderField, itBefore.next());
                            // Hey I might actually want to use an inner class here... ok array for now.
                            Object[] event = new Object[4];
                            event[0] = UpdateAtIndex;
                            event[1] = updateAtIndexQuery;
                            event[2] = updateAtIndexRow;
                            DatabaseRecord modifyRow = new DatabaseRecord(1);
                            modifyRow.add(this.listOrderField, itAfter.next());
                            event[3] = modifyRow;
                            writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
                        } else {
                            // delete the object at indexBefore
                            AbstractRecord deleteAtIndexRow = writeQuery.getTranslationRow().clone();
                            deleteAtIndexRow.add(getDirectField(), value);
                            deleteAtIndexRow.add(this.listOrderField, itBefore.next());
                            // Hey I might actually want to use an inner class here... ok array for now.
                            Object[] event = new Object[3];
                            event[0] = DeleteAtIndex;
                            event[1] = deleteAtIndexQuery;
                            event[2] = deleteAtIndexRow;
                            writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
                        }
                    } else {
                        // itAfter.hasNext() must be true
                        // insert the object at indexAfter
                        AbstractRecord insertRow = writeQuery.getTranslationRow().clone();
                        insertRow.add(getDirectField(), value);
                        insertRow.add(this.listOrderField, itAfter.next());
                        // Hey I might actually want to use an inner class here... ok array for now.
                        Object[] event = new Object[3];
                        event[0] = Insert;
                        event[1] = getInsertQuery();
                        event[2] = insertRow;
                        writeQuery.getSession().getCommitManager().addDataModificationEvent(this, event);
                    }
                }
            }
        }
    }
}
Also used : ObjectChangeSet(org.eclipse.persistence.internal.sessions.ObjectChangeSet) Set(java.util.Set) HashSet(java.util.HashSet) DatabaseRecord(org.eclipse.persistence.sessions.DatabaseRecord) ObjectChangeSet(org.eclipse.persistence.internal.sessions.ObjectChangeSet) DeleteObjectQuery(org.eclipse.persistence.queries.DeleteObjectQuery) AbstractRecord(org.eclipse.persistence.internal.sessions.AbstractRecord) IndirectList(org.eclipse.persistence.indirection.IndirectList) DatabaseField(org.eclipse.persistence.internal.helper.DatabaseField) Iterator(java.util.Iterator) DescriptorIterator(org.eclipse.persistence.internal.descriptors.DescriptorIterator) DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord) List(java.util.List) IndirectList(org.eclipse.persistence.indirection.IndirectList) ArrayList(java.util.ArrayList) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) HashMap(java.util.HashMap)

Example 3 with DirectCollectionChangeRecord

use of org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method compareCollectionsForChange.

/**
 * INTERNAL:
 * This method is used to calculate the differences between two collections.
 */
@Override
public void compareCollectionsForChange(Object oldCollection, Object newCollection, ChangeRecord changeRecord, AbstractSession session) {
    if (this.listOrderField != null) {
        compareListsForChange((List) oldCollection, (List) newCollection, changeRecord, session);
        return;
    }
    ContainerPolicy cp = getContainerPolicy();
    int numberOfNewNulls = 0;
    HashMap originalKeyValues = new HashMap(10);
    HashMap cloneKeyValues = new HashMap(10);
    if (oldCollection != null) {
        Object backUpIter = cp.iteratorFor(oldCollection);
        while (cp.hasNext(backUpIter)) {
            // Make a lookup of the objects
            Object secondObject = cp.next(backUpIter, session);
            // For CR#2258/CR#2378 handle null values inserted in a collection.
            if (secondObject == null) {
                numberOfNewNulls--;
            } else {
                Integer count = (Integer) originalKeyValues.get(secondObject);
                if (count == null) {
                    originalKeyValues.put(secondObject, 1);
                } else {
                    originalKeyValues.put(secondObject, count + 1);
                }
            }
        }
    }
    // should a removal occur this is the original count of objects on the database.
    // this value is used to determine how many objects to re-insert after the delete as a
    // delete will delete all of the objects not just one.
    HashMap databaseCount = (HashMap) originalKeyValues.clone();
    int databaseNullCount = Math.abs(numberOfNewNulls);
    if (newCollection != null) {
        Object cloneIter = cp.iteratorFor(newCollection);
        /* The following code is used to compare objects in a direct collection.
               Because objects in a direct collection are primitives and may be the same object
               the following code must count the number of instances in the collection not just the
               existence of an object.
            */
        while (cp.hasNext(cloneIter)) {
            // Compare them with the objects from the clone
            Object firstObject = cp.next(cloneIter, session);
            // For CR#2258/CR#2378 handle null values inserted in a collection.
            if (firstObject == null) {
                numberOfNewNulls++;
            } else {
                Integer count = (Integer) originalKeyValues.get(firstObject);
                if (count == null) {
                    // the object was not in the backup
                    Integer cloneCount = (Integer) cloneKeyValues.get(firstObject);
                    // Add it to the additions hashtable
                    if (cloneCount == null) {
                        cloneKeyValues.put(firstObject, 1);
                    } else {
                        cloneKeyValues.put(firstObject, cloneCount + 1);
                    }
                } else if (count == 1) {
                    // There is only one object so remove the whole reference
                    originalKeyValues.remove(firstObject);
                } else {
                    originalKeyValues.put(firstObject, count - 1);
                }
            }
        }
    }
    if (cloneKeyValues.isEmpty() && originalKeyValues.isEmpty() && (numberOfNewNulls == 0) && (!changeRecord.getOwner().isNew())) {
        return;
    }
    ((DirectCollectionChangeRecord) changeRecord).clearChanges();
    ((DirectCollectionChangeRecord) changeRecord).addAdditionChange(cloneKeyValues, databaseCount);
    ((DirectCollectionChangeRecord) changeRecord).addRemoveChange(originalKeyValues, databaseCount);
    ((DirectCollectionChangeRecord) changeRecord).setIsDeferred(false);
    ((DirectCollectionChangeRecord) changeRecord).setLatestCollection(null);
    // For CR#2258, produce a changeRecord which reflects the addition and removal of null values.
    if (numberOfNewNulls != 0) {
        ((DirectCollectionChangeRecord) changeRecord).getCommitAddMap().put(null, databaseNullCount);
        if (numberOfNewNulls > 0) {
            ((DirectCollectionChangeRecord) changeRecord).addAdditionChange(null, numberOfNewNulls);
        } else {
            numberOfNewNulls *= -1;
            ((DirectCollectionChangeRecord) changeRecord).addRemoveChange(null, numberOfNewNulls);
        }
    }
}
Also used : OrderedListContainerPolicy(org.eclipse.persistence.internal.queries.OrderedListContainerPolicy) ContainerPolicy(org.eclipse.persistence.internal.queries.ContainerPolicy) IdentityHashMap(java.util.IdentityHashMap) HashMap(java.util.HashMap) DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord)

Example 4 with DirectCollectionChangeRecord

use of org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method calculateDeferredChanges.

/**
 * INTERNAL:
 * Used by AttributeLevelChangeTracking to update a changeRecord with calculated changes
 * as apposed to detected changes.  If an attribute can not be change tracked it's
 * changes can be detected through this process.
 */
@Override
public void calculateDeferredChanges(ChangeRecord changeRecord, AbstractSession session) {
    DirectCollectionChangeRecord collectionRecord = (DirectCollectionChangeRecord) changeRecord;
    // TODO: Handle events that fired after collection was replaced.
    compareCollectionsForChange(collectionRecord.getOriginalCollection(), collectionRecord.getLatestCollection(), collectionRecord, session);
}
Also used : DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord)

Example 5 with DirectCollectionChangeRecord

use of org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method mergeIntoObject.

/**
 * INTERNAL:
 * Merge changes from the source to the target object.
 */
@Override
public void mergeIntoObject(Object target, boolean isTargetUnInitialized, Object source, MergeManager mergeManager, AbstractSession targetSession) {
    if (this.descriptor.getCachePolicy().isProtectedIsolation() && !this.isCacheable && !targetSession.isProtectedSession()) {
        setAttributeValueInObject(target, this.indirectionPolicy.buildIndirectObject(new ValueHolder<>(null)));
        return;
    }
    if (isTargetUnInitialized) {
        // This will happen if the target object was removed from the cache before the commit was attempted
        if (mergeManager.shouldMergeWorkingCopyIntoOriginal() && (!isAttributeValueInstantiated(source))) {
            setAttributeValueInObject(target, getIndirectionPolicy().getOriginalIndirectionObject(getAttributeValueFromObject(source), targetSession));
            return;
        }
    }
    if (!shouldMergeCascadeReference(mergeManager)) {
        // This is only going to happen on mergeClone, and we should not attempt to merge the reference
        return;
    }
    if (mergeManager.shouldRefreshRemoteObject() && usesIndirection()) {
        mergeRemoteValueHolder(target, source, mergeManager);
        return;
    }
    if (mergeManager.isForRefresh()) {
        if (!isAttributeValueInstantiated(target)) {
            // the refresh that attribute
            return;
        }
    } else if (!isAttributeValueInstantiatedOrChanged(source)) {
        // modified
        return;
    }
    ContainerPolicy containerPolicy = getContainerPolicy();
    Object valueOfSource = getRealCollectionAttributeValueFromObject(source, mergeManager.getSession());
    // trigger instantiation of target attribute
    Object valueOfTarget = getRealCollectionAttributeValueFromObject(target, mergeManager.getSession());
    Object newContainer = containerPolicy.containerInstance(containerPolicy.sizeFor(valueOfSource));
    boolean fireCollectionChangeEvents = false;
    boolean firePropertyChangeEvent = false;
    ObjectChangeListener listener = null;
    if ((this.descriptor.getObjectChangePolicy().isObjectChangeTrackingPolicy()) && (target instanceof ChangeTracker) && (((ChangeTracker) target)._persistence_getPropertyChangeListener() != null)) {
        listener = (ObjectChangeListener) ((ChangeTracker) target)._persistence_getPropertyChangeListener();
        if (this.listOrderField == null) {
            fireCollectionChangeEvents = true;
            // Collections may not be indirect list or may have been replaced with user collection.
            Object iterator = containerPolicy.iteratorFor(valueOfTarget);
            // remove does not seem to use index.
            Integer zero = 0;
            while (containerPolicy.hasNext(iterator)) {
                // Bug304251: let the containerPolicy build the proper remove CollectionChangeEvent
                CollectionChangeEvent event = containerPolicy.createChangeEvent(target, getAttributeName(), valueOfTarget, containerPolicy.next(iterator, mergeManager.getSession()), CollectionChangeEvent.REMOVE, zero, false);
                listener.internalPropertyChange(event);
            }
            if (newContainer instanceof ChangeTracker) {
                ((ChangeTracker) newContainer)._persistence_setPropertyChangeListener(((ChangeTracker) target)._persistence_getPropertyChangeListener());
            }
            if (valueOfTarget instanceof ChangeTracker) {
                // remove listener
                ((ChangeTracker) valueOfTarget)._persistence_setPropertyChangeListener(null);
            }
        } else {
            firePropertyChangeEvent = true;
        }
    }
    Object originalValueOfTarget = valueOfTarget;
    valueOfTarget = newContainer;
    int i = 0;
    for (Object sourceValuesIterator = containerPolicy.iteratorFor(valueOfSource); containerPolicy.hasNext(sourceValuesIterator); ) {
        Object sourceValue = containerPolicy.next(sourceValuesIterator, mergeManager.getSession());
        if (fireCollectionChangeEvents) {
            // Bug304251: let the containerPolicy build the proper remove CollectionChangeEvent
            CollectionChangeEvent event = containerPolicy.createChangeEvent(target, getAttributeName(), valueOfTarget, sourceValue, CollectionChangeEvent.ADD, i, false);
            listener.internalPropertyChange(event);
        }
        containerPolicy.addInto(sourceValue, valueOfTarget, mergeManager.getSession());
        i++;
    }
    if (fireCollectionChangeEvents && (this.descriptor.getObjectChangePolicy().isAttributeChangeTrackingPolicy())) {
        // check that there were changes, if not then remove the record.
        ObjectChangeSet changeSet = ((AttributeChangeListener) ((ChangeTracker) target)._persistence_getPropertyChangeListener()).getObjectChangeSet();
        if (changeSet != null) {
            DirectCollectionChangeRecord changeRecord = (DirectCollectionChangeRecord) changeSet.getChangesForAttributeNamed(getAttributeName());
            if (changeRecord != null) {
                if (!changeRecord.isDeferred()) {
                    if (!changeRecord.hasChanges()) {
                        changeSet.removeChange(getAttributeName());
                    }
                } else {
                    // Must reset the latest collection.
                    changeRecord.setLatestCollection(valueOfTarget);
                }
            }
        }
    }
    if (firePropertyChangeEvent) {
        ((ObjectChangeListener) ((ChangeTracker) target)._persistence_getPropertyChangeListener()).internalPropertyChange(new PropertyChangeEvent(target, getAttributeName(), originalValueOfTarget, valueOfTarget));
        if (valueOfTarget instanceof ChangeTracker) {
            ((ChangeTracker) valueOfTarget)._persistence_setPropertyChangeListener(((ChangeTracker) target)._persistence_getPropertyChangeListener());
        }
        if (originalValueOfTarget instanceof ChangeTracker) {
            // remove listener
            ((ChangeTracker) originalValueOfTarget)._persistence_setPropertyChangeListener(null);
        }
    }
    // Must re-set variable to allow for set method to re-morph changes if the collection is not being stored directly.
    setRealAttributeValueInObject(target, valueOfTarget);
}
Also used : PropertyChangeEvent(java.beans.PropertyChangeEvent) ObjectChangeSet(org.eclipse.persistence.internal.sessions.ObjectChangeSet) ObjectChangeListener(org.eclipse.persistence.internal.descriptors.changetracking.ObjectChangeListener) ValueHolder(org.eclipse.persistence.indirection.ValueHolder) ChangeTracker(org.eclipse.persistence.descriptors.changetracking.ChangeTracker) OrderedListContainerPolicy(org.eclipse.persistence.internal.queries.OrderedListContainerPolicy) ContainerPolicy(org.eclipse.persistence.internal.queries.ContainerPolicy) AttributeChangeListener(org.eclipse.persistence.internal.descriptors.changetracking.AttributeChangeListener) CollectionChangeEvent(org.eclipse.persistence.descriptors.changetracking.CollectionChangeEvent) DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord)

Aggregations

DirectCollectionChangeRecord (org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord)11 IndirectList (org.eclipse.persistence.indirection.IndirectList)4 ObjectChangeSet (org.eclipse.persistence.internal.sessions.ObjectChangeSet)4 ArrayList (java.util.ArrayList)3 HashMap (java.util.HashMap)3 IdentityHashMap (java.util.IdentityHashMap)3 List (java.util.List)3 ContainerPolicy (org.eclipse.persistence.internal.queries.ContainerPolicy)3 OrderedListContainerPolicy (org.eclipse.persistence.internal.queries.OrderedListContainerPolicy)3 HashSet (java.util.HashSet)2 Iterator (java.util.Iterator)2 Set (java.util.Set)2 ValueHolder (org.eclipse.persistence.indirection.ValueHolder)2 DescriptorIterator (org.eclipse.persistence.internal.descriptors.DescriptorIterator)2 DatabaseField (org.eclipse.persistence.internal.helper.DatabaseField)2 AbstractRecord (org.eclipse.persistence.internal.sessions.AbstractRecord)2 PropertyChangeEvent (java.beans.PropertyChangeEvent)1 Map (java.util.Map)1 ChangeTracker (org.eclipse.persistence.descriptors.changetracking.ChangeTracker)1 CollectionChangeEvent (org.eclipse.persistence.descriptors.changetracking.CollectionChangeEvent)1