JPOX
JPOX
 Project  |  Ver 1.1  |  Ver 1.2  |  JDO  |  JPA  |  Guides  |  Tools
1.1 | Preparation | O/R Mapping | Runtime | Extensions | Developer
JPOX 1.1 Runtime
Runtime Tools
Queries
RDBMS Datastores
JPOX Queries

Other sections have discussed the persistent of objects. 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.

The JDO specifications (ver 1.0.1 and ver 2.0) require that implementations provide a Query capability using its own query language (JDOQL). JDOQL is oriented around the objects that are persisted, and provides an interface for selecting these objects within the framework of a query. JPOX provides such a JDOQL Query mechanism. The JDO 2.0 specification requires that implementations provide a SQL query mechanism (for datastores that support SQL). JPOX provides this. In addition, JPOX provides a query language that is positioned between JDOQL and SQL that has SQL-like syntax yet allows access to the field names of classes - JPOXSQL .

Which query language is used is down to the developer. The data-tier of an application could be written by a primarily Java developer, who would typically think in an object-oriented way and so would likely prefer JDOQL. On the other hand the data-tier could be written by a datastore developer who is more familiar with SQL concepts and so could easily make more use of SQL. This is the power of an implementation like JPOX in that it provides the flexibility for different people to develop the data-tier utilising their own skills to the full without having to learn totally new concepts.



Let's now try to understand the Query interface in JDO , We firstly need to look at a typical Query.

Query query = pm.newQuery("javax.jdo.query.JDOQL", "SELECT FROM org.jpox.MyClass WHERE param2 < threshold");
query.declareImports("import java.util.Date");
query.declareParameters("Date threshold");
query.setOrdering("param1 ascending");
List results = (List)query.execute(my_threshold);

In this Query, we select our query language (JDOQL in this case), and the query is specified to return all objects of type org.jpox.MyClass (or subclasses) which have the field param2 less than some threshold value. We've specified the query like this because we want to pass the threshold value in dynamically. We then import the type of our threshold parameter, and the parameter itself, and set the ordering of the results from the Query to be in ascending order of some field param1. The Query is then executed, passing in the threshold value. The example is to highlight the typical methods specified for a Query. Clearly you may only specify the Query line if you wanted something very simple. The result of the Query is cast to a List since in this case it returns a List of results.



Query Language differences

As we've mentioned, JPOX provides 3 query languages for the user. The most portable, provided across all datastores is JDOQL. Then we have one using the RDBMS query language SQL. In addition JPOX provides its own extension to SQL called JPOXSQL. The latter is non-portable across JDO implementations, and so by using it you would be reducing the portability of your JDO application. The 3 languages have clear differences in their syntax, but also in the application of the various methods on the Query. This table attempts to highlight the differences.

Query methodJDOQLSQLJPOXSQL
Defined by
setClass()Sets the class of the objects to select from the candidate collectionSets the class of the query result objects to be returnedSets the class of the query result objects to be returned
setUnique()Identifies that only 1 object is required to be returnedIdentifies that only 1 object is required to be returnedIdentifies that only 1 object is required to be returned
setResult()Sets the result parameters outputN/A (throws a JDOUserException)N/A (throws a JDOUserException)
setResultClass()Sets the output results classSets the output results classSets the output results class
setRange()Sets the range of result objects returnedN/A (throws a JDOUserException)N/A (throws a JDOUserException)
setFilter()Sets the filter expression for the queryN/A (throws a JDOUserException)N/A (throws a JDOUserException)
declareImports()Used to qualify class names in the parameter list and variable listN/A (throws a JDOUserException)Used to qualify class names in the parameter list and the SQL text
declareParameters()Declares any parameters referenced in the filter stringN/A (throws a JDOUserException)Declares any parameters referenced in the SQL text
declareVariables()Declares any variables referenced in the filter stringN/A (throws a JDOUserException)N/A (throws a JDOUserException)
setCandidates()Sets the Collection or Extent of objects to query. If the argument is null, this is a no-op. If the argument is an empty Collection, the query results will be empty.N/A (throws a JDOUserException)N/A (throws a JDOUserException)
setOrdering()Sets the ordering expression for the queryN/A (throws a JDOUserException)N/A (throws a JDOUserException)
setGrouping()Sets the grouping expression for the queryN/A (throws a JDOUserException)N/A (throws a JDOUserException)


Named Queries

The query described above is constructed dynamically. Queries of that form are perfect for situations like a web system where the user selects something and you want to present them with particular information based on their selections. There do however exist other types of situation where you know a particular query will be needed. In this case it isn't desirable to have to construct it at runtime. This functionality is added in the JDO 2.0 specification, allowing the user to specify queries in the JDO Meta-Data.

To highlight how to do this, lets say we have a class called Product (something to sell in a store). We define the JDO Meta-Data for the class in the normal way, but we also have some query that we know we will require, so we define the following in the Meta-Data.

<jdo>
    <package name="org.jpox.example">
        <class name="Product">
            ...
            <query name="SoldOut" language="javax.jdo.query.JDOQL"><![CDATA[
            SELECT FROM org.jpox.example.Product WHERE status == "Sold Out"
            ]]></query>
        </class>
    </package>
</jdo>

So we have a query called "SoldOut" defined for the class org.jpox.example.Product that returns all Product (and subclasses) that have a status of "Sold Out". So in our application all we need to do now is

Query query = pm.newNamedQuery(org.jpox.example.Product.class,"SoldOut");
List results = (List)query.execute();

Please note that this syntax is based around the single-string form of JDOQL and applies to JPOX 1.1.0-beta-1 onwards.

You now have the means to use the 2 principal types of queries in JDO. Please proceed to the sections specific to JDOQL and SQL for details on the precise nature of the query for these languages.

See also :-



Result Class

When you perform a query, using JDOQL or SQL the query will, in general, return a List of objects. These objects are by default of the same type as the candidate class. This is good for the majority of situations but there are some situations where you would like to control the output object. This can be achieved by specifying the Result Class.

query.setResultClass(myResultClass);

The Result Class has to meet certain requirements. These are

  • Can be one of Integer, Long, Short, Float, Double, Character, Byte, Boolean, String, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp, or Object[]
  • Can be a user defined class, that has either a constructor taking arguments of the same type as those returned by the query (in the same order), or has a public put(Object, Object) method, or public setXXX() methods, or public fields.

Where you have a query returning a single field, you could specify the Result Class to be one of the first group for example. Where your query returns multiple fields then you can set the Result Class to be your own class. So we could have a query like this

Query query = pm.newQuery(pm.getExtent(org.jpox.samples.Payment.class,false));
query.setFilter("amount > 10.0");
query.setResultClass(Price.class);
query.setResult("amount, currency");
List results = (List)query.execute();

and we define our Result Class Price as follows

public class Price
{
    protected double amount = 0.0;
    protected String currency = null;

    public Price(double amount, String currency)
    {
        this.amount = amount;
        this.currency = currency;
    }

    ...
}

In this case our query is returning 2 fields (a Double and a String), and these map onto the constructor arguments, so JPOX will create objects of the Price class using that constructor. We could have provided a class with public fields instead, or provided setXXX methods or a put method. They all work in the same way.



Result Fetching

When a Query is executed it executes SQL in the datastore, which returns a ResultSet. JPOX 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 JPOX full control over the fetching. In this case JPOX 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 - JPOX 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 - JPOX 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.



JPOX 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("org.jpox.query.loadResultsAtCommit", "false");

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

Query Timeouts

JPOX provides a useful extension to JDO queries by allowing control over the timeout of the query. So, for example, if you have a query that can cause problems in terms of the time taken, you can set a timeout on the query to retain the usability of your application.

A JDO2 standard way of doing this (from 1.1.0-beta-6) for each query is to do

query.addExtension("org.jpox.query.timeout","20");

The value passed in is in seconds. You can also specify this for all queries using a PMF property "org.jpox.query.timeout".



Result Set Control

JPOX provides a useful extension to JDO by allowing control over the ResultSet's that are created by queries. You have at your convenience 4 properties that give you the power to control whether the result set is read only, whether it can be read forward only, the number of rows to be fetched etc. You can specify these on a per-Query basis (from version 1.1.0-beta-6) as follows

query.addExtension("org.jpox.query.fetchSize", "20");
query.addExtension("org.jpox.query.fetchDirection", "forward");
query.addExtension("org.jpox.query.resultSetType", "scroll-insensitive");
query.addExtension("org.jpox.query.resultSetConcurrency", "read-only");

Alternatively you can specify these on the PersistenceManagerFactory so that they apply to all queries for that PMF. Again, the properties are

  • org.jpox.query.fetchSize - controls the number of records fetched from the datastore each time more are required.
  • org.jpox.query.fetchDirection - controls the direction that the ResultSet is navigated. By default this is forwards only. Use this property to change that.
  • org.jpox.query.resultSetType - controls the type of ResultSet.
  • org.jpox.query.resultSetConcurrency - controls whether the ResultSet is read only or updateable.

Bear in mind that not all JDBC drivers support all of the possible values for these options. That said, they do add a degree of control that is often useful.