/***************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. ****************************************************************/ package org.apache.cayenne.access; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.PersistenceState; import org.apache.cayenne.Persistent; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.query.EntityResultSegment; import org.apache.cayenne.query.ObjectIdQuery; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.RefreshQuery; import org.apache.cayenne.util.ListResponse; import org.apache.cayenne.util.ObjectContextQueryAction; /** * A DataContext-specific version of * {@link org.apache.cayenne.util.ObjectContextQueryAction}. * * @since 1.2 */ class DataContextQueryAction extends ObjectContextQueryAction { protected DataContext actingDataContext; public DataContextQueryAction(DataContext actingContext, ObjectContext targetContext, Query query) { super(actingContext, targetContext, query); actingDataContext = actingContext; } @Override protected boolean interceptInternalQuery() { return interceptObjectFromDataRowsQuery(); } private boolean interceptObjectFromDataRowsQuery() { if (query instanceof ObjectsFromDataRowsQuery) { ObjectsFromDataRowsQuery objectsFromDataRowsQuery = (ObjectsFromDataRowsQuery) query; response = new ListResponse(actingDataContext.objectsFromDataRows( objectsFromDataRowsQuery.getDescriptor(), objectsFromDataRowsQuery.getDataRows())); return DONE; } return !DONE; } /** * Overrides super implementation to property handle data row fetches. */ @Override protected boolean interceptOIDQuery() { if (query instanceof ObjectIdQuery) { ObjectIdQuery oidQuery = (ObjectIdQuery) query; if (!oidQuery.isFetchMandatory()) { Object object = polymorphicObjectFromCache(oidQuery.getObjectId()); if (object != null) { // TODO: andrus, 10/14/2006 - obtaining a row from an object is the // only piece that makes this method different from the super // implementation. This is used in NEW objects sorting on insert. It // would be nice to implement an alternative algorithm that wouldn't // require this hack. if (oidQuery.isFetchingDataRows()) { object = actingDataContext.currentSnapshot((Persistent) object); } // do not return hollow objects else if (((Persistent) object).getPersistenceState() == PersistenceState.HOLLOW) { return !DONE; } this.response = new ListResponse(object); return DONE; } } } return !DONE; } @Override protected boolean interceptPaginatedQuery() { if (metadata.getPageSize() > 0) { Integer maxIdQualifierSize = actingDataContext.getParentDataDomain().getMaxIdQualifierSize(); List<?> paginatedList; List<Object> rsMapping = metadata.getResultSetMapping(); boolean mixedResults = false; if(rsMapping != null) { if(rsMapping.size() > 1) { mixedResults = true; } else if(rsMapping.size() == 1) { mixedResults = !(rsMapping.get(0) instanceof EntityResultSegment); } } if(mixedResults) { paginatedList = new MixedResultIncrementalFaultList<>(actingDataContext, query, maxIdQualifierSize); } else { DbEntity dbEntity = metadata.getDbEntity(); if (dbEntity != null && dbEntity.getPrimaryKeys().size() == 1) { paginatedList = new SimpleIdIncrementalFaultList<Object>(actingDataContext, query, maxIdQualifierSize); } else { paginatedList = new IncrementalFaultList<Object>(actingDataContext, query, maxIdQualifierSize); } } response = new ListResponse(paginatedList); return DONE; } return !DONE; } @Override protected boolean interceptRefreshQuery() { if (query instanceof RefreshQuery) { RefreshQuery refreshQuery = (RefreshQuery) query; DataContext context = (DataContext) actingContext; // handle four separate cases, but do not combine them as it will be // unclear how to handle cascading behavior // 1. refresh all if (refreshQuery.isRefreshAll()) { synchronized (context.getObjectStore()) { invalidateLocally(context.getObjectStore(), context .getObjectStore() .getObjectIterator()); context.getQueryCache().clear(); } // cascade return !DONE; } // 2. invalidate object collection Collection objects = refreshQuery.getObjects(); if (objects != null && !objects.isEmpty()) { synchronized (context.getObjectStore()) { invalidateLocally(context.getObjectStore(), objects.iterator()); } // cascade return !DONE; } // 3. refresh query - have to do it eagerly to refresh the objects involved Query cachedQuery = refreshQuery.getQuery(); if (cachedQuery != null) { String cacheKey = cachedQuery .getMetaData(context.getEntityResolver()) .getCacheKey(); context.getQueryCache().remove(cacheKey); this.response = context.performGenericQuery(cachedQuery); // do not cascade to avoid running query twice return DONE; } // 4. refresh groups... String[] groups = refreshQuery.getGroupKeys(); if (groups != null && groups.length > 0) { for (String group : groups) { context.getQueryCache().removeGroup(group); } // cascade group invalidation return !DONE; } // shouldn't ever happen return DONE; } return !DONE; } private void invalidateLocally(ObjectStore objectStore, Iterator it) { Map<Object, ObjectDiff> diffMap = objectStore.getChangesByObjectId(); while (it.hasNext()) { Persistent object = (Persistent) it.next(); int state = object.getPersistenceState(); // we don't care about NEW objects, // but we still do care about HOLLOW, since snapshot might still // be present if (state == PersistenceState.NEW) { continue; } if (state == PersistenceState.MODIFIED || state == PersistenceState.DELETED) { // remove cached changes diffMap.remove(object.getObjectId()); } object.setPersistenceState(PersistenceState.HOLLOW); } } }