While there's lots of things we need to figure out to create
generified queries, here is one low-hanging fruit - type-safe
qualifiers. The idea is borrowed from the WebObjects EOF extensions
framework called Wonder:
http://webobjects.mdimension.com/hudson/job/Wonder53/javadoc/er/extensions/eof/ERXKey.html
The way it works, is that for each String property in a Persistent
object, we generate an extra parameterized "key". E.g.:
public class _Artist extends CayenneDataObject {
public static final String DATE_OF_BIRTH_PROPERTY = "dateOfBirth";
public static final Key<Date> DATE_OF_BIRTH = new Key<Date>(
DATE_OF_BIRTH_PROPERTY);
public static final String NAME_PROPERTY = "name";
public static final Key<String> NAME = new Key<String>(NAME_PROPERTY);
public static final String PAINTINGS_PROPERTY = "paintings";
public static final Key<Painting> PAINTINGS = new Key<Painting>(
PAINTINGS_PROPERTY);
...
Key class is a builder of type-safe key-value expressions (see the
link above for the type of methods that it has). I wrote a quick API-
only prototype that is a replacement of ExpressionFactory. Here is an
example of building the following Expression the old way and the new
way:
Expression: name='X' and (painting.name='Y' or painting.name='Z' or
painting.name='A')
1. Current API:
Expression clause1 = ExpressionFactory.matchExp(Artist.NAME_PROPERTY,
"X");
Expression clause2 =
ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY + "." +
Painting.NAME_PROPERTY, "Y");
Expression clause3 =
ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY + "." +
Painting.NAME_PROPERTY, "Z");
Expression clause4 =
ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY + "." +
Painting.NAME_PROPERTY, "A");
Expression clause23 = clause2.orExp(clause3);
Expression clause234 = clause23.orExp(clause4);
Expression qualifier = clause1.andExp(clause234);
2. New API:
Expression clause1 = Artist.NAME.eq("X");
Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
Expression clause3 = Artist.PAINTINGS.dot(Painting.NAME).eq("Z");
Expression clause4 = Artist.PAINTINGS.dot(Painting.NAME).eq("A");
Expression qualifier = Each.get(clause1, Any.get(clause2, clause3,
clause4));
As you see the new API is much tighter, and the first part of it is
completely type-safe (generated "keys" ensure that we are matching
against the right type of value, even in a multi-step path). The last
line uses 2 new Expression factories, "Each" and "Any", to quickly
organize key-value expressions into a nested expression tree. I think
this is as good as it can get within the constraints of the Java syntax.
I'd like to see if a similar approach can be extended to query
building. Although that'll be a entirely different level of effort.
For one thing it may force us to reconcile EJQBL and SelectQuery into
a single query (SelectQuery is functionally a subset of EJBQLQuery.
But SelectQuery "compilation" into SQL is fairly optimized, while
EJBQLQuery-to-SQL conversion involves a much longer and slower
pipeline). Plus of course there are other obstacles that we discussed
before (such as a variety of possible result types).
Andrus
This archive was generated by hypermail 2.0.0 : Wed Dec 30 2009 - 14:59:53 EST