So I'm investigating this situation.
PrefetchSelectQuery prefetchQuery = new PrefetchSelectQuery(
prefetchPath,
relationship);
I replaced
prefetchQuery.setQualifier(classDescriptor.getEntity().translateToRelatedEntity(
queryQualifier,
prefetchPath));
with this instead:
prefetchQuery.setQualifier(relationship.getTargetEntity().translateToRelatedEntity(
queryQualifier,
prefetchPath));
which caused a number of cayenne tests to fail during build, but seems
to be generating correct prefetching query qualifiers.
On Tue, Sep 24, 2013 at 4:16 PM, Mike Kienenberger <[email protected]> wrote:
> Added as Issue CAY-1875 - PrefetchSelectQuery incorrectly applies
> entity qualifiers
>
> On Tue, Sep 24, 2013 at 1:48 PM, Andrus Adamchik <[email protected]>
> wrote:
>>
>>> From what testing I've done so far, the qualifier isn't put on for
>>> prefetch queries, which leaves me at the same situation as when using
>>> my datacontext delegate.
>>
>> This is bad and is not supposed to happen. Appears to be a bug. I am
>> checking SelectQueryPrefetchRouterAction, and it applies *root* entity
>> qualifier to prefetch query instead of prefetched entity. Should be a
>> relatively easy fix for "disjoint" prefetches at least.
>>
>>> What do you think of my general idea of exposing PrefetchSelectQueries to
>>> the DataContextDelegate?
>>
>> FWIW, I was hoping DataContextDelegate itself is on the way out.
>>
>> So I'd start with entity qualifier, as it is intended exactly for what you
>> are trying to do - filtering selects (presuming we fix the bug above). The
>> "special DataContext" case where the qualifier should be ignored can
>> probably be handled by starting a separate ServerRuntime, where you can
>> strip off the qualifiers. For whatever overhead it creates (ideally not
>> much), this has an advantage of cleanly separating "spaces" with different
>> ORM rules.
>>
>> Andrus
>>
>>
>> On Sep 24, 2013, at 8:03 PM, Mike Kienenberger <[email protected]> wrote:
>>> From what testing I've done so far, the qualifier isn't put on for
>>> prefetch queries, which leaves me at the same situation as when using
>>> my datacontext delegate.
>>>
>>> And I also have one case where I need to be able to disable the
>>> qualifier on a specific entity for a specific datacontext.
>>> I don't think this is possible even if the modeler qualifier was
>>> working in all other cases. I consider doing something else, like an
>>> SQL template, but I'd still have entities being fetched in this
>>> situation where foreign keys from that entity wouldn't find a matching
>>> foreign entity due to qualifier restriction.
>>>
>>>
>>>
>>> On Tue, Sep 24, 2013 at 12:07 PM, Andrus Adamchik
>>> <[email protected]> wrote:
>>>>>> I still need a way to filter all query
>>>>>> types for specific entities to filter out certain entities (appending
>>>>>> "where INVALIDATED = 'N'").
>>>>
>>>> Is this a global rule, or does it depend on some context (like user role)?
>>>> If it's the former, you can add a qualifier to affected entities in the
>>>> Modeler.
>>>>
>>>> Andrus
>>>>
>>>> On Sep 24, 2013, at 7:00 PM, Mike Kienenberger <[email protected]> wrote:
>>>>
>>>>> Here's one possible way to add support for intercepting prefetch
>>>>> queries. I'm not entirely certain it's the best way, but I didn't
>>>>> see another obvious point.
>>>>>
>>>>> What I did was to call
>>>>> QueryRouter.willPerformQuery(PrefetchSelectQuery query) before routing
>>>>> the newly-created prefetch select query.
>>>>>
>>>>> For DataDomainQueryAction, this will call context.willPerformQuery()
>>>>> if there's a non-null context.
>>>>> For anything else (DataDomainLegacyQueryAction, MockQueryRouter), it's a
>>>>> noop.
>>>>>
>>>>> If the returned query is null, then we skip routing the query and
>>>>> return either true or false. I picked true since it might be useful
>>>>> to process children of the prefetch even if the prefetch is not
>>>>> skipped. My own use case is never going to return null, so I'm fine
>>>>> with false.
>>>>>
>>>>> There's also no reason why I picked
>>>>> QueryRouter.willPerformQuery(PrefetchSelectQuery query) instead of
>>>>> QueryRouter.willPerformQuery(Query query) other than it made it more
>>>>> obvious that this method was only being used for
>>>>> PrefetchSelectQueries. But there may be other kinds of queries which
>>>>> should also be going through this method. The more I think about
>>>>> this, the more reasonable it seems have it be Query since developers
>>>>> might be writing their own Query types, and any Queries being created
>>>>> internally should be exposed through
>>>>> DataContextDelegate.willPerformQuery, and the QueryRouter is the most
>>>>> likely place to be able to forward such new queries.
>>>>>
>>>>> This has solved my issues with prefetching under 3.1. I'm still open
>>>>> to suggestions for solving my specific problem another way in the
>>>>> application code (adding database table views isn't an option), but I
>>>>> think exposing prefetch queries (as well as others) is something we
>>>>> should be supporting in Cayenne.
>>>>>
>>>>>
>>>>> Index:
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
>>>>> ===================================================================
>>>>> ---
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
>>>>> (revision 1524993)
>>>>> +++
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainLegacyQueryAction.java
>>>>> (working copy)
>>>>> @@ -27,6 +27,7 @@
>>>>>
>>>>> import org.apache.cayenne.CayenneRuntimeException;
>>>>> import org.apache.cayenne.map.DataMap;
>>>>> +import org.apache.cayenne.query.PrefetchSelectQuery;
>>>>> import org.apache.cayenne.query.Query;
>>>>> import org.apache.cayenne.query.QueryMetadata;
>>>>> import org.apache.cayenne.query.QueryRouter;
>>>>> @@ -163,4 +164,8 @@
>>>>>
>>>>> return q != null ? q : executedQuery;
>>>>> }
>>>>> +
>>>>> + public Query willPerformQuery(PrefetchSelectQuery prefetchQuery) {
>>>>> + return prefetchQuery;
>>>>> + }
>>>>> }
>>>>> Index:
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
>>>>> ===================================================================
>>>>> ---
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
>>>>> (revision 1524993)
>>>>> +++
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
>>>>> (working copy)
>>>>> @@ -772,4 +772,14 @@
>>>>> }
>>>>> }
>>>>> }
>>>>> +
>>>>> + public Query willPerformQuery(PrefetchSelectQuery prefetchQuery) {
>>>>> + // Notify DataContextDelegate that we have created a new
>>>>> PrefetchSelectQuery
>>>>> + if (null != context) {
>>>>> + Query transformedQuery =
>>>>> context.nonNullDelegate().willPerformQuery(context, prefetchQuery);
>>>>> + return transformedQuery;
>>>>> + } else {
>>>>> + return prefetchQuery;
>>>>> + }
>>>>> + }
>>>>> }
>>>>> Index:
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QueryRouter.java
>>>>> ===================================================================
>>>>> ---
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QueryRouter.java
>>>>> (revision 1524993)
>>>>> +++
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/QueryRouter.java
>>>>> (working copy)
>>>>> @@ -49,4 +49,6 @@
>>>>> * @throws NullPointerException if a map parameter is null.
>>>>> */
>>>>> QueryEngine engineForDataMap(DataMap map);
>>>>> +
>>>>> + Query willPerformQuery(PrefetchSelectQuery prefetchQuery);
>>>>> }
>>>>> Index:
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java
>>>>> ===================================================================
>>>>> ---
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java
>>>>> (revision 1524993)
>>>>> +++
>>>>> framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/query/SelectQueryPrefetchRouterAction.java
>>>>> (working copy)
>>>>> @@ -114,9 +114,15 @@
>>>>>
>>>>> // pass prefetch subtree to enable joint prefetches...
>>>>> prefetchQuery.setPrefetchTree(node);
>>>>> -
>>>>> +
>>>>> + Query transformedQuery = router.willPerformQuery(prefetchQuery);
>>>>> + if (null == transformedQuery) {
>>>>> + // Not sure if we want to return false instead.
>>>>> Returning true seems safer.
>>>>> + return true;
>>>>> + }
>>>>> +
>>>>> // route...
>>>>> - prefetchQuery.route(router, resolver, null);
>>>>> + transformedQuery.route(router, resolver, null);
>>>>> return true;
>>>>> }
>>>>>
>>>>> Index:
>>>>> framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/MockQueryRouter.java
>>>>> ===================================================================
>>>>> ---
>>>>> framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/MockQueryRouter.java
>>>>> (revision 1524993)
>>>>> +++
>>>>> framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/query/MockQueryRouter.java
>>>>> (working copy)
>>>>> @@ -50,4 +50,8 @@
>>>>> public QueryEngine engineForDataMap(DataMap map) {
>>>>> return new MockQueryEngine();
>>>>> }
>>>>> +
>>>>> + public Query willPerformQuery(PrefetchSelectQuery prefetchQuery) {
>>>>> + return prefetchQuery;
>>>>> + }
>>>>> }
>>>>>
>>>>>
>>>>>
>>>>> On Mon, Sep 23, 2013 at 7:04 PM, Mike Kienenberger <[email protected]>
>>>>> wrote:
>>>>>> All of my tests pass now, but I'm still hitting a few issues for 3.1
>>>>>> that the tests didn't reveal.
>>>>>>
>>>>>> In previous versions (not sure when it changed), there existed the
>>>>>> ability to intercept prefetch queries using
>>>>>> DataContextDelegate.willPerformQuery() or willPerformGenericQuery().
>>>>>> Those queries are no longer available -- only the original query with
>>>>>> the prefetchTree goes through those methods.
>>>>>>
>>>>>> It's the end of the day here, so I haven't traced through the code yet
>>>>>> to see what's going on, but I still need a way to filter all query
>>>>>> types for specific entities to filter out certain entities (appending
>>>>>> "where INVALIDATED = 'N'"). I've got this working for select
>>>>>> queries, relationship queries, objectIdQueries, but not prefetch
>>>>>> queries.
>>>>>>
>>>>>> And I'm still wondering what the difference between
>>>>>> willPerformGenericQuery and willPerformQuery might be. So far, I
>>>>>> just forward willPerformGenericQuery requests through my
>>>>>> willPerformQuery code.
>>>>>
>>>>
>>>
>>