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;
}
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);
}
}
}
}
}
}
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);
}
}
}
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);
}
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);
}
Aggregations