Queries have the ability to pack result data into a custom result
class. You might use this feature for anything from populating data
transfer objects automatically, to avoiding the casting and other
inconveniences involved in dealing with the
Object[]
s normally generated by multi-valued
projections. You specify a custom result class with the
setResultClass
method.
public void setResultClass (Class resultClass);
JDO populates result objects by matching the result class' public fields and JavaBean setter methods to the expressions defined in the query result string. The result class must be public (or otherwise instantiable via reflection), and must have a no-args constructor.
Example 11.20. Populating a JavaBean
public class SalesData { private double price; private int copiesSold; public void setPrice (double price) { this.price = price; } public double getPrice () { return price; } public void setCopiesSold (int copiesSold) { this.copiesSold = copiesSold; } public int getCopiesSold () { return copiesSold; } } Query query = pm.newQuery (Magazine.class); query.setResult ("price, copiesSold"); query.setResultClass (SalesData.class); List results = (List) query.execute (); for (Iterator itr = results.iterator (); itr.hasNext ();) processSalesData ((SalesData) itr.next ()); query.close (results);
The example above is simple enough; the names of the projected fields are matched to the setter methods in the result class. But what if the names don't match? What if the result expressions contain aggregates, mathematical expressions, and relation traversals, all of which contain symbols that can't match a field or setter method name?
JDO provides the answer in the form of result expression
aliases. An alias is a label assigned to
a particular result expression for the purposes of matching
that expression to fields or methods in the result class. To
demonstrate this, let's modify our example above to populate each
SalesData
object not with the price and
copies sold of each magazine, but with the average price and total
copies sold of all magazines published by each company.
Example 11.21. Result Aliases
Query query = pm.newQuery (Magazine.class); query.setResult ("avg(price) as price, sum(copiesSold) as copiesSold"); query.setResultClass (SalesData.class); query.setGrouping ("publisher"); List results = (List) query.execute (); for (Iterator itr = results.iterator (); itr.hasNext ();) processSalesData ((SalesData) itr.next ()); query.close (results);
Earlier in this chapter, we mentioned that the default result
string for a query is distinct this as C
, where
C
is the unqualified name of the candidate class.
Now, finally, the meaning of this default string should be clear.
But just to make it concrete, here is an example:
Example 11.22. Taking Advantage of the Default Result String
public class Wrapper { private Magazine mag; public void setMagazine (Magazine mag) { this.mag = mag; } public Magazine getMagazine () { return mag; } } Query query = pm.newQuery (Magazine.class); query.setResultClass (Wrapper.class); List results = (List) query.execute (); for (Iterator itr = results.iterator (); itr.hasNext ();) processWrapper ((Wrapper) itr.next ()); query.close (results);
In this query on the org.mag.Magazine
class, the default result string is distinct this
as Magazine
. Because our result class has a
corresponding setMagazine
method, the
query can automatically populate each Wrapper
with a matching magazine.
Whenever the specified result class does not contain a public field
or setter method matching a particular result expression,
the query looks for a method named put
that takes two Object
arguments. If found,
the query invokes that method with the result expression or
alias as the first argument, and its value as the second argument.
This not only means that you can include a generic put
method in your custom result classes, but that any
Map
implementation is suitable for use as
a query result class.
Example 11.23. Populating a Map
Query query = pm.newQuery (Magazine.class); query.setResult ("title.toUpperCase (), copiesSold"); query.setResultClass (HashMap.class); List results = (List) query.execute (); for (Iterator itr = results.iterator (); itr.hasNext ();) { HashMap map = (HashMap) itr.next (); processData (map.get ("title.toUpperCase ()"), map.get ("copiesSold")); } query.close (results);