Named queries provide a means to define complex or commonly used queries in metadata. These queries have all the capabilities of queries created in code, including support for parameters, aggregates, and projections.
You typically define named queries in query metadata
files. These files use a .jdoquery
extension, but otherwise follow the same naming and
placement rules we outlined for .jdo
files in
Section 5.7, “Metadata Placement”.
Query metadata also has the same basic structure
as persistence metadata. There is a root jdoquery
element that contains package
elements, which in
turn contain class
elements.
extension
s are sprinkled throughout. The only
novel element is query
.
<!ELEMENT jdoquery (extension*, (package|query)+, extension*)> <!ELEMENT package (extension*, class+, extension*)> <!ATTLIST package name CDATA #REQUIRED> <!ELEMENT class (query+)> <!ATTLIST class name CDATA #REQUIRED> <!ELEMENT query ((#PCDATA|extension)*)> <!ATTLIST query name CDATA #REQUIRED> <!ATTLIST query language CDATA #IMPLIED> <!ELEMENT extension ANY> <!ATTLIST extension vendor-name CDATA #REQUIRED> <!ATTLIST extension key CDATA #IMPLIED> <!ATTLIST extension value CDATA #IMPLIED>
The query
element defines a named query. You can
place query
elements just below the document's
root element or at the class
level. Queries
within the root element do not have an automatic candidate class.
Queries in a class
element default to
that candidate class.
Named queries are expressed in single-string form, which we
detailed in the previous section. The text of the query goes
within the query
element. This element also
has the following attributes:
name
: The name of the query. This
attribute is required.
language
: The language of the query text.
Defaults to javax.jdo.query.JDOQL
.
In Chapter 17, SQL Queries we discuss how
to write SQL queries, and vendors may offer support for
additional query languages.
The following example defines both a root-level and a class-level named query.
Example 11.24. Query Metadata Document
The salesReport
query generates sales
information. The findByTitle
query,
on the other hand, finds a Magazine
by title. Note that this query does not use a from
clause to name its candidate class; its candidate
class defaults to the surrounding class
element.
<?xml version="1.0"?> <jdoquery> <query name="salesReport"> select title, price * copiesSold from org.mag.Magazine </query> <package name="org.mag"> <class name="Magazine"> <query name="findByTitle">select unique where title == :t</query> </class> </package> </jdoquery>
The Magazine
query metadata file above
might be in any of the following locations:
org/mag/Magazine.jdoquery
org/mag/package.jdoquery
org/package.jdoquery
package.jdoquery
You do not have to use separate .jdoquery
files
to define named queries, however. You can integrate query
definitions right into your persistence metadata
(.jdo
files) or mapping metadata
(.orm
files). Just add query
elements below the root element, or after the last
field
and before any fetch-group
elements in any class
.
Here is what it looks like to define the queries
above in Magazine
's persistence metadata,
rather than in a .jdoquery
file:
Example 11.25. Persistence Metadata Document
<?xml version="1.0"?> <jdo> <query name="salesReport"> select title, price * copiesSold from org.mag.Magazine </query> <package name="org.mag"> <class name="Magazine" objectid-class="Magazine$ObjectId"> <field name="isbn" primary-key="true"/> <field name="title" primary-key="true"/> <field name="articles"> <collection element-type="Article" dependent-element="true"/> </field> <query name="findByTitle">select unique where title == :t</query> <fetch-group name="detail"> <field name="publisher" fetch-depth="0"/> <field name="articles" fetch-depth="0"/> </fetch-group> </class> </package> </jdo>
public Query newNamedQuery (Class cls, String name);
At runtime, you obtain named queries with the
PersistenceManager.newNamedQuery
method. The
Class
argument specifies the class that defines
the query. Use null
if the query is defined outside
the scope of a class. The String
argument is the
name of the query.
Before returning a named query, the system populates it with the
information provided in the metadata, then compiles it to make sure it
is valid. You can still change the query before executing it, using
the methods of the
Query
interface. These
changes won't affect other query instances returned from subsequent
calls to newNamedQuery
.
The example below executes the two queries we defined in Example 11.24, “Query Metadata Document”.
Example 11.26. Executing Named Queries
PersistenceManager pm = ...; Query query = pm.newNamedQuery (null, "salesReport"); List sales = (List) query.execute (); for (Iterator itr = sales.iterator (); itr.hasNext ();) { Object[] salesData = (Object[]) itr.next (); processSalesData ((String) salesData[0], (Double) salesData[1]); } query.close (sales); query = pm.newNamedQuery (Magazine.class, "findByTitle"); Magazine jdj = (Magazine) query.execute ("JDJ");