JDOQL Queries

JDO defines ways of querying objects persisted into the datastore. It provides its own object-based query language (JDOQL). JDOQL is designed as the Java developers way of having the power of SQL queries, yet retaining the Java object relationship that exist in their application model. DataNucleus provides all functionality required by the JDO 1.0 and 2.0 specifications as well as providing a series of vendor extensions. A typical JDOQL query may be set up in one of 2 ways. Here's an example

Declarative JDOQL :
Query q = pm.newQuery(mydomain.Person.class);
q.setFilter("lastName == \"Jones\" && age < age_limit");
q.declareParameters("double age_limit");
List results = (List)q.execute(20.0);

Single-String JDOQL :
Query q = pm.newQuery(
    "SELECT FROM mydomain.Person WHERE lastName == \"Jones\" && " +
    "age < age_limit PARAMETERS double age_limit");
List results = (List)q.execute(20.0);

So here in our example we select all "Person" objects with surname of "Jones" and where the persons age is below 20. The language is intuitive for Java developers, and is intended as their interface to accessing the persisted data model. As can be seen above, the query is made up of distinct parts. The class being selected (the SELECT clause in SQL), the filter (which equates to the WHERE clause in SQL), together with any sorting (the ORDER BY clause in SQL), etc.



Single-String JDOQL

In traditional (declarative) JDOQL (JDO 1.0) it was necessary to specify the component parts (filter, candidate class, ordering, etc) of the query using the mutator methods on the Query. In JDO 2 you can now specify it all in a single string. This string has to follow a particular pattern, but provides the convenience that many people have been asking for. The pattern to use is as follows

SELECT [UNIQUE] [<result>] [INTO <result-class>]
        [FROM <candidate-class> [EXCLUDE SUBCLASSES]]
        [WHERE <filter>]
        [VARIABLES <variable declarations>]
        [PARAMETERS <parameter declarations>]
        [<import declarations>]
        [GROUP BY <grouping>]
        [ORDER BY <ordering>]
        [RANGE <start>, <end>]>

The "keywords" in the query are shown in UPPER CASE but can be in UPPER or lower case.

Lets give an example of a query using this syntax

SELECT UNIQUE FROM mydomain.Employee ORDER BY departmentNumber

so we form the parts of the query as before, yet here we just specify it all in a single call.

Accessing Fields

In JDOQL you access fields in the query by referring to the field name. For example, if you are querying a class called Product and it has a field "price", then you access it like this

Query query = pm.newQuery(Product.class);
query.setFilter("price < 150.0");

In addition to the persistent fields, you can also access "public static final" fields of any class. You can do this as follows

Query query = pm.newQuery(Product.class, "taxPercent < mydomain.Product.TAX_BAND_A");

So this will find all products that include a tax percentage less than some "BAND A" level. Where you are using "public static final" fields you can either fully-qualify the class name or you can include it in the "imports" section of the query (see later).

Data types : literals

JDOQL supports the following literals: IntegerLiteral, FloatingPointLiteral, BooleanLiteral, CharacterLiteral, StringLiteral, and NullLiteral.

Operators precedence

The following list describes the operator precedence in JDOQL.

  1. Cast
  2. Unary ("~") ("!")
  3. Unary ("+") ("-")
  4. Multiplicative ("*") ("/") ("%")
  5. Additive ("+") ("-")
  6. Relational (">=") (">") ("<=") ("<") ("instanceof")
  7. Equality ("==") ("=!")
  8. Boolean logical AND ("&")
  9. Boolean logical OR ("|")
  10. Conditional AND ("&&")
  11. Conditional OR ("||")

Concatenation Expressions

The concatenation operator(+) concatenates a String to either another String or Number. Concatenations of String or Numbers to null results in null.

Control over locking of fetched objects

JDOQL allows control over whether objects found by a query are locked during that transaction so that other transactions can't update them in the meantime. To do this you would do

Query q = pm.newQuery(...);
q.setSerializeRead(true);

You can also specify this for all queries for all PMs using a PMF property datanucleus.SerializeRead . In addition you can perform this on a per-transaction basis by doing

tx.setSerializeRead(true);

If the datastore in use doesn't support locking of objects then this will do nothing



Explicit Parameters

With a query you can pass values into the query as parameters. If you declare the parameters when defining the query then these are explicit parameters, so you set the type of the parameter, and when you pass the value in it has to be of that type.

Here's a simple example for finding the elements of a class with a field below a particular threshold level. Here we pass in the threshold value ( limit )

Declarative JDOQL :
Query query = pm.newQuery(mydomain.Product.class, "price < limit");
query.declareParameters("double limit");
List results = (List)query.execute(150.00);

Single-String JDOQL :
Query query = pm.newQuery(
    "SELECT FROM mydomain.Product WHERE price < limit PARAMETERS double limit");
List results = (List)query.execute(150.00);

For completeness, the class is shown here

class Product
{
    double price;
    ...
}


Implicit Parameters

With a query you can pass values into the query as parameters. If you don't declare the parameters when defining the query but instead prefix identifiers in the query with : (colon) then these are implicit parameters.

Let's repeat the previous query but this time using implicit parameters.

Declarative JDOQL :
Query query = pm.newQuery(Product.class, "price < :limit");
List results = (List)query.execute(150.00);

Single-String JDOQL :
Query query = pm.newQuery(
    "SELECT FROM mydomain.Product WHERE price < :limit");
List results = (List)query.execute(150.00);


instanceof

JDOQL allows the Java keyword instanceof so you can compare objects against a class.

Let's take an example. We have a class A that has a field "b" of type B and B has subclasses B1, B2, B3. Clearly the field "b" of A can be of type B, B1, B2, B3 etc, and we want to find all objects of type A that have the field "b" that is of type B2. We do it like this

Declarative JDOQL :
Query query = pm.newQuery(A.class);
query.setFilter("b instanceof mydomain.B2");
List results = (List)query.execute();

Single-String JDOQL :
Query query = pm.newQuery("SELECT FROM mydomain.A WHERE b instanceof mydomain.B2");
List results = (List)query.execute();