JDO Query

Once you have persisted objects you need to query them. For example if you have a web application representing an online store, the user asks to see all products of a particular type, ordered by the price. This requires you to query the datastore for these products. JDO specifies support for an object-oriented query language (JDOQL) and a relational query language (SQL) (for datastores that support it). DataNucleus also supports the pseudo-OO query language for JPA (JPQL). Note that for some datastores, additional query languages may be available specific to that datastore - please check the datastores documentation. The query language you choose is your choice, typically dependent on the skillset of the developers of your application.

We recommend using JDOQL for queries wherever possible since it is object-based and datastore agnostic, giving you extra flexibility in the future. If not possible using JDOQL, only then use a language appropriate to the datastore in question
Creating a query

The principal ways of creating a query are

  • Specifying the query language, and using a single-string form of the query
    Query q = pm.newQuery("javax.jdo.query.JDOQL", 
        "SELECT FROM mydomain.MyClass WHERE field2 < threshold " +
        "PARAMETERS java.util.Date threshold");
  • Specify the single-string form of the query, and assume it is JDOQL
    Query q = pm.newQuery("SELECT FROM mydomain.MyClass WHERE field2 < threshold " +
        " PARAMETERS java.util.Date threshold");
  • Specify the candidate class for the query, and use the query API to define the query
    Query q = pm.newQuery(MyClass.class);
    q.setFilter("field2 < threshold");
    q.declareParameters("java.util.Date threshold");
  • Specify the candidate class for the query, and a collection of instances to query over and use the query API to define the query
    Collection candidates = ...; // Collection of possible result instances
    Query q = pm.newQuery(MyClass.class, candidates);
    q.setFilter("field2 < threshold");
    q.declareParameters("java.util.Date threshold");
  • A "named" query, (pre-)defined in metadata (refer to metadata docs).
    Query q = pm.newNamedQuery(MyClass.class, "MyQuery1");

As shown, there are two ways to specify the content of the query. One is via a single-string form of the query defining all components. The other is using the Query API. The query API is of most relevance for JDOQL, but can be used also for SQL and JPQL. Please note that with the query API you can also specify execution time information for the query, such as whether it executes in memory, or whether to apply a datastore timeout etc.

Compiling a query

An intermediate step once you have your query defined, if you want to check its validity is to compile it. You do this as follows

q.compile();

If the query is invalid, then a JDO exception will be thrown.



Executing a query

So we have set up our query. We now execute it

Object result = q.execute();

If we have parameters to pass in we can also do any of

Object result = q.execute(paramVal1);


Object result = q.execute(paramVal1, paramVal2);


Object result = q.executeWithArray(new Object[]{paramVal1, paramVal2});


Map paramMap = new HashMap();
paramMap("param1", paramVal1);
paramMap("param2", paramVal2);
Object result = q.executeWithMap(paramMap);

By default, when a query is executed, it will execute in the datastore with what is present in the datastore at that time. If there are outstanding changes waiting to be flushed then these will not feature in the results. To flush these changes before execution, set the following query "extension" before calling execute

q.addExtension("datanucleus.query.flushBeforeExecution","true");


Controlling the execution : Vendor extensions

JDO's query API allows implementations to support extensions and provides a simple interface for enabling the use of such extensions on queries.

q.addExtension("extension_name", "value");
    HashMap exts = new HashMap();
    exts.put("extension1", value1);
    exts.put("extension2", value2);
    q.setExtensions(exts);


Controlling the execution : FetchPlan

When a Query is executed it executes in the datastore, which returns a set of results. DataNucleus could clearly read all results from this ResultSet in one go and return them all to the user, or could allow control over this fetching process. JDO2 provides a fetch size on the Fetch Plan to allow this control. You would set this as follows

Query q = pm.newQuery(...);
q.getFetchPlan().setFetchSize(FetchPlan.FETCH_SIZE_OPTIMAL);

fetch size has 3 possible values.

  • FETCH_SIZE_OPTIMAL - allows DataNucleus full control over the fetching. In this case DataNucleus will fetch each object when they are requested, and then when the owning transaction is committed will retrieve all remaining rows (so that the Query is still usable after the close of the transaction).
  • FETCH_SIZE_GREEDY - DataNucleus will read all objects in at query execution. This can be efficient for queries with few results, and very inefficient for queries returning large result sets.
  • A positive value - DataNucleus will read this number of objects at query execution. Thereafter it will read the objects when requested.

In addition to the number of objects fetched, you can also control which fields are fetched for each object of the candidate type. This is controlled via the FetchPlan . See also Fetch Groups.



DataNucleus also allows an extension to give further control. As mentioned above, when the transaction containing the Query is committed, all remaining results are read so that they can then be accessed later (meaning that the query is still usable). Where you have a large result set and you don't want this behaviour you can turn it off by specifying a Query extension

q.addExtension("datanucleus.query.loadResultsAtCommit", "false");

so when the transaction is committed, no more results will be available from the query.



In some situations you don't want all FetchPlan fields retrieving, and DataNucleus provides an extension to turn this off, like this

q.addExtension("datanucleus.query.useFetchPlan", "false");


Controlling the execution : timeout on datastore reads
q.setDatastoreReadTimeout(1000);

Sets the timeout for this query (in milliseconds). Will throw a JDOUnsupportedOperationException if the query implementation doesn't support timeouts.

Controlling the execution : timeout on datastore writes
q.setDatastoreWriteTimeout(1000);

Sets the timeout for this query (in milliseconds) when it is a delete. Will throw a JDOUnsupportedOperationException if the query implementation doesn't support timeouts.