Search in sources :

Example 1 with BatchFetchPolicy

use of org.eclipse.persistence.queries.BatchFetchPolicy in project eclipselink by eclipse-ee4j.

the class ForeignReferenceMapping method extractResultFromBatchQuery.

/**
 * INTERNAL:
 * Extract the value from the batch optimized query, this should be supported by most query types.
 */
public Object extractResultFromBatchQuery(ReadQuery batchQuery, CacheKey parentCacheKey, AbstractRecord sourceRow, AbstractSession session, ObjectLevelReadQuery originalQuery) throws QueryException {
    Map<Object, Object> batchedObjects;
    Object result;
    Object sourceKey = extractBatchKeyFromRow(sourceRow, session);
    if (sourceKey == null) {
        // If the foreign key was null, then just return null.
        return null;
    }
    Object cachedObject = checkCacheForBatchKey(sourceRow, sourceKey, null, batchQuery, originalQuery, session);
    if (cachedObject != null) {
        // If the object is already in the cache, then just return it.
        return cachedObject;
    }
    // Ensure the query is only executed once.
    synchronized (batchQuery) {
        // Check if query was already executed.
        batchedObjects = batchQuery.getBatchObjects();
        BatchFetchPolicy originalPolicy = originalQuery.getBatchFetchPolicy();
        if (batchedObjects == null) {
            batchedObjects = new Hashtable<>();
            batchQuery.setBatchObjects(batchedObjects);
        } else {
            result = batchedObjects.get(sourceKey);
            if (result == Helper.NULL_VALUE) {
                return null;
            // If IN may not have that batch yet, or it may have been null.
            } else if ((result != null) || (!originalPolicy.isIN())) {
                return result;
            }
        }
        // In case of IN the batch including this row may not have been executed yet.
        AbstractRecord translationRow = originalQuery.getTranslationRow();
        if (translationRow == null) {
            translationRow = new DatabaseRecord();
        }
        // Execute query and index resulting object sets by key.
        if (originalPolicy.isIN()) {
            // Need to extract all foreign key values from all parent rows for IN parameter.
            List<AbstractRecord> parentRows = originalPolicy.getDataResults(this);
            // Execute queries by batch if too many rows.
            int rowsSize = parentRows.size();
            int size = Math.min(rowsSize, originalPolicy.getSize());
            if (size == 0) {
                return null;
            }
            int startIndex = 0;
            if (size != rowsSize) {
                // If only fetching a page, need to make sure the row we want is in the page.
                startIndex = parentRows.indexOf(sourceRow);
            }
            if (startIndex == -1) {
                return null;
            }
            List foreignKeyValues = new ArrayList(size);
            Set foreignKeys = new HashSet(size);
            int index = 0;
            int offset = startIndex;
            for (int count = 0; count < size; count++) {
                if (index >= rowsSize) {
                    // Processed all rows, done.
                    break;
                } else if ((offset + index) >= rowsSize) {
                    // If passed the end, go back to start.
                    offset = index * -1;
                }
                AbstractRecord row = parentRows.get(offset + index);
                // result of duplicate filtering being true for constructing the ComplexQueryResult
                if (row != null) {
                    Object foreignKey = extractBatchKeyFromRow(row, session);
                    if (foreignKey == null) {
                        // Ignore null foreign keys.
                        count--;
                    } else {
                        cachedObject = checkCacheForBatchKey(row, foreignKey, batchedObjects, batchQuery, originalQuery, session);
                        if (cachedObject != null) {
                            // Avoid fetching things a cache hit occurs for.
                            count--;
                        } else {
                            // Ensure the same id is not selected twice.
                            if (foreignKeys.contains(foreignKey)) {
                                count--;
                            } else {
                                Object[] key = ((CacheId) foreignKey).getPrimaryKey();
                                Object foreignKeyValue = key[0];
                                // Support composite keys using nested IN.
                                if (key.length > 1) {
                                    foreignKeyValue = Arrays.asList(key);
                                }
                                foreignKeyValues.add(foreignKeyValue);
                                foreignKeys.add(foreignKey);
                            }
                        }
                    }
                }
                index++;
            }
            // Need to compute remaining rows, this is tricky because a page in the middle could have been processed.
            List<AbstractRecord> remainingParentRows;
            if (startIndex == 0) {
                // Tail
                remainingParentRows = new ArrayList<>(parentRows.subList(index, rowsSize));
            } else if (startIndex == offset) {
                // Head and tail.
                remainingParentRows = new ArrayList<>(parentRows.subList(0, startIndex));
                remainingParentRows.addAll(parentRows.subList(startIndex + index, rowsSize));
            } else {
                // Middle
                remainingParentRows = new ArrayList<>(parentRows.subList(offset + index, startIndex));
            }
            originalPolicy.setDataResults(this, remainingParentRows);
            translationRow = translationRow.clone();
            translationRow.put(QUERY_BATCH_PARAMETER, foreignKeyValues);
            // Register each id as null, in case it has no relationship.
            for (Object foreignKey : foreignKeys) {
                batchedObjects.put(foreignKey, Helper.NULL_VALUE);
            }
        } else if (batchQuery.isReadAllQuery() && ((ReadAllQuery) batchQuery).getBatchFetchPolicy().isIN()) {
            throw QueryException.originalQueryMustUseBatchIN(this, originalQuery);
        }
        executeBatchQuery(batchQuery, parentCacheKey, batchedObjects, session, translationRow);
        batchQuery.setSession(null);
    }
    result = batchedObjects.get(sourceKey);
    if (result == Helper.NULL_VALUE) {
        return null;
    } else {
        return result;
    }
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) UnitOfWorkChangeSet(org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet) DatabaseRecord(org.eclipse.persistence.sessions.DatabaseRecord) AbstractRecord(org.eclipse.persistence.internal.sessions.AbstractRecord) ArrayList(java.util.ArrayList) CacheId(org.eclipse.persistence.internal.identitymaps.CacheId) BatchFetchPolicy(org.eclipse.persistence.queries.BatchFetchPolicy) List(java.util.List) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet)

Aggregations

ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Set (java.util.Set)1 CacheId (org.eclipse.persistence.internal.identitymaps.CacheId)1 AbstractRecord (org.eclipse.persistence.internal.sessions.AbstractRecord)1 UnitOfWorkChangeSet (org.eclipse.persistence.internal.sessions.UnitOfWorkChangeSet)1 BatchFetchPolicy (org.eclipse.persistence.queries.BatchFetchPolicy)1 DatabaseRecord (org.eclipse.persistence.sessions.DatabaseRecord)1