Search in sources :

Example 1 with IndirectList

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

the class AggregateCollectionMapping method compareListsAndWrite_NonUpdatableListOrderField.

/**
 * INTERNAL:
 * Old and new lists are compared and only the changes are written to the database.
 * Called only if listOrderField != null
 */
protected void compareListsAndWrite_NonUpdatableListOrderField(List previousList, List currentList, WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
    boolean shouldRepairOrder = false;
    if (currentList instanceof IndirectList) {
        shouldRepairOrder = ((IndirectList) currentList).isListOrderBrokenInDb();
    }
    HashMap<Object, Object[]> previousAndCurrentByKey = new HashMap<>();
    int pkSize = getReferenceDescriptor().getPrimaryKeyFields().size();
    // First index the current objects by their primary key.
    for (int i = 0; i < currentList.size(); i++) {
        Object currentObject = currentList.get(i);
        try {
            CacheId primaryKey = (CacheId) getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
            primaryKey.add(i);
            Object[] previousAndCurrent = new Object[] { null, currentObject };
            previousAndCurrentByKey.put(primaryKey, previousAndCurrent);
        } catch (NullPointerException e) {
            // ideally the customer should check for these themselves.
            if (currentObject != null) {
                throw e;
            }
        }
    }
    if (shouldRepairOrder) {
        DeleteAllQuery deleteAllQuery = (DeleteAllQuery) this.deleteAllQuery;
        if (this.isCascadeOnDeleteSetOnDatabase) {
            deleteAllQuery = (DeleteAllQuery) deleteAllQuery.clone();
            deleteAllQuery.setIsInMemoryOnly(false);
        }
        deleteAllQuery.executeDeleteAll(query.getSession().getSessionForClass(getReferenceClass()), query.getTranslationRow(), new Vector(previousList));
    } else {
        // Next index the previous objects (read from db or from backup in uow)
        for (int i = 0; i < previousList.size(); i++) {
            Object previousObject = previousList.get(i);
            CacheId primaryKey = (CacheId) getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
            primaryKey.add(i);
            Object[] previousAndCurrent = previousAndCurrentByKey.get(primaryKey);
            if (previousAndCurrent == null) {
                // there's no current object - that means that previous object should be deleted
                DatabaseRecord extraData = new DatabaseRecord(1);
                extraData.put(this.listOrderField, i);
                objectRemovedDuringUpdate(query, previousObject, extraData);
            } else {
                previousAndCurrent[0] = previousObject;
            }
        }
    }
    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];
        // currentObject is not null
        Object currentObject = previousAndCurrent[1];
        if (previousObject == null) {
            // there's no previous object - that means that current object should be added.
            // index of currentObject in currentList
            int iCurrent = (Integer) ((CacheId) key).getPrimaryKey()[pkSize];
            DatabaseRecord extraData = new DatabaseRecord(1);
            extraData.put(this.listOrderField, iCurrent);
            objectAddedDuringUpdate(query, currentObject, null, extraData);
        } else {
            if (!this.isEntireObjectPK) {
                objectUnchangedDuringUpdate(query, currentObject, previousObject);
            }
        }
    }
    if (shouldRepairOrder) {
        ((IndirectList) currentList).setIsListOrderBrokenInDb(false);
    }
}
Also used : DeleteAllQuery(org.eclipse.persistence.queries.DeleteAllQuery) DatabaseRecord(org.eclipse.persistence.sessions.DatabaseRecord) HashMap(java.util.HashMap) IndirectList(org.eclipse.persistence.indirection.IndirectList) CacheId(org.eclipse.persistence.internal.identitymaps.CacheId) Vector(java.util.Vector) NonSynchronizedVector(org.eclipse.persistence.internal.helper.NonSynchronizedVector) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Example 2 with IndirectList

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

the class OrderedListContainerPolicy method mergeChanges.

/**
 * 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 change set.
 */
@Override
protected void mergeChanges(CollectionChangeRecord changeRecord, Object valueOfTarget, boolean shouldMergeCascadeParts, MergeManager mergeManager, AbstractSession targetSession) {
    ObjectChangeSet objectChanges;
    if (changeRecord.orderHasBeenRepaired() && valueOfTarget instanceof IndirectList) {
        ((IndirectList) valueOfTarget).setIsListOrderBrokenInDb(false);
    }
    if (!changeRecord.getOrderedChangeObjectList().isEmpty()) {
        Iterator<OrderedChangeObject> objects = changeRecord.getOrderedChangeObjectList().iterator();
        while (objects.hasNext()) {
            OrderedChangeObject changeObject = objects.next();
            objectChanges = changeObject.getChangeSet();
            if (changeObject.getChangeType() == CollectionChangeEvent.REMOVE) {
                boolean objectRemoved = changeRecord.getRemoveObjectList().containsKey(objectChanges);
                Object objectToRemove = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
                // This should not happen unless identity is lost.
                if (objectToRemove != null) {
                    Integer index = changeObject.getIndex();
                    if (index != null) {
                        if (objectToRemove.equals(get(index, valueOfTarget, mergeManager.getSession()))) {
                            removeFromAtIndex(index, valueOfTarget);
                        } else {
                            // Object is in the cache, but the collection doesn't have it at the location we expect
                            // Collection is invalid with respect to these changes, so invalidate the parent and abort
                            Object key = changeRecord.getOwner().getId();
                            targetSession.getIdentityMapAccessor().invalidateObject(key, changeRecord.getOwner().getClassType(targetSession));
                            return;
                        }
                    } else {
                        removeFrom(objectToRemove, valueOfTarget, targetSession);
                    }
                    if ((!mergeManager.shouldMergeChangesIntoDistributedCache()) && changeRecord.getMapping().isPrivateOwned()) {
                        // Check that the object was actually removed and not moved.
                        if (objectRemoved) {
                            mergeManager.registerRemovedNewObjectIfRequired(objectChanges.getUnitOfWorkClone());
                        }
                    }
                }
            } else {
                // getChangeType == add
                boolean objectAdded = changeRecord.getAddObjectList().containsKey(objectChanges);
                Object object = null;
                // The object was actually added and not moved.
                if (objectAdded && shouldMergeCascadeParts) {
                    object = mergeCascadeParts(objectChanges, mergeManager, targetSession);
                }
                if (object == null) {
                    // Retrieve the object to be added to the collection.
                    object = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
                }
                // not moved.
                if (objectAdded && mergeManager.shouldMergeChangesIntoDistributedCache()) {
                    // during merge into distributed cache
                    if (!contains(object, valueOfTarget, mergeManager.getSession())) {
                        addIntoAtIndex(changeObject.getIndex(), object, valueOfTarget, mergeManager.getSession());
                    }
                } else {
                    addIntoAtIndex(changeObject.getIndex(), object, valueOfTarget, targetSession);
                }
            }
        }
    } else {
        // Deferred change tracking merge behavior
        // Step 1 - iterate over the removed changes and remove them from the container.
        List<Integer> removedIndices = changeRecord.getOrderedRemoveObjectIndices();
        if (removedIndices.isEmpty()) {
            // Check if we have removed objects via a
            // simpleRemoveFromCollectionChangeRecord API call.
            Iterator<ObjectChangeSet> removedObjects = changeRecord.getRemoveObjectList().keySet().iterator();
            while (removedObjects.hasNext()) {
                objectChanges = removedObjects.next();
                removeFrom(objectChanges.getOldKey(), objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession), valueOfTarget, targetSession);
                registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
            }
        } else {
            for (int i = removedIndices.size() - 1; i >= 0; i--) {
                Integer index = removedIndices.get(i);
                objectChanges = (ObjectChangeSet) changeRecord.getOrderedRemoveObject(index);
                Object objectToRemove = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
                if ((objectToRemove != null) && (objectToRemove.equals(get(index, valueOfTarget, mergeManager.getSession())))) {
                    removeFromAtIndex(index, valueOfTarget);
                    // The object was actually removed and not moved.
                    if (changeRecord.getRemoveObjectList().containsKey(objectChanges)) {
                        registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
                    }
                } else {
                    // Object is either not in the cache, or not at the location we expect
                    // Collection is invalid with respect to these changes, so invalidate the parent and abort
                    Object key = changeRecord.getOwner().getId();
                    targetSession.getIdentityMapAccessor().invalidateObject(key, changeRecord.getOwner().getClassType(targetSession));
                    return;
                }
            }
        }
        // Step 2 - iterate over the added changes and add them to the container.
        for (ObjectChangeSet addChangeSet : changeRecord.getOrderedAddObjects()) {
            objectChanges = addChangeSet;
            boolean objectAdded = changeRecord.getAddObjectList().containsKey(objectChanges);
            Object object = null;
            // The object was actually added and not moved.
            if (objectAdded && shouldMergeCascadeParts) {
                object = mergeCascadeParts(objectChanges, mergeManager, targetSession);
            }
            if (object == null) {
                // Retrieve the object to be added to the collection.
                object = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
            }
            // not moved.
            if (objectAdded && mergeManager.shouldMergeChangesIntoDistributedCache()) {
                // during merge into distributed cache
                if (!contains(object, valueOfTarget, mergeManager.getSession())) {
                    addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, mergeManager.getSession());
                }
            } else {
                addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, targetSession);
            }
        }
    }
}
Also used : OrderedChangeObject(org.eclipse.persistence.internal.sessions.OrderedChangeObject) ObjectChangeSet(org.eclipse.persistence.internal.sessions.ObjectChangeSet) IndexedObject(org.eclipse.persistence.internal.helper.IndexedObject) OrderedChangeObject(org.eclipse.persistence.internal.sessions.OrderedChangeObject) IndirectList(org.eclipse.persistence.indirection.IndirectList)

Example 3 with IndirectList

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

the class OrderedListContainerPolicy method addAll.

protected boolean addAll(List elements, Object container, AbstractSession session, List<AbstractRecord> dbRows, ReadQuery query, CacheKey parentCacheKey) {
    int size = dbRows.size();
    if (this.elementDescriptor != null && this.elementDescriptor.getObjectBuilder().hasWrapperPolicy()) {
        ObjectBuilder objectBuilder = this.elementDescriptor.getObjectBuilder();
        List wrappedElements = new ArrayList(size);
        for (int i = 0; i < size; i++) {
            wrappedElements.add(objectBuilder.wrapObject(elements.get(i), session));
        }
        elements = wrappedElements;
    }
    ConversionManager conversionManager = session.getDatasourcePlatform().getConversionManager();
    // populate container with a dummy so the container.set(i, obj) could be used later.
    for (int i = 0; i < size; i++) {
        ((List) container).add(NOT_SET);
    }
    // insert the elements into container
    boolean failed = false;
    for (int i = 0; i < size; i++) {
        AbstractRecord row = dbRows.get(i);
        Object orderValue = row.get(this.listOrderField);
        // order value is null
        if (orderValue == null) {
            failed = true;
            break;
        }
        int intOrderValue = conversionManager.convertObject(orderValue, Integer.class);
        try {
            // one or more elements have the same order value
            if (NOT_SET != ((List) container).set(intOrderValue, elements.get(i))) {
                failed = true;
                break;
            }
        } catch (IndexOutOfBoundsException indexException) {
            // order value negative or greater/equal to size
            failed = true;
            break;
        }
    }
    if (failed) {
        ((List) container).clear();
        // extract order list - it will be set into exception or used by order correction.
        List<Integer> orderList = new ArrayList(size);
        for (int i = 0; i < size; i++) {
            AbstractRecord row = dbRows.get(i);
            Object orderValue = row.get(this.listOrderField);
            if (orderValue == null) {
                orderList.add(null);
            } else {
                orderList.add(conversionManager.convertObject(orderValue, Integer.class));
            }
        }
        if (this.orderCorrectionType == OrderCorrectionType.READ || this.orderCorrectionType == OrderCorrectionType.READ_WRITE) {
            // pair each element with its order index
            List<IndexedObject> indexedElements = new ArrayList(size);
            for (int i = 0; i < size; i++) {
                indexedElements.add(new IndexedObject(orderList.get(i), elements.get(i)));
            }
            // put elements in order and add to container
            ((List) container).addAll(correctOrderList(indexedElements));
            if (this.orderCorrectionType == OrderCorrectionType.READ_WRITE) {
                // mark IndirectList to have broken order
                ((IndirectList) container).setIsListOrderBrokenInDb(true);
            }
        } else {
            // this.orderCorrectionType == OrderCorrectionType.EXCEPTION
            throw QueryException.listOrderFieldWrongValue(query, this.listOrderField, orderList);
        }
    }
    return size > 0;
}
Also used : ArrayList(java.util.ArrayList) AbstractRecord(org.eclipse.persistence.internal.sessions.AbstractRecord) ObjectBuilder(org.eclipse.persistence.internal.descriptors.ObjectBuilder) IndirectList(org.eclipse.persistence.indirection.IndirectList) IndexedObject(org.eclipse.persistence.internal.helper.IndexedObject) ConversionManager(org.eclipse.persistence.internal.helper.ConversionManager) ArrayList(java.util.ArrayList) List(java.util.List) IndirectList(org.eclipse.persistence.indirection.IndirectList) IndexedObject(org.eclipse.persistence.internal.helper.IndexedObject) OrderedChangeObject(org.eclipse.persistence.internal.sessions.OrderedChangeObject)

Example 4 with IndirectList

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

the class SimpleFetchGroupTests method verifyUnfetchedAttributes.

@Test
public void verifyUnfetchedAttributes() throws Exception {
    EntityManager em = createEntityManager("fieldaccess");
    try {
        beginTransaction(em);
        TypedQuery<Employee> q = em.createQuery("SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(p.owner.id) FROM PhoneNumber p)", Employee.class);
        FetchGroup fg = new FetchGroup("Employee.empty");
        q.setHint(QueryHints.FETCH_GROUP, fg);
        Employee emp = q.getSingleResult();
        assertNotNull(emp);
        assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
        // This check using the mapping returns a default (empty) IndirectList
        /*OneToManyMapping phoneMapping = (OneToManyMapping) employeeDescriptor.getMappingForAttributeName("phoneNumbers");
            IndirectList phones = (IndirectList) phoneMapping.getAttributeValueFromObject(emp);
            assertNotNull(phones);
            assertTrue(phones.isInstantiated());
            assertEquals(0, phones.size());
            assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());*/
        IndirectList phonesIL = (IndirectList) emp.getPhoneNumbers();
        assertFalse(phonesIL.isInstantiated());
        assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
        assertTrue(emp.getPhoneNumbers().size() > 0);
        assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
    } finally {
        if (isTransactionActive(em)) {
            rollbackTransaction(em);
        }
        closeEntityManager(em);
    }
}
Also used : EntityManager(jakarta.persistence.EntityManager) Employee(org.eclipse.persistence.testing.models.jpa.fieldaccess.advanced.Employee) FetchGroup(org.eclipse.persistence.queries.FetchGroup) IndirectList(org.eclipse.persistence.indirection.IndirectList) Test(org.junit.Test)

Example 5 with IndirectList

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

the class SimpleFetchGroupTests method verifyUnfetchedAttributes.

@Test
public void verifyUnfetchedAttributes() throws Exception {
    EntityManager em = createEntityManager();
    try {
        beginTransaction(em);
        TypedQuery<Employee> q = em.createQuery("SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(p.id) FROM PhoneNumber p)", Employee.class);
        FetchGroup fg = new FetchGroup("Employee.empty");
        q.setHint(QueryHints.FETCH_GROUP, fg);
        Employee emp = q.getSingleResult();
        assertNotNull(emp);
        assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
        // This check using the mapping returns a default (empty) IndirectList
        /*OneToManyMapping phoneMapping = (OneToManyMapping) employeeDescriptor.getMappingForAttributeName("phoneNumbers");
            IndirectList phones = (IndirectList) phoneMapping.getAttributeValueFromObject(emp);
            assertNotNull(phones);
            assertTrue(phones.isInstantiated());
            assertEquals(0, phones.size());
            assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());*/
        IndirectList phonesIL = (IndirectList) emp.getPhoneNumbers();
        assertFalse(phonesIL.isInstantiated());
        assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
        assertTrue(emp.getPhoneNumbers().size() > 0);
        assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
    } finally {
        if (isTransactionActive(em)) {
            rollbackTransaction(em);
        }
        closeEntityManager(em);
    }
}
Also used : EntityManager(jakarta.persistence.EntityManager) Employee(org.eclipse.persistence.testing.models.jpa.advanced.Employee) FetchGroup(org.eclipse.persistence.queries.FetchGroup) IndirectList(org.eclipse.persistence.indirection.IndirectList) Test(org.junit.Test)

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