Pages

JDO Typesafe Queries : Part 2 - Expressions

Wednesday, July 7, 2010
Continuing on from the previous post, in order to be able to express components of a query in a Java style we need to represent all fields/properties/parameters/variables as expressions. The type of the field/property/parameter/variable determines its expression type. Let's take an example

public class Person
{
String firstName;
String lastName;
int age;
Person bestFriend;
}

We need to represent these field types with expressions, so let's start with

  • StringExpression - for Strings

  • NumericExpression - for int, long, short, double, float, Integer, Long, Short, Double, Float, BigDecimal, BigInteger etc

  • BooleanExpression - for boolean, Boolean

  • ByteExpression - for byte, Byte

  • CharacterExpression - for char, Character

  • DateExpression - for Date-based types

  • ObjectExpression - for other Object-based types where we have no specific handling


Ok, so this is all well and good and we can express firstName as a StringExpression, and similarly age is a NumericExpression. So for that we can do

QPerson person = QPerson.person;
Query<Person> q = pm.newTypesafeQuery(person);
Person bob =
q.filter(person.firstName.eq("Bob")).executeUnique();

To represent a persistable field (i.e a 1-1 relation, bestFriend in the above example) we have another expression type PersistableExpression.

"Q" classes


Above you see use of metamodel query classes. We refer to them as "Q" classes currently, but the naming is arbitrary for now. So what does a "Q" class look like ?

public class QPerson implements PersistableExpression
{
public static final QPerson person = new QPerson("person");

public final QPerson bestFriend;
public final NumericExpression age;
public final StringExpression name;

... (implementation of other PersistableExpression methods).
}

So in simple terms, we have a public field for each of the normal fields in Person, but that are of XXXExpression types. So when a user accessed QPerson.person they get the candidate for use in a query. Then they can do "person.name" and this is a StringExpression. The NumericExpressionImpl/StringExpressionImpl are the implementations for the particular provider of this typesafe query (e.g DataNucleus). You also notice above that the bestFriend field is also a QPerson, and hence a PersistableExpression, so we can chain field access as "person.bestFriend.firstName"


Methods of field types


Obviously in JDOQL (and Java), we allow some method calls. This is represented here by adding the supported methods to StringExpression, NumericExpression etc. For example StringExpression has a method toUpperCase() to match what Java allows, so we can upgrade the query example to be

QPerson person = QPerson.person;
Query<Person> q = pm.newTypesafeQuery(person);
Person bob =
q.filter(person.firstName.toUpperCase().eq("BOB")).executeUnique();






There are still some more challenging areas of the API to work out, but the above is just to give a further taster and provoke comment.

Please refer to the current javadocs here (Work-in-progress).

11 comments:

  1. Some comments

    * ComparableExpression for literal types which are comparable (as supertype for String,Number,Boolean etc.?)

    * new NumericExpressionImpl(this,"age"); ?!?, otherwise you lose the parent reference

    * You have lots of comparison methods in Expression. Entity expressions will not support them, so maybe push the down to a ComparableExpression?

    * Same thing with numeric methods in Expression. They belong to NumericExpression

    * with DateExpression you bind the expression type to java.util.Date. DataNucleus doesn't support the JodaTime API?

    ReplyDelete
  2. Having <, >, <=, >= on an intermediate interface is fine, but don't think I'd call it "ComparableExpression" since ==, != are also comparison operations and they aren't included. But that's just naming ...

    NumericExpressionImpl is an implementation detail, so not part of any interface and not a concern here.

    As DateExpression javadoc says, 'consider splitting it into something more generic'. JodaTime is one consideration, but it is non-standard (though supported by DN). Also we have javax.time as a consideration (also supported by DN, and in the future likely also by JDO). Could have a base TemporalExpression and then add subexpression for java.util.Date based types, others for javax.time based types, and optionally (maybe impl dependent) jodatime expression type(s).

    ReplyDelete
  3. Though maybe "ComparableExpression" makes more sense as a name to just map across to java.lang.Comparable. That's in SVN now

    ReplyDelete
  4. Ok, ComparableExpression for the relation to java.util.Comparable, but that's just naming.

    NumericExpressionImpl usage was just quite confusing here. Code examples should make sense, that's why I mentioned it.

    ReplyDelete
  5. Post updated to remove implementation details of the Q class so the user sees the API. Javadocs also updated to reflect above comments. Retained the add() method on StringExpression since it is concatenation in that case. Thx

    ReplyDelete
  6. In the javadocs I found this :

    public interface ObjectExpression
    extends Expression

    This seems to be a bug.

    Also maybe ObjectExpression is not necessary. Object is the supertype for everything in Java, so maybe just implemenent Expression when nothing else fits.

    ReplyDelete
  7. Sorry, I meant

    public interface ObjectExpression
    extends Expression<java.lang.Number&gtM

    More comments

    * asc, desc, min, max belong to ComparableExpression

    * avg and sum to NumericExpression

    * eq(Expression) -> eq(Expression) etc.

    * NumericExpresssion.add, div, mul, mod, mul sub also with Number arguments

    ReplyDelete
  8. Suggestions for StringExpression :

    * BooleanExpression equalsIgnoreCase(String str);

    * BooleanExpression equalsIgnoreCase(Expression e);

    ReplyDelete
  9. All suggestions ought to be in DN SVN now.
    Those StringExpression ideas (whilst now in DN SVN for typesafe queries) aren't part of JDOQL itself currently but you can obviously get the same via things like "str.toUpperCase() == str2.toUpperCase()" though makes sense to have that method added.

    ReplyDelete
  10. How do you extract from Expression instances the data needed for serialization? e.g. the used operators and operands.

    If we could standardize this layer, then Querydsl and DataNucleus/JDO expressions could easily be mixed.

    This is something I am sketching for Querydsl 2.0 : http://source.mysema.com/forum/mvnforum/viewthread_thread,106

    The last class diagram shows the core types. For Querydsl 2.0 I am trying to separate the DSL part from the Expression archetypes.

    ReplyDelete
  11. Timo, I've got a working prototype of this API for JDOQL now in DN SVN trunk. See
    http://www.datanucleus.org/products/accessplatform_2_2/jdo/jdoql_typesafe.html
    I've not implemented all TypesafeQuery methods yet (i.e parameters, variables, and some of the execute methods), and also I've not yet considered subqueries.

    ReplyDelete