cayenne-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andrus Adamchik <and...@objectstyle.org>
Subject Type-safe qualifiers
Date Wed, 30 Dec 2009 19:59:13 GMT
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






Mime
View raw message