You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cayenne.apache.org by Andrus Adamchik <an...@objectstyle.org> on 2009/12/30 20:59:13 UTC
Type-safe qualifiers
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
Re: Type-safe qualifiers (and queries)
Posted by Andrus Adamchik <an...@objectstyle.org>.
Yeah, seems like the same thing, but not parameterized properly.
Andrus
On Dec 31, 2009, at 6:38 AM, Mike Kienenberger wrote:
> Maybe worth imitating how JPA 2.0 does it? I've only glanced at it in
> the past, but it seems similar. (Eclipselink is the reference
> implementation.)
>
> http://wiki.eclipse.org/Introduction_to_EclipseLink_Expressions_%28ELUG%29
>
> http://wiki.eclipse.org/EclipseLink/Examples/JPA/ORMQueries
>
> http://www.eclipse.org/eclipselink/api/1.1/org/eclipse/persistence/expressions/ExpressionBuilder.html
>
> There's also something called a Criteria api now in JPA 2.0. I only
> looked at it (in the spec) as I was typing this message.
>
> http://jcp.org/aboutJava/communityprocess/pfd/jsr317/index2.html
> Chapter 6
>
> Not sure exactly how it's different -- maybe it's just a specification
> of the expression builder syntax.
>
>
>
> On Wed, Dec 30, 2009 at 4:54 PM, Andrus Adamchik <andrus@objectstyle.org
> > wrote:
>>
>> On Dec 30, 2009, at 9:59 PM, Andrus Adamchik wrote:
>>
>>> 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.
>>
>> Or maybe it is not that hopeless and we can use a set of user-
>> facing query
>> "builders" without affecting our backend implementations. Just
>> prototyped a
>> builder that can create EJBQL/SelectQuery with parameterized result
>> type
>> that can be tied to ObjectContext.performQuery()...
>>
>> public interface Query<T> ...
>> public class Select<T> implements Query<T>...
>>
>> Here is now we can build correctly parameterized queries for
>> different
>> result types. Also shows other EJBQL features, like subqueries.
>> While it
>> adds some more terse API compared to SelectQuery, parameterization
>> is the
>> main point here. I guess this is a good way to make EJBQLQuery
>> finally
>> usable:
>>
>> void query_newAPI_EntityResult() {
>>
>> Select<Artist> query = Select.entity(Artist.class);
>> query.setDistinct();
>>
>> Select<Painting> subquery = Select.entity(Painting.class);
>> subquery.setWhere(Painting.NAME.like("X%"));
>>
>> Expression clause1 = Artist.NAME.eq("X");
>> Expression clause2 =
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>> Expression clause3 = Exists.in(subquery);
>> query.setWhere(All.of(clause1, clause2, clause3));
>>
>> query.addOrderByAscending(Artist.NAME);
>>
>> query.addOrderByAscending(Artist.PAINTINGS.dot(Painting.NAME));
>>
>> query.addDisjointPrefetch(Artist.PAINTINGS);
>> }
>>
>> void query_newAPI_ScalarResult() {
>>
>> Select<Date> query = Select.scalar(Artist.class,
>> Artist.DATE_OF_BIRTH);
>>
>> Expression clause1 = Artist.NAME.eq("X");
>> Expression clause2 =
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>> query.setWhere(Any.of(clause1, clause2));
>> }
>>
>> void query_newAPI_MixedResult() {
>>
>> // fetching Artist entity plus paintings count
>> Select<Object[]> query = Select.mixed(Artist.class,
>> new Key<Artist>(""),
>> Artist.PAINTINGS.count());
>>
>> Expression clause1 = Artist.NAME.eq("X");
>> Expression clause2 =
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>> query.setWhere(Any.of(clause1, clause2));
>> }
>>
>> void query_newAPI_GenericObject() {
>>
>> Select<Persistent> query = Select.generic(Persistent.class,
>> "MyGenericEntity");
>>
>> Expression clause1 = new Key<String>("name").eq("X");
>> Expression clause2 = new Key<Integer>("age").eq(5);
>> query.setWhere(All.of(clause1, clause2));
>> }
>>
>> void query_newAPI_DataRow() {
>>
>> Select<DataRow> query = Select.dataRow(Artist.class);
>>
>> Expression clause1 = Artist.NAME.eq("X");
>> Expression clause2 =
>> Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>> query.setWhere(All.of(clause1, clause2));
>> }
>>
>>
>>
>> Andrus
>>
>
Re: Type-safe qualifiers (and queries)
Posted by Mike Kienenberger <mk...@gmail.com>.
Maybe worth imitating how JPA 2.0 does it? I've only glanced at it in
the past, but it seems similar. (Eclipselink is the reference
implementation.)
http://wiki.eclipse.org/Introduction_to_EclipseLink_Expressions_%28ELUG%29
http://wiki.eclipse.org/EclipseLink/Examples/JPA/ORMQueries
http://www.eclipse.org/eclipselink/api/1.1/org/eclipse/persistence/expressions/ExpressionBuilder.html
There's also something called a Criteria api now in JPA 2.0. I only
looked at it (in the spec) as I was typing this message.
http://jcp.org/aboutJava/communityprocess/pfd/jsr317/index2.html
Chapter 6
Not sure exactly how it's different -- maybe it's just a specification
of the expression builder syntax.
On Wed, Dec 30, 2009 at 4:54 PM, Andrus Adamchik <an...@objectstyle.org> wrote:
>
> On Dec 30, 2009, at 9:59 PM, Andrus Adamchik wrote:
>
>> 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.
>
> Or maybe it is not that hopeless and we can use a set of user-facing query
> "builders" without affecting our backend implementations. Just prototyped a
> builder that can create EJBQL/SelectQuery with parameterized result type
> that can be tied to ObjectContext.performQuery()...
>
> public interface Query<T> ...
> public class Select<T> implements Query<T>...
>
> Here is now we can build correctly parameterized queries for different
> result types. Also shows other EJBQL features, like subqueries. While it
> adds some more terse API compared to SelectQuery, parameterization is the
> main point here. I guess this is a good way to make EJBQLQuery finally
> usable:
>
> void query_newAPI_EntityResult() {
>
> Select<Artist> query = Select.entity(Artist.class);
> query.setDistinct();
>
> Select<Painting> subquery = Select.entity(Painting.class);
> subquery.setWhere(Painting.NAME.like("X%"));
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
> Expression clause3 = Exists.in(subquery);
> query.setWhere(All.of(clause1, clause2, clause3));
>
> query.addOrderByAscending(Artist.NAME);
> query.addOrderByAscending(Artist.PAINTINGS.dot(Painting.NAME));
>
> query.addDisjointPrefetch(Artist.PAINTINGS);
> }
>
> void query_newAPI_ScalarResult() {
>
> Select<Date> query = Select.scalar(Artist.class,
> Artist.DATE_OF_BIRTH);
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
> query.setWhere(Any.of(clause1, clause2));
> }
>
> void query_newAPI_MixedResult() {
>
> // fetching Artist entity plus paintings count
> Select<Object[]> query = Select.mixed(Artist.class,
> new Key<Artist>(""), Artist.PAINTINGS.count());
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
> query.setWhere(Any.of(clause1, clause2));
> }
>
> void query_newAPI_GenericObject() {
>
> Select<Persistent> query = Select.generic(Persistent.class,
> "MyGenericEntity");
>
> Expression clause1 = new Key<String>("name").eq("X");
> Expression clause2 = new Key<Integer>("age").eq(5);
> query.setWhere(All.of(clause1, clause2));
> }
>
> void query_newAPI_DataRow() {
>
> Select<DataRow> query = Select.dataRow(Artist.class);
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
> query.setWhere(All.of(clause1, clause2));
> }
>
>
>
> Andrus
>
Re: Type-safe qualifiers (and queries)
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 30, 2009, at 9:59 PM, Andrus Adamchik wrote:
> 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.
Or maybe it is not that hopeless and we can use a set of user-facing
query "builders" without affecting our backend implementations. Just
prototyped a builder that can create EJBQL/SelectQuery with
parameterized result type that can be tied to
ObjectContext.performQuery()...
public interface Query<T> ...
public class Select<T> implements Query<T>...
Here is now we can build correctly parameterized queries for different
result types. Also shows other EJBQL features, like subqueries. While
it adds some more terse API compared to SelectQuery, parameterization
is the main point here. I guess this is a good way to make EJBQLQuery
finally usable:
void query_newAPI_EntityResult() {
Select<Artist> query = Select.entity(Artist.class);
query.setDistinct();
Select<Painting> subquery = Select.entity(Painting.class);
subquery.setWhere(Painting.NAME.like("X%"));
Expression clause1 = Artist.NAME.eq("X");
Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
Expression clause3 = Exists.in(subquery);
query.setWhere(All.of(clause1, clause2, clause3));
query.addOrderByAscending(Artist.NAME);
query.addOrderByAscending(Artist.PAINTINGS.dot(Painting.NAME));
query.addDisjointPrefetch(Artist.PAINTINGS);
}
void query_newAPI_ScalarResult() {
Select<Date> query = Select.scalar(Artist.class, Artist.DATE_OF_BIRTH);
Expression clause1 = Artist.NAME.eq("X");
Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
query.setWhere(Any.of(clause1, clause2));
}
void query_newAPI_MixedResult() {
// fetching Artist entity plus paintings count
Select<Object[]> query = Select.mixed(Artist.class,
new Key<Artist>(""), Artist.PAINTINGS.count());
Expression clause1 = Artist.NAME.eq("X");
Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
query.setWhere(Any.of(clause1, clause2));
}
void query_newAPI_GenericObject() {
Select<Persistent> query = Select.generic(Persistent.class,
"MyGenericEntity");
Expression clause1 = new Key<String>("name").eq("X");
Expression clause2 = new Key<Integer>("age").eq(5);
query.setWhere(All.of(clause1, clause2));
}
void query_newAPI_DataRow() {
Select<DataRow> query = Select.dataRow(Artist.class);
Expression clause1 = Artist.NAME.eq("X");
Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
query.setWhere(All.of(clause1, clause2));
}
Andrus
Re: Type-safe qualifiers
Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 31/12/09 7:04 PM, Andrus Adamchik wrote:
> I like "dot" and "eq" because they have fewer letters, making things
> more tight. (also we used the term "path", not "join", so I guess
> there's really no precedent here). But good point about outer joins.
> Will definitely need to address that aspect.
I don't want to bikeshed on the details here when the overall idea is so nice, but just to finish this part of the conversation....
* 'dot' isn't very clear to me. I don't immediately make the connection that 'dot' == '.' == 'path' == 'join'. And maybe it just my maths background, but the first thing I thought of was 'dot product' in vector maths. [1]
* I liked 'join' mainly because anyone who knows SQL will understand instantly what that means, and 'outerJoin' is also immediately obvious.
* 'outerDot' sounds really silly :-)
Ari
[1] http://en.wikipedia.org/wiki/Dot_product
--
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: Type-safe qualifiers
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 31, 2009, at 10:04 AM, Andrus Adamchik wrote:
> On Dec 31, 2009, at 7:13 AM, Aristedes Maniatis wrote:
>> How does Key relate to Attribute and Relationship? Is it an
>> interface that both implement?
>
> Unrelated at all, except for the matching name. I thought of using
> Attributes/Relationships for this purpose, but it is not possible to
> parameterize them as they are abstract metadata. Their "compiled"
> counterparts (Property/ArcProperty) could've potentially been used
> as "keys", but those can't be statically bound to Persistent objects
> in compile time, as they are generated in runtime.
Also I guess Key is a "path", so logically it is not a single
attribute or relationship, but potentially a chain of those. The
closest thing in Cayenne 3.0 is a "path" Expression. So maybe rename
it from Key to Path and subclass from Expression (??). Anyways, that's
some details to figure out once we are comfortable with the general
direction...
Andrus
Re: Type-safe qualifiers
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 31, 2009, at 7:13 AM, Aristedes Maniatis wrote:
> How does Key relate to Attribute and Relationship? Is it an
> interface that both implement?
Unrelated at all, except for the matching name. I thought of using
Attributes/Relationships for this purpose, but it is not possible to
parameterize them as they are abstract metadata. Their "compiled"
counterparts (Property/ArcProperty) could've potentially been used as
"keys", but those can't be statically bound to Persistent objects in
compile time, as they are generated in runtime.
> 2. New API:
>>
>> Expression clause1 = Artist.NAME.eq("X");
>> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
>
> Would this be more in keeping with existing usage:
>
> Artist.PAINTINGS.join(Painting.NAME).match("Y");
>
> and
>
> Artist.PAINTINGS.outerJoin(Painting.NAME).match("Y");
>
> to emulate the "|" we have now.
I like "dot" and "eq" because they have fewer letters, making things
more tight. (also we used the term "path", not "join", so I guess
there's really no precedent here). But good point about outer joins.
Will definitely need to address that aspect.
> At first glance Artist.PAINTINGS looks like it should be
> <Collection<Painting>>. But that breaks the type-safeness. We can
> tell when a key represents a Relationship (<? extends
> PersistentObject>) rather than an Attribute. But would we ever want
> to know when it represents a to-many relation rather than a to-one?
I had this question as well when first looking at Wonder. For the
expression building stuff seems like it is ok to not make a
distinction between to-one and to-many. However if we stumble on the
case where we need to, we can simply subclass Key. (similar to
Property/ArcProperty).
> Expression qualifier = clause1.and(clause2.or(clause3).or(clause4));
I like this one. I guess we'll go with it.
Andrus
Re: Type-safe qualifiers
Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 31/12/09 6:59 AM, Andrus Adamchik wrote:
> 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);
What a shame String is final, so Key can't just extend String. Oh well.
In the long term, perhaps the old property will go away, so perhaps this is a better first step:
public static final Key<Date> DATE_OF_BIRTH = new Key<Date>("dateOfBirth");
@deprecated
public static final String DATE_OF_BIRTH_PROPERTY = DATE_OF_BIRTH.toString();
How does Key relate to Attribute and Relationship? Is it an interface that both implement?
> 2. New API:
>
> Expression clause1 = Artist.NAME.eq("X");
> Expression clause2 = Artist.PAINTINGS.dot(Painting.NAME).eq("Y");
Would this be more in keeping with existing usage:
Artist.PAINTINGS.join(Painting.NAME).match("Y");
and
Artist.PAINTINGS.outerJoin(Painting.NAME).match("Y");
to emulate the "|" we have now.
> 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).
At first glance Artist.PAINTINGS looks like it should be <Collection<Painting>>. But that breaks the type-safeness. We can tell when a key represents a Relationship (<? extends PersistentObject>) rather than an Attribute. But would we ever want to know when it represents a to-many relation rather than a to-one?
> 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.
Any and Each are a bit obscure. You aren't going to find them by reading the Javadoc for Expression. What do they give over the existing orExp/andExp syntax?
Expression qualifier = Each.get(clause1, Any.get(clause2, clause3, clause4));
Expression qualifier = clause1.andExp(clause2.orExp(clause3).orExp(clause4));
Intuitively I don't equate "each" as meaning "all" or "and". Or even simpler:
Expression qualifier = clause1.and(clause2.or(clause3).or(clause4));
Ari
--
-------------------------->
Aristedes Maniatis
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: Type-safe qualifiers
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 31, 2009, at 11:24 AM, Andrey Razumovsky wrote:
> 1. Instead of:
> Expression clause2 =
> ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
> "." + Painting.NAME_PROPERTY, "Y");
> I prefer using just
> Expression clause2 =
> ExpressionFactory.matchExp("paintings.paintingName",
> "Y");
> Yes, I don't like using constants, to me bottom example seems more
> readable,
> short and quicker to write (especially if there are a lot of items
> in the
> path). As far as I understood, this will not be allowed in new API (?)
Looks like your schema doesn't change too often :-)
Anyways, we can still allow that in the new API. And even preserve
"declarative" type-safety:
Expression clause2 = new Key<String>("paintings.paintingName").eq("Y");
> 2. I wrote a library which automatically builds Cayenne expressions
> from
> values user specified in grid filter of Web (GWT) client - path is
> coming
> from client. With new API, shall I have something like
> CayenneDataObject.getKey(String) method to obtain any key in uniform
> way?
> (even with that, I will have more work to do to convert path to new
> API,
> maybe we need some method like Key Cayenne.getNestedKey(Class clazz,
> String
> path)?
See the example above (new Key<?>(...)). Capability-wise the new API
will allow everything ExpressionFactory does.
> 3. I have examples of intentionally not following type-safing for
> expressions (these are real examples)
>
> a. EF.matchExp("artist", "200") - quick comparison of relation
> object's id
> b. EF.lessExp("date", "2010-01-01 10:00:00.000") - replacement of
> date (or
> int, double, whatever) with just string
>
> Will those be allowed somehow in new API?
I guess there are two ways about it.
1. new Key<Object>(..)
2. Provide methods that do String to object conversions for common
cases of dates and numbers, using explicit or implicit formatters E.g.
new Key<Date>("dob").after("2010-01-01 10:00:00.000", "YYYY-mm-
dd...");
new Key<Integer>("age").eq("200");
Among other things this will make some drivers (like PostgreSQL)
happy, as they don't allow fuzzy types in PreparedStatement bind
parameters.
>
> Happy New Year!!!
Happy New Year to everybody :-)
Andrus
Re: Type-safe qualifiers
Posted by Andrey Razumovsky <ra...@gmail.com>.
Type-safing for expressions is cool, but I have few questions about its
profitness.
Just a couple examples.
1. Instead of:
Expression clause2 = ExpressionFactory.matchExp(Artist.PAINTINGS_PROPERTY +
"." + Painting.NAME_PROPERTY, "Y");
I prefer using just
Expression clause2 = ExpressionFactory.matchExp("paintings.paintingName",
"Y");
Yes, I don't like using constants, to me bottom example seems more readable,
short and quicker to write (especially if there are a lot of items in the
path). As far as I understood, this will not be allowed in new API (?)
2. I wrote a library which automatically builds Cayenne expressions from
values user specified in grid filter of Web (GWT) client - path is coming
from client. With new API, shall I have something like
CayenneDataObject.getKey(String) method to obtain any key in uniform way?
(even with that, I will have more work to do to convert path to new API,
maybe we need some method like Key Cayenne.getNestedKey(Class clazz, String
path)?
3. I have examples of intentionally not following type-safing for
expressions (these are real examples)
a. EF.matchExp("artist", "200") - quick comparison of relation object's id
b. EF.lessExp("date", "2010-01-01 10:00:00.000") - replacement of date (or
int, double, whatever) with just string
Will those be allowed somehow in new API?
Happy New Year!!!
2009/12/30 Andrus Adamchik <an...@objectstyle.org>
> 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
>
>
>
>
>
>
--
Andrey