[[typesafe-queries-and-fetchgroups]]
= Typesafe Queries and Fetch-groups

:Notice: 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.



Fetch groups provide a means to hint to DataNucleus that it should perform a SQL join when querying.
A common use case is to avoid the xref:pjdo:ROOT:hints-and-tips.adoc#diagnosing-n-plus-1[n+1] issue.

(So far as I could ascertain) it isn't possible to specify fetch group hints using JDOQL, but it is possible to specify them using the programmatic API or using typesafe queries.

For example, here's a JDOQL query:

[source,java]
----
@Query(
		name = "findCompletedOrLaterWithItemsByReportedDate", language = "JDOQL",
		value = "SELECT "
				+ "FROM com.mycompany.invoice.IncomingInvoice "
				+ "WHERE items.contains(ii) "
				+ "   && (ii.reportedDate == :reportedDate) "
				+ "   && (approvalState != 'NEW' && approvalState != 'DISCARDED') "
				+ "VARIABLES com.mycompany.invoice.IncomingInvoiceItem ii "
),
public class IncomingInvoice ... { /* ... */ }
----

which normally would be used from a repository:


[source,java]
----
public List<IncomingInvoice> findCompletedOrLaterWithItemsByReportedDate(
        final LocalDate reportedDate) {
    return repositoryService.allMatches(
            Query.named(IncomingInvoice.class,
                    "findCompletedOrLaterWithItemsByReportedDate")
               .withParameter("reportedDate", reportedDate));
}
----


This can be re-written as a type-safe query as follows:

[source,java]
----
public List<IncomingInvoice> findCompletedOrLaterWithItemsByReportedDate(final LocalDate reportedDate) {

    final QIncomingInvoice ii = QIncomingInvoice.candidate();
    final QIncomingInvoiceItem iii = QIncomingInvoiceItem.variable("iii");

    final TypesafeQuery<IncomingInvoice> q =
        isisJdoSupport.newTypesafeQuery(IncomingInvoice.class);

    q.filter(
            ii.items.contains(iii)
        .and(iii.reportedDate.eq(reportedDate))
        .and(ii.approvalState.ne(IncomingInvoiceApprovalState.NEW))
        .and(ii.approvalState.ne(IncomingInvoiceApprovalState.DISCARDED)));
    final List<IncomingInvoice> incomingInvoices = Lists.newArrayList(q.executeList());
    q.closeAll();
    return incomingInvoices;
}
----

Now the `IncomingInvoice` has four fields that require eager loading.
This can be specified by defining a named fetch group:

[source,java]
----
@FetchGroup(
        name="seller_buyer_property_bankAccount",
        members={
                @Persistent(name="seller"),
                @Persistent(name="buyer"),
                @Persistent(name="property"),
                @Persistent(name="bankAccount")
        })
public class IncomingInvoice ... { /* ... */ }
----


This fetch group can then be used in the query using `q.getFetchPlan().addGroup(...)`.
Putting this all together, we get:

[source,java]
----
public List<IncomingInvoice> findCompletedOrLaterWithItemsByReportedDate(final LocalDate reportedDate) {

    final QIncomingInvoice ii = QIncomingInvoice.candidate();
    final QIncomingInvoiceItem iii = QIncomingInvoiceItem.variable("iii");

    final TypesafeQuery<IncomingInvoice> q =
        isisJdoSupport.newTypesafeQuery(IncomingInvoice.class);

    q.getFetchPlan().addGroup("seller_buyer_property_bankAccount");     // <1>

    q.filter(
            ii.items.contains(iii)
        .and(iii.reportedDate.eq(reportedDate))
        .and(ii.approvalState.ne(IncomingInvoiceApprovalState.NEW))
        .and(ii.approvalState.ne(IncomingInvoiceApprovalState.DISCARDED)));
    final List<IncomingInvoice> incomingInvoices = Lists.newArrayList(q.executeList());
    q.closeAll();
    return incomingInvoices;
}
----
<1> specify the fetch group to use.

