Search in sources :

Example 11 with IndirectList

use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.

the class AggregateCollectionMapping method compareListsAndWrite_UpdatableListOrderField.

/**
 * INTERNAL:
 * Old and new lists are compared and only the changes are written to the database.
 * Called only if listOrderField != null
 */
protected void compareListsAndWrite_UpdatableListOrderField(List previousList, List currentList, WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
    boolean shouldRepairOrder = false;
    if (currentList instanceof IndirectList) {
        shouldRepairOrder = ((IndirectList) currentList).isListOrderBrokenInDb();
    }
    // Object[] = {previousObject, currentObject, previousIndex, currentIndex}
    HashMap<Object, Object[]> previousAndCurrentByKey = new HashMap<>();
    // a SortedMap, current index mapped by previous index, both indexes must exist and be not equal.
    TreeMap<Integer, Integer> currentIndexByPreviousIndex = new TreeMap<>();
    // First index the current objects by their primary key.
    for (int i = 0; i < currentList.size(); i++) {
        Object currentObject = currentList.get(i);
        try {
            Object primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
            Object[] previousAndCurrent = new Object[] { null, currentObject, null, i };
            previousAndCurrentByKey.put(primaryKey, previousAndCurrent);
        } catch (NullPointerException e) {
            // ideally the customer should check for these themselves.
            if (currentObject != null) {
                throw e;
            }
        }
    }
    // Next index the previous objects (read from db or from backup in uow), also remove the objects to be removed.
    for (int i = 0; i < previousList.size(); i++) {
        Object previousObject = previousList.get(i);
        Object primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
        Object[] previousAndCurrent = previousAndCurrentByKey.get(primaryKey);
        if (previousAndCurrent == null) {
            // there's no current object - that means that previous object should be deleted
            objectRemovedDuringUpdate(query, previousObject, null);
        } else {
            previousAndCurrent[0] = previousObject;
            previousAndCurrent[2] = i;
            int iCurrent = (Integer) previousAndCurrent[3];
            if (i != iCurrent || shouldRepairOrder) {
                currentIndexByPreviousIndex.put(i, iCurrent);
            }
        }
    }
    // some order indexes should be changed
    if (!currentIndexByPreviousIndex.isEmpty()) {
        boolean shouldUpdateOrderUsingPk = shouldRepairOrder;
        if (!shouldUpdateOrderUsingPk) {
            // search for cycles in order changes, such as, for instance:
            // previous index  1, 2
            // current  index  2, 1
            // or
            // previous index  1, 3, 5
            // current  index  3, 5, 1
            // those objects order index can't be updated using their previous order index value - should use pk in where clause instead.
            // For now, if a cycle is found let's update all order indexes using pk.
            // Ideally that should be refined in the future so that only indexes participating in cycles updated using pks - others still through bulk update.
            boolean isCycleFound = false;
            int iCurrentMax = -1;
            Iterator<Integer> itCurrentIndexes = currentIndexByPreviousIndex.values().iterator();
            while (itCurrentIndexes.hasNext() && !isCycleFound) {
                int iCurrent = itCurrentIndexes.next();
                if (iCurrent > iCurrentMax) {
                    iCurrentMax = iCurrent;
                } else {
                    isCycleFound = true;
                }
            }
            shouldUpdateOrderUsingPk = isCycleFound;
        }
        if (shouldUpdateOrderUsingPk) {
            Iterator<Map.Entry<Object, Object[]>> it = previousAndCurrentByKey.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<Object, Object[]> entry = it.next();
                Object key = entry.getKey();
                Object[] previousAndCurrent = entry.getValue();
                // previousObject may be null, meaning currentObject has been added to the list
                Object previousObject = previousAndCurrent[0];
                if (previousObject != null) {
                    Object currentObject = previousAndCurrent[1];
                    if (!this.isEntireObjectPK) {
                        objectUnchangedDuringUpdate(query, currentObject, previousObject);
                    }
                    int iPrevious = (Integer) previousAndCurrent[2];
                    int iCurrent = (Integer) previousAndCurrent[3];
                    if (iPrevious != iCurrent || shouldRepairOrder) {
                        objectChangedListOrderDuringUpdate(query, key, iCurrent);
                    }
                }
            }
        } else {
            // update the objects - but not their order values
            if (!this.isEntireObjectPK) {
                Iterator<Map.Entry<Object, Object[]>> iterator = previousAndCurrentByKey.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Object, Object[]> entry = iterator.next();
                    Object[] previousAndCurrent = entry.getValue();
                    // previousObject may be null, meaning currentObject has been added to the list
                    Object previousObject = previousAndCurrent[0];
                    if (previousObject != null) {
                        Object currentObject = previousAndCurrent[1];
                        objectUnchangedDuringUpdate(query, currentObject, previousObject);
                    }
                }
            }
            // a bulk update query will be executed for each bunch of adjacent previous indexes from which current indexes could be obtained with a shift, for instance:
            // previous index  1, 2, 3
            // current  index  5, 6, 7
            // the sql will look like:
            // UPDATE ... SET ListOrderField = ListOrderField + 4 WHERE 1 <= ListOrderField AND ListOrderField <= 3 AND FK = ...
            int iMin = -1;
            int iMax = -1;
            int iShift = 0;
            // each index corresponds to a bunch of objects to be shifted
            ArrayList<Integer> iMinList = new ArrayList();
            ArrayList<Integer> iMaxList = new ArrayList();
            ArrayList<Integer> iShiftList = new ArrayList();
            Iterator<Map.Entry<Integer, Integer>> itEntries = currentIndexByPreviousIndex.entrySet().iterator();
            while (itEntries.hasNext()) {
                Map.Entry<Integer, Integer> entry = itEntries.next();
                int iPrevious = entry.getKey();
                int iCurrent = entry.getValue();
                if (iMin >= 0) {
                    // the shift should be the same for all indexes participating in bulk update
                    int iPreviousExpected = iMax + 1;
                    if (iPrevious == iPreviousExpected && iCurrent == iPreviousExpected + iShift) {
                        iMax++;
                    } else {
                        iMinList.add(iMin);
                        iMaxList.add(iMax);
                        iShiftList.add(iShift);
                        iMin = -1;
                    }
                }
                if (iMin == -1) {
                    // start defining a new bulk update - define iShift, iFirst, iLast
                    iMin = iPrevious;
                    iMax = iPrevious;
                    iShift = iCurrent - iPrevious;
                }
            }
            if (iMin >= 0) {
                iMinList.add(iMin);
                iMaxList.add(iMax);
                iShiftList.add(iShift);
            }
            // Look for the left-most and right-most bunches and update them first.
            while (!iMinList.isEmpty()) {
                int iMinLeft = previousList.size() + 1;
                int iMinRight = -1;
                int indexShiftLeft = -1;
                int indexShiftRight = -1;
                for (int i = 0; i < iMinList.size(); i++) {
                    iMin = iMinList.get(i);
                    iShift = iShiftList.get(i);
                    if (iShift < 0) {
                        if (iMin < iMinLeft) {
                            iMinLeft = iMin;
                            indexShiftLeft = i;
                        }
                    } else {
                        // iShift > 0
                        if (iMin > iMinRight) {
                            iMinRight = iMin;
                            indexShiftRight = i;
                        }
                    }
                }
                if (indexShiftLeft >= 0) {
                    objectChangedListOrderDuringUpdate(query, iMinList.get(indexShiftLeft), iMaxList.get(indexShiftLeft), iShiftList.get(indexShiftLeft));
                }
                if (indexShiftRight >= 0) {
                    objectChangedListOrderDuringUpdate(query, iMinList.get(indexShiftRight), iMaxList.get(indexShiftRight), iShiftList.get(indexShiftRight));
                }
                if (indexShiftLeft >= 0) {
                    iMinList.remove(indexShiftLeft);
                    iMaxList.remove(indexShiftLeft);
                    iShiftList.remove(indexShiftLeft);
                }
                if (indexShiftRight >= 0) {
                    iMinList.remove(indexShiftRight);
                    iMaxList.remove(indexShiftRight);
                    iShiftList.remove(indexShiftRight);
                }
            }
        }
    }
    // Add the new objects
    Iterator<Map.Entry<Object, Object[]>> iterator = previousAndCurrentByKey.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<Object, Object[]> entry = iterator.next();
        Object[] previousAndCurrent = entry.getValue();
        // previousObject may be null, meaning currentObject has been added to the list
        Object previousObject = previousAndCurrent[0];
        if (previousObject == null) {
            // there's no previous object - that means that current object should be added.
            // currentObject is not null
            Object currentObject = previousAndCurrent[1];
            // index of currentObject in currentList
            int iCurrent = (Integer) previousAndCurrent[3];
            DatabaseRecord extraData = new DatabaseRecord(1);
            extraData.put(this.listOrderField, iCurrent);
            objectAddedDuringUpdate(query, currentObject, null, extraData);
        }
    }
    if (shouldRepairOrder) {
        ((IndirectList) currentList).setIsListOrderBrokenInDb(false);
    }
}
Also used : DatabaseRecord(org.eclipse.persistence.sessions.DatabaseRecord) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) IndirectList(org.eclipse.persistence.indirection.IndirectList) TreeMap(java.util.TreeMap) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Example 12 with IndirectList

use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.

the class EntityManagerJUnitTestSuite method testForUOWInSharedCacheWithBatchQueryHint.

/**
 * This test ensures that the eclipselink.batch query hint works. It tests
 * two things.
 *
 * 1. That the batch read attribute is properly added to the queyr 2. That
 * the query will execute
 *
 * It does not do any verification that the batch reading feature actually
 * works. That is left for the batch reading testing to do.
 */
public void testForUOWInSharedCacheWithBatchQueryHint() {
    if (isOnServer()) {
        // Can not unwrap on WLS.
        return;
    }
    int id1 = 0;
    EntityManager em = createEntityManager();
    beginTransaction(em);
    Employee manager = new Employee();
    manager.setFirstName("Marvin");
    manager.setLastName("Malone");
    PhoneNumber number = new PhoneNumber("cell", "613", "888-8888");
    manager.addPhoneNumber(number);
    number = new PhoneNumber("home", "613", "888-8880");
    manager.addPhoneNumber(number);
    em.persist(manager);
    id1 = manager.getId();
    Employee emp = new Employee();
    emp.setFirstName("Melvin");
    emp.setLastName("Malone");
    emp.setManager(manager);
    manager.addManagedEmployee(emp);
    number = new PhoneNumber("cell", "613", "888-9888");
    emp.addPhoneNumber(number);
    number = new PhoneNumber("home", "613", "888-0880");
    emp.addPhoneNumber(number);
    em.persist(emp);
    emp = new Employee();
    emp.setFirstName("David");
    emp.setLastName("Malone");
    emp.setManager(manager);
    manager.addManagedEmployee(emp);
    number = new PhoneNumber("cell", "613", "888-9988");
    emp.addPhoneNumber(number);
    number = new PhoneNumber("home", "613", "888-0980");
    emp.addPhoneNumber(number);
    em.persist(emp);
    commitTransaction(em);
    closeEntityManager(em);
    clearCache();
    // org.eclipse.persistence.jpa.JpaQuery query =
    // (org.eclipse.persistence.jpa.JpaQuery)em.createQuery("SELECT e FROM Employee e WHERE e.lastName = 'Malone' order by e.firstName");
    em = createEntityManager();
    beginTransaction(em);
    org.eclipse.persistence.jpa.JpaQuery query = (org.eclipse.persistence.jpa.JpaQuery) em.createQuery("SELECT e FROM Employee e WHERE e.lastName = 'Malone' order by e.firstName");
    query.setHint(QueryHints.BATCH, "e.phoneNumbers");
    List resultList = query.getResultList();
    emp = (Employee) resultList.get(0);
    emp.setFirstName("somethingelse" + System.currentTimeMillis());
    // execute random other query
    em.createQuery("SELECT e FROM Employee e WHERE e.lastName = 'Malone'").getResultList();
    ((Employee) resultList.get(1)).getPhoneNumbers().hashCode();
    commitTransaction(em);
    try {
        emp = (Employee) JpaHelper.getEntityManager(em).getServerSession().getIdentityMapAccessor().getFromIdentityMap(emp);
        assertNotNull("Error, phone numbers is empty.  Not Batch Read", emp.getPhoneNumbers());
        assertFalse("PhoneNumbers was empty.  This should not be the case as the test created phone numbers", emp.getPhoneNumbers().isEmpty());
        assertTrue("Phonee numbers was not an indirectList", emp.getPhoneNumbers() instanceof IndirectList);
        assertNotNull("valueholder was null in triggered batch attribute", ((IndirectList) emp.getPhoneNumbers()).getValueHolder());
        QueryBasedValueHolder bvh = (QueryBasedValueHolder) ((IndirectList) emp.getPhoneNumbers()).getValueHolder();
        if (!usesSOP()) {
            if (!(bvh instanceof BatchValueHolder)) {
                fail("BatchValueHolder was expected, instead got " + bvh);
            }
        }
        if (bvh.getQuery() != null && bvh.getQuery().getSession() != null && bvh.getQuery().getSession().isUnitOfWork()) {
            fail("In Shared Cache a UOW was set within a BatchValueHolder's query object");
        }
        closeEntityManager(em);
    } finally {
        em = createEntityManager();
        beginTransaction(em);
        emp = em.find(Employee.class, id1);
        Iterator<Employee> it = emp.getManagedEmployees().iterator();
        while (it.hasNext()) {
            Employee managedEmp = it.next();
            it.remove();
            managedEmp.setManager(null);
            em.remove(managedEmp);
        }
        em.remove(emp);
        commitTransaction(em);
    }
}
Also used : BatchValueHolder(org.eclipse.persistence.internal.indirection.BatchValueHolder) IndirectList(org.eclipse.persistence.indirection.IndirectList) JpaQuery(org.eclipse.persistence.jpa.JpaQuery) JpaQuery(org.eclipse.persistence.jpa.JpaQuery) QueryBasedValueHolder(org.eclipse.persistence.internal.indirection.QueryBasedValueHolder) EntityManager(jakarta.persistence.EntityManager) JpaEntityManager(org.eclipse.persistence.jpa.JpaEntityManager) Employee(org.eclipse.persistence.testing.models.jpa.advanced.Employee) PhoneNumber(org.eclipse.persistence.testing.models.jpa.advanced.PhoneNumber) IndirectList(org.eclipse.persistence.indirection.IndirectList) ArrayList(java.util.ArrayList) List(java.util.List)

Example 13 with IndirectList

use of org.eclipse.persistence.indirection.IndirectList 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 14 with IndirectList

use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method simpleRemoveFromCollectionChangeRecord.

protected void simpleRemoveFromCollectionChangeRecord(Object objectToRemove, Integer index, boolean isSet, ObjectChangeSet changeSet, AbstractSession session, boolean isChangeApplied) {
    DirectCollectionChangeRecord collectionChangeRecord = (DirectCollectionChangeRecord) changeSet.getChangesForAttributeNamed(getAttributeName());
    if (collectionChangeRecord == null) {
        collectionChangeRecord = new DirectCollectionChangeRecord(changeSet);
        collectionChangeRecord.setAttribute(getAttributeName());
        collectionChangeRecord.setMapping(this);
        changeSet.addChange(collectionChangeRecord);
        Object collection = getRealAttributeValueFromObject(changeSet.getUnitOfWorkClone(), session);
        if (this.listOrderField != null) {
            List originalListCopy = new ArrayList((List) collection);
            // index is not null because IndirectList does remove through indexOf.
            if (isSet) {
                originalListCopy.set(index, objectToRemove);
            } else {
                originalListCopy.add(index, objectToRemove);
            }
            collectionChangeRecord.setOriginalCollection(originalListCopy);
            collectionChangeRecord.setLatestCollection(collection);
        } else {
            collectionChangeRecord.storeDatabaseCounts(collection, getContainerPolicy(), session);
            collectionChangeRecord.setFirstToRemoveAlreadyOutCollection(isChangeApplied);
            if (isSet) {
                collectionChangeRecord.setFirstToAddAlreadyInCollection(isChangeApplied);
            }
        }
    }
    if (!collectionChangeRecord.isDeferred() && this.listOrderField == null) {
        collectionChangeRecord.addRemoveChange(objectToRemove, 1);
    }
}
Also used : ArrayList(java.util.ArrayList) DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord) List(java.util.List) IndirectList(org.eclipse.persistence.indirection.IndirectList) ArrayList(java.util.ArrayList)

Example 15 with IndirectList

use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.

the class DirectCollectionMapping method mergeChangesIntoObject.

/**
 * INTERNAL:
 * Merge changes from the source to the target object.
 * Because this is a collection mapping, values are added to or removed from the
 * collection based on the changeset
 */
@Override
public void mergeChangesIntoObject(Object target, ChangeRecord changeRecord, Object source, MergeManager mergeManager, AbstractSession targetSession) {
    if (this.descriptor.getCachePolicy().isProtectedIsolation() && !this.isCacheable && !targetSession.isProtectedSession()) {
        setAttributeValueInObject(target, this.indirectionPolicy.buildIndirectObject(new ValueHolder<>(null)));
        return;
    }
    ContainerPolicy containerPolicy = getContainerPolicy();
    Object valueOfTarget = null;
    AbstractSession session = mergeManager.getSession();
    DirectCollectionChangeRecord directCollectionChangeRecord = (DirectCollectionChangeRecord) changeRecord;
    // Check to see if the target has an instantiated collection
    if ((isAttributeValueInstantiated(target)) && (!changeRecord.getOwner().isNew())) {
        if (isSynchronizeOnMerge) {
            valueOfTarget = getRealCollectionAttributeValueFromObject(target, session);
        } else {
            valueOfTarget = containerPolicy.cloneFor(getRealCollectionAttributeValueFromObject(target, session));
        }
    } else {
        // if not create an instance of the collection
        valueOfTarget = containerPolicy.containerInstance(directCollectionChangeRecord.getAddObjectMap().size());
    }
    if (!isAttributeValueInstantiated(target)) {
        if (mergeManager.shouldMergeChangesIntoDistributedCache()) {
            return;
        }
        for (Object iterator = containerPolicy.iteratorFor(getRealCollectionAttributeValueFromObject(source, session)); containerPolicy.hasNext(iterator); ) {
            containerPolicy.addInto(containerPolicy.next(iterator, session), valueOfTarget, session);
        }
    } else {
        Object synchronizationTarget = valueOfTarget;
        // not the wrapper as the clone synchs on the delegate, see bug#5685287.
        if (valueOfTarget instanceof IndirectCollection) {
            synchronizationTarget = ((IndirectCollection) valueOfTarget).getDelegateObject();
            if (((DirectCollectionChangeRecord) changeRecord).orderHasBeenRepaired() && (valueOfTarget instanceof IndirectList)) {
                ((IndirectList) valueOfTarget).setIsListOrderBrokenInDb(false);
            }
        }
        if (isSynchronizeOnMerge) {
            synchronized (synchronizationTarget) {
                mergeAddRemoveChanges(valueOfTarget, synchronizationTarget, directCollectionChangeRecord, mergeManager, session);
            }
        } else {
            mergeAddRemoveChanges(valueOfTarget, synchronizationTarget, directCollectionChangeRecord, mergeManager, session);
        }
    }
    setRealAttributeValueInObject(target, valueOfTarget);
}
Also used : OrderedListContainerPolicy(org.eclipse.persistence.internal.queries.OrderedListContainerPolicy) ContainerPolicy(org.eclipse.persistence.internal.queries.ContainerPolicy) DirectCollectionChangeRecord(org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord) ValueHolder(org.eclipse.persistence.indirection.ValueHolder) IndirectCollection(org.eclipse.persistence.indirection.IndirectCollection) IndirectList(org.eclipse.persistence.indirection.IndirectList) AbstractSession(org.eclipse.persistence.internal.sessions.AbstractSession)

Aggregations

IndirectList (org.eclipse.persistence.indirection.IndirectList)15 EntityManager (jakarta.persistence.EntityManager)7 ArrayList (java.util.ArrayList)7 List (java.util.List)5 JpaEntityManager (org.eclipse.persistence.jpa.JpaEntityManager)4 FetchGroup (org.eclipse.persistence.queries.FetchGroup)4 Test (org.junit.Test)4 HashMap (java.util.HashMap)3 Map (java.util.Map)3 DirectCollectionChangeRecord (org.eclipse.persistence.internal.sessions.DirectCollectionChangeRecord)3 DatabaseRecord (org.eclipse.persistence.sessions.DatabaseRecord)3 Employee (org.eclipse.persistence.testing.models.jpa.advanced.Employee)3 TreeMap (java.util.TreeMap)2 IndexedObject (org.eclipse.persistence.internal.helper.IndexedObject)2 BatchValueHolder (org.eclipse.persistence.internal.indirection.BatchValueHolder)2 ContainerPolicy (org.eclipse.persistence.internal.queries.ContainerPolicy)2 EntityFetchGroup (org.eclipse.persistence.internal.queries.EntityFetchGroup)2 AbstractRecord (org.eclipse.persistence.internal.sessions.AbstractRecord)2 ObjectChangeSet (org.eclipse.persistence.internal.sessions.ObjectChangeSet)2 OrderedChangeObject (org.eclipse.persistence.internal.sessions.OrderedChangeObject)2