Core Java Data Objects

Let us now look at the Query interface a bit more in detail. In this section we look at the different factory methods available for query construction, how queries are cached, how JDO for compiled queries, using templates for query construction and the use of other query languages.

6.6.1 Query construction

There are a number of factory methods in the PersistenceManager interface that can be used to construct a Query instance. We have seen some of these methods in preceding code snippets. Table 6-5 lists all the factory methods. The methods dealing with query compilation and creating queries from other queries are covered further in Sections 6.6.4 and 6.6.5, respectively.

Table 6-5. Factory Methods for Creating Queries

Factory method

Description

Query newQuery()

Creates a new Query with no elements.

Query newQuery(Class class)

Creates a new Query specifying the Class of the candidate instances.

Query newQuery(Class cls, Collection candaidates)

Creates a new Query with the candidate Extent ; the class is taken from the Extent .

Query newQuery(Class class, Collection candaidates, String filter)

Creates a new Query with the Class of the candidate instances, candidate Collection and filter.

Query newQuery(Class class, String filter)

Creates a new Query with the Class of the candidate instances and filter.

Query newQuery(Extent extent)

Creates a new Query with the Class of the candidate instances and candidate Extent .

Query newQuery(Extent extent, String filter)

Creates a new Query with the candidate Extent and filter; the class is taken from the Extent .

Query newQuery(Object compiled)

Creates a new Query using elements from another Query .

Query newQuery(String language, Object query)

Creates a new Query using the specified language.

6.6.2 Queries and the cache

In Chapter 5, the concept of the cache maintained by the PersistenceManager was introduced. The Query interface exposes two methods that specify the run-time behavior and query execution characteristics: the setIgnoreCache(boolean ignoreCache) method and the getIgnoreCache() set and get the javax.jdo.option.IgnoreCache property. When this property is set to true, the query execution ignores the instances in the PersistenceManager 's cache. These cached instances may have changed from their persistent representations in the datastore (see Chapter 4 and Appendix A for state changes). Although synchronizing state for the query execution may result in more accurate results (this may also depend on the isolation settings in the datastore), it also negatively affects the performance characteristics of the system. When the cache is ignored, the query executes entirely in the datastore. This feature, when used in conjunction with optimistic transactions or read only type transactions (see Chapter 10), can dramatically improve performance.

6.6.3 Compiled queries

JDO queries can be compiled with the compile() method shown in Figure 6-3. This does not mean that the query is compiled into some native format; it is merely a hint to the implementation to optimize an execution plan for the query. It is up to the vendor to support this optional feature and choose the compilation strategy depending on the datastore. If the vendor chooses a compile-time optimization technique, a potential disadvantage could be that the execution strategy for the query may actually become sub-optimal at runtime due to updates to the datastore at runtime.

Compiled Queries

The notion of compiled queries is analogous to the concept of compiled queries in object-oriented databases that use OQL. OQL is an object-oriented SQL-like query language and is a part of the ODMG-93 standard. It can be used in two different ways: either as an embedded function in a programming language or as an ad-hoc query language. Some vendors compile OQL declarations into native constructs such as C++ to perform query optimization at preprocessing time rather than at runtime. Therefore, all type errors are caught at compile time. (Note that precompiled queries are also available in relational databases.)

6.6.4 Template queries

The vendor-provided implementation class for the Query interface is required to be serializable . This means that the application code can serialize and save the Query instances indefinitely and recreate them subsequently. Because a Query may hold onto active resources in the underlying datastore, the deserialized Query object cannot actually be re-executed. However, the details associated with the Query (e.g., the candidate class, filter string, imports, parameters, variables , and ordering) are retained. This deserialized query can be used as a sort of template to recreate another query instance from the PersistenceManager with the original details that were saved. For example, this code snippet from TemplateExample.java shows how the Query can be serialized and reused:

// create and serialize the query if(args[0].equalsIgnoreCase("serialize")) { String filter = "books.contains(myfavoritebook) && " + "(myfavouritebook.title==\"Learn Java Today\")"; Query query = pm.newQuery(Author.class, filter); query.declareVariables("Book myfavoritebook"); // close resources just to be safe query.closeAll(); serializeQuery(query); } // other code here /**Method to serialize and save the query object*/ public static void serializeQuery(Query query){ try{ ObjectOutputStream os = new ObjectOutputStream (new FileOutputStream("query.ser")); os.writeObject(query); os.close(); } catch(Exception e){ System.out.println("An exception occurred during query serialization :"+e); } }

The serialized query can then be read later and used as a template and executed. It is required that the Query instance being reused must come from the same JDO vendor; in other words, you should not expect to serialize and reuse Query instances across vendor implementations . The corresponding snippet below from the same example shows how the serialized query can be read and used as a template:

if (args[0].equalsIgnoreCase("deserialize")) { // deserialize and execute Transaction tx = pm.currentTransaction(); tx.begin(); Object deserQuery = deserializeQuery(); Query recreatedQuery = pm.newQuery(deserQuery); Collection results = (Collection) recreatedQuery.execute(); if (results.isEmpty()) System.out.println("No results found"); else { Iterator iter = results.iterator(); while (iter.hasNext()) { Author author = (Author) iter.next(); String name = author.getName(); System.out.println("Author's name is " + name); System.out.println("Author's address is " + author.getAddress()); System.out.println("Author's books are "); Iterator books = author.getBooks().iterator(); while (books.hasNext()) System.out.println("Book:" + books.next()); } } recreatedQuery.closeAll(); tx.commit(); } // other code here /** Method to Deserialize the query */ public static Query deserializeQuery() { try { ObjectInputStream os = new ObjectInputStream(new FileInputStream("query.ser")); Query q = (Query) os.readObject(); os.close(); return q; } catch (Exception e) { System.out.println("An exception occurred during query deserialization :" + e); return null; }

6.6.5 Choosing a different query language

The JDOQL discussed in this chapter is required to be implemented by all vendors. However, vendors can choose to support other query languages natively in their implementations. For example, a vendor using a relational database as the underlying datastore may choose to allow SQL as the query language; another vendor using an object database may support OQL directly. A query can be constructed in a format other than JDOQL using the overloaded newQuery() method in the persistence manager.

public Query newQuery(String language, Object query);

If the vendor supports this feature, the application may potentially leverage features that are specific to the datastore; however, using anything other than JDOQL may render the application code non-portable to other vendor implementations. The code example below shows how an SQL query can be used in a particular vendor's implementation:

String SQL= "Select * from Authors"; Query query = pm.newQuery(VendorInterface.SQL, SQL); Collection results = (Collection)query.execute();

Note that the value of the String parameter language is not specified in the JDO specifications for any language; it depends on the vendor.

Категории