You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cayenne.apache.org by Tore Halset <ha...@pvv.ntnu.no> on 2007/12/21 10:41:44 UTC
performQuery generics
Hello.
I typically do a lot of
SelectQuery query = new SelectQuery(MyEntity.class, qual);
List entities = ctxt.performQuery(query);
What I want instead is something like
SelectQuery<MyEntity> query = new SelectQuery<MyEntity>(qual);
List<MyEntity> entities = ctxt.performQuery(query);
How can I get rid of the "MyEntity.class, " argument to the
SelectQuery constructor?
What about SelectQuery#setFetchingDataRows that changes the expected
return type?
What do you think?
Regards,
- Tore.
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 21, 2007, at 2:38 PM, Aristedes Maniatis wrote:
> Also, is reversing the syntax an interesting idea if this change is
> going to be made?
>
> List<Artists> result = query.getResult(Artist.class, context);
> List<Artists> result = query.getResultAsDataRows(Artist.class,
> context);
>
> Maybe that will just confuse people. Otherwise,
>
> context.getResultAsDataRows(Artist.class, query)
> context.getResult(Artist.class, query)
The wrapper idea (SelectBuilder) is sort of along the lines of
reversing the API that you suggested. But let me comment on why I am
not a fan of adding new ways to run a query to the ObjectContext. When
implementing ROP and nested contexts I found out that having so many
"redundant" or "utility" methods in a context object makes it
extremely hard to implement multi-layer contexts. So I'd prefer that
we keep ObjectContext API as lean as possible, and pass all query
parameters in the query object itself.
On the other hand what is seen as fluff by the framework developers,
is in fact what makes things smooth for the end users. So we have to
address both conflicting concerns. In such situation, I feel that
having special end-user API separate from the stack abstraction of
ObjectContext is the least invasive solution.
DataObjectUtils is one example of that and it turned to be a big
success... So what I am suggesting is to expand on that and provide an
even more powerful and terse way to get data in Cayenne. That'll be
another layer on top of what we have, the one that doesn't have API
constraints of the main stack building blocks.
Andrus
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 21/12/2007, at 8:41 PM, Tore Halset wrote:
> Hello.
>
> I typically do a lot of
>
> SelectQuery query = new SelectQuery(MyEntity.class, qual);
> List entities = ctxt.performQuery(query);
>
> What I want instead is something like
>
> SelectQuery<MyEntity> query = new SelectQuery<MyEntity>(qual);
> List<MyEntity> entities = ctxt.performQuery(query);
>
> How can I get rid of the "MyEntity.class, " argument to the
> SelectQuery constructor?
> What about SelectQuery#setFetchingDataRows that changes the expected
> return type?
> What do you think?
How will the query know which table to perform the query on? I think
that passing the table is essential. But having two separate methods,
one of which returns data rows and one which return persistent objects
will help tremendously.
Also, is reversing the syntax an interesting idea if this change is
going to be made?
List<Artists> result = query.getResult(Artist.class, context);
List<Artists> result = query.getResultAsDataRows(Artist.class, context);
Maybe that will just confuse people. Otherwise,
context.getResultAsDataRows(Artist.class, query)
context.getResult(Artist.class, query)
Ari
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 21, 2007, at 2:30 PM, Tore Halset wrote:
> SelectQuery would be a Query<Object>
> DataRowSelectQuery would be a Query<DataRow>
> PersistentSelectQuery would be a Query<? extends Persistent>
That will work... My problem is conceptual - Query interface is too
generic to imply a single result type parameter. There are updating
queries, but even selecting ones like SQLTemplate and ProcedureQuery
can return multiple results of different type (which is abstracted in
org.apache.cayenne.QueryResponse).
Andrus
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 28/12/2007, at 7:55 AM, Tore Halset wrote:
>>> // new
>>> <T> List<T> performQuery(Class<T> aClass, Query query);
>
> +0. I would like to use a specific interface instead if possible.
> public <T> List<T> performQuery(PersistentSelectQuery<T> query);
+1 but I think a separate function name will be required if
SelectQuery, ObjectIdQuery, etc are to implement PersistentSelectQuery
and keep backward compatibility with existing code.
public <T> List<T> fetchObjects(PersistentSelectQuery<T> query);
>>> // new
>>> List<DataRow> performDataRowQuery(Query query);
>
> +0. I would like to use a specific interface instead if possible.
> public List<DataRow> performQuery(DataRowQuery query);
>
> My hope is that SelectQuery would become typesafe, but that would
> destroy DataRow support for SelectQuery.
Not necessarily: SelectQuery still could be generified. And then that
type data is not used when performing a data row query.
public List<DataRow> fetchDataRows(Query query);
> That is not too important for me as I only use DataRow SelectQuery
> in a couple of places. The second best alternative (AFAIK) would be
> to create new Query classes to handle this as I have written earlier.
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
Interestingly, chained method calls are being considered as an
encouraged pattern in Java 7:
http://tech.puredanger.com/java7/#chained
Ari
On 21/01/2008, at 8:00 AM, Andrus Adamchik wrote:
> There is even a name for the kind of pattern we discussed for the
> new set of queries - "fluent interface":
>
> http://www.martinfowler.com/bliki/FluentInterface.html
>
> Andrus
>
> On Jan 13, 2008, at 4:32 PM, Andrus Adamchik wrote:
>
>> Select<Artist> query1 = new Select<Artist>(Artist.class);
>> query1.andQualifier("artistName = 'ABC'")
>> .orQualifier("artistName = 'XYZ'")
>> .orderAscending(Artist.ARTIST_NAME_PROPERTY);
>> List<Artist> artists = context.performQuery(query1);
>
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
There is even a name for the kind of pattern we discussed for the new
set of queries - "fluent interface":
http://www.martinfowler.com/bliki/FluentInterface.html
Andrus
On Jan 13, 2008, at 4:32 PM, Andrus Adamchik wrote:
> Select<Artist> query1 = new Select<Artist>(Artist.class);
> query1.andQualifier("artistName = 'ABC'")
> .orQualifier("artistName = 'XYZ'")
> .orderAscending(Artist.ARTIST_NAME_PROPERTY);
> List<Artist> artists = context.performQuery(query1);
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Jan 13, 2008, at 4:32 PM, Andrus Adamchik wrote:
> 3. What I haven't done yet is applying this to the new
> List<Object[]> results, as the backend is not fully ready yet, and I
> need a better understanding of all consequences myself.
I tried to analyze this remaining case tonight (for the background you
may check @SqlResultSetMapping annotation per Chapter 8.3.3 of the JPA
spec)... SQLSelect (a would-be selecting parameterized SQLTemplate
analog) is still a bit verbose...
SQLSelect<Integer> q1 = new SQLSelect<Integer>("DataMap1",
Integer.class)
.append("SELECT COUNT(1) FROM X");
SQLSelect<Object[]> q2 = new SQLSelect<Object[]>("DataMap1",
Object[].class)
.append("SELECT COUNT(1), SUM(PRICE), 'CONST_STRING' FROM X");
Of course <Object[]> doesn't tell us what's in the array... For the
reference, here is a SQLSelect prototype:
public class SQLSelect<T> implements TypedQuery<T> {
public SQLSelect(String dataMapName, Class<T> resultClass) {
}
public SQLSelect<T> append(String sql) {
}
public SQLSelect<T> append(String key, String sql) {
}
// more stuff goes here ...
}
Andrus
Re: performQuery generics
Posted by Tore Halset <ha...@pvv.ntnu.no>.
On Jan 13, 2008, at 15:32, Andrus Adamchik wrote:
> Since there was a clear dissatisfaction with all suggested
> solutions, I kept trying other options. Here is one more attempt. I
> ditched both ideas - to subclass existing queries and to implement
> Query wrappers that are not queries themselves. Instead I combined
> functionality of the existing queries and a user-friendly wrapper in
> single new class that is itself a Query. As a result there's no
> backwards compatibility issues allowing for the tight and clean API.
>
> 1. an example of usage... Notice that DataRows/Persistent/Generic
> objects are all handled by the same query class:
>
> Select<Artist> query1 = new Select<Artist>(Artist.class);
> query1.andQualifier("artistName = 'ABC'")
> .orQualifier("artistName = 'XYZ'")
> .orderAscending(Artist.ARTIST_NAME_PROPERTY);
> List<Artist> artists = context.performQuery(query1);
Nice.
> 2. here is an implementation draft... Omitted are SQLSelect,
> NamedSelect, and ProcedureSelect. Also I think at the end "Select"
> will be based internally on EJBQLQuery, not SelectQuery, as it has a
> number of extra capabilities...
>
> interface TypedQuery<T> extends Query {
>
> }
>
> interface ObjectContext {
> List<T> performQuery(TypedQuery<T> query);
> }
+1
> So ... does the above sound acceptable, or are there still
> reservations?
Nice work! I think this is getting very good. What do you think Andrus?
- Tore.
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 14/01/2008, at 8:57 AM, Lachlan Deck wrote:
> I don't think this works. i.e., I don't think it's up to the user to
> determine the type returned for a data row fetch. That's a function
> of the framework as far as I see it.
>
> How about this?
> List<DataRow> dataRows = context.performRawQuery(query1)
I agree... just to flesh out what Lachlan said: you might have a query
which is constructed in one place (which also sets up the expression,
etc) and then passes it to another bit of code to perform the query
and deal with the results. It should not be predetermined what type of
results are returned, but up to the code which performs the query to
make that choice.
Otherwise as Andrus' suggestion stands you have to make that choice at
the time of constructing the query, making queries less reusable. The
other benefit is that Select then always has the type of the class it
is querying. This may prove useful in other places.
Ari Maniatis
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Lachlan Deck <la...@gmail.com>.
On 14/01/2008, at 8:57 AM, Lachlan Deck wrote:
> On 14/01/2008, at 1:32 AM, Andrus Adamchik wrote:
>
>> Select<DataRow> query2 = new Select<DataRow>(DataRow.class,
>> "Artist");
>> query2.andQualifier("artistName = 'ABC'")
>> .orQualifier("artistName = 'XYZ'")
>> .orderAscending(Artist.ARTIST_NAME_PROPERTY);
>> List<DataRow> dataRows = context.performQuery(query2);
>
> I don't think this works. i.e., I don't think it's up to the user
> to determine the type returned for a data row fetch. That's a
> function of the framework as far as I see it.
>
> How about this?
> List<DataRow> dataRows = context.performRawQuery(query1)
Woops, of course some raw fetches return Integer or Long etc (e.g.,
for a count). The actual data type returned can depend on the
column's data-type or otherwise. So:
performRawQuery would could have a signature like this?
public List<?> performRawQuery(...)
with regards,
--
Lachlan Deck
Re: performQuery generics
Posted by Lachlan Deck <la...@gmail.com>.
On 14/01/2008, at 1:32 AM, Andrus Adamchik wrote:
> Since there was a clear dissatisfaction with all suggested
> solutions, I kept trying other options. Here is one more attempt. I
> ditched both ideas - to subclass existing queries and to implement
> Query wrappers that are not queries themselves. Instead I combined
> functionality of the existing queries and a user-friendly wrapper
> in single new class that is itself a Query. As a result there's no
> backwards compatibility issues allowing for the tight and clean API.
>
>
> 1. an example of usage... Notice that DataRows/Persistent/Generic
> objects are all handled by the same query class:
>
> Select<Artist> query1 = new Select<Artist>(Artist.class);
> query1.andQualifier("artistName = 'ABC'")
> .orQualifier("artistName = 'XYZ'")
> .orderAscending(Artist.ARTIST_NAME_PROPERTY);
> List<Artist> artists = context.performQuery(query1);
That's good... but...
> Select<DataRow> query2 = new Select<DataRow>(DataRow.class, "Artist");
> query2.andQualifier("artistName = 'ABC'")
> .orQualifier("artistName = 'XYZ'")
> .orderAscending(Artist.ARTIST_NAME_PROPERTY);
> List<DataRow> dataRows = context.performQuery(query2);
I don't think this works. i.e., I don't think it's up to the user to
determine the type returned for a data row fetch. That's a function
of the framework as far as I see it.
How about this?
List<DataRow> dataRows = context.performRawQuery(query1)
with regards,
--
Lachlan Deck
Re: Non-entity result types [Was: performQuery generics]
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Jan 14, 2008, at 11:41 AM, Andrus Adamchik wrote:
> (b) allows to do checking for the type mismatch inside ObjectContext
> (so we can throw an Exception inside 'performQuery' vs. user
> discovering it later when iterating over the result).
In other words we can guarantee the result type in runtime.
Andrus
Non-entity result types [Was: performQuery generics]
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Jan 14, 2008, at 1:46 AM, Lachlan Deck wrote:
> This would lead to something like the following wouldn't it? (e.g.,
> for a @sum of columnA for rows satisfying the query)
>
> Select<Long> rawSelect = new Select<Long>(Long.class, "Artist");
> <...>
> List<Long> dataRows = context.performQuery(rawSelect);
>
> Problem is that Long is not guaranteed to be returned. It could very
> well be that the data type of the column, for example, targeted by
> the raw fetch could be mapped to an Integer.
>
> Whereas with a function like (public List<?> performRawQuery(...))
> that would seem to me to bypass those concerns.
>
> What do you think Andrus?
>
> with regards,
Yes, the "raw" query case from the generics perspective is as good as
casting (or as bad as casting).
IMO there's nothing wrong with it though as (a) this prevents the need
for casting and suppresses Eclipse warnings, (b) allows to do checking
for the type mismatch inside ObjectContext (so we can throw an
Exception inside 'performQuery' vs. user discovering it later when
iterating over the result).
Another positive side effect of it is that the method eliminates the
need for "setFetchingDataRows".
Andrus
Re: performQuery generics
Posted by Lachlan Deck <la...@gmail.com>.
On 14/01/2008, at 1:32 AM, Andrus Adamchik wrote:
> 2. here is an implementation draft... Omitted are SQLSelect,
> NamedSelect, and ProcedureSelect. Also I think at the end "Select"
> will be based internally on EJBQLQuery, not SelectQuery, as it has
> a number of extra capabilities...
>
> interface TypedQuery<T> extends Query {
>
> }
>
> interface ObjectContext {
> List<T> performQuery(TypedQuery<T> query);
> }
>
>
> public class Select<T> implements TypedQuery<T> {
>
> protected SelectQuery delegateQuery;
>
> public Select(Class<T> rootClass) {
> this.delegateQuery = new SelectQuery(rootClass);
> }
>
> public Select(Class<T> rootClass, String entityName) {
>
> if (entityName == null) {
> this.delegateQuery = new SelectQuery(rootClass);
> } else {
> this.delegateQuery = new SelectQuery(entityName);
>
> if (rootClass.isAssignableFrom(DataRow.class)) {
> delegateQuery.setFetchingDataRows(true);
> }
> }
> }
<...>
> So ... does the above sound acceptable, or are there still
> reservations?
This would lead to something like the following wouldn't it? (e.g.,
for a @sum of columnA for rows satisfying the query)
Select<Long> rawSelect = new Select<Long>(Long.class, "Artist");
<...>
List<Long> dataRows = context.performQuery(rawSelect);
Problem is that Long is not guaranteed to be returned. It could very
well be that the data type of the column, for example, targeted by
the raw fetch could be mapped to an Integer.
Whereas with a function like (public List<?> performRawQuery(...))
that would seem to me to bypass those concerns.
What do you think Andrus?
with regards,
--
Lachlan Deck
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
Since there was a clear dissatisfaction with all suggested solutions,
I kept trying other options. Here is one more attempt. I ditched both
ideas - to subclass existing queries and to implement Query wrappers
that are not queries themselves. Instead I combined functionality of
the existing queries and a user-friendly wrapper in single new class
that is itself a Query. As a result there's no backwards compatibility
issues allowing for the tight and clean API.
1. an example of usage... Notice that DataRows/Persistent/Generic
objects are all handled by the same query class:
Select<Artist> query1 = new Select<Artist>(Artist.class);
query1.andQualifier("artistName = 'ABC'")
.orQualifier("artistName = 'XYZ'")
.orderAscending(Artist.ARTIST_NAME_PROPERTY);
List<Artist> artists = context.performQuery(query1);
Select<DataRow> query2 = new Select<DataRow>(DataRow.class, "Artist");
query2.andQualifier("artistName = 'ABC'")
.orQualifier("artistName = 'XYZ'")
.orderAscending(Artist.ARTIST_NAME_PROPERTY);
List<DataRow> dataRows = context.performQuery(query2);
2. here is an implementation draft... Omitted are SQLSelect,
NamedSelect, and ProcedureSelect. Also I think at the end "Select"
will be based internally on EJBQLQuery, not SelectQuery, as it has a
number of extra capabilities...
interface TypedQuery<T> extends Query {
}
interface ObjectContext {
List<T> performQuery(TypedQuery<T> query);
}
public class Select<T> implements TypedQuery<T> {
protected SelectQuery delegateQuery;
public Select(Class<T> rootClass) {
this.delegateQuery = new SelectQuery(rootClass);
}
public Select(Class<T> rootClass, String entityName) {
if (entityName == null) {
this.delegateQuery = new SelectQuery(rootClass);
} else {
this.delegateQuery = new SelectQuery(entityName);
if (rootClass.isAssignableFrom(DataRow.class)) {
delegateQuery.setFetchingDataRows(true);
}
}
}
public Select<T> andQualifier(Expression qualifier) {
delegateQuery.andQualifier(qualifier);
return this;
}
public Select<T> andQualifier(String qualifier) {
delegateQuery.andQualifier(Expression.fromString(qualifier));
return this;
}
public Select<T> orQualifier(Expression qualifier) {
delegateQuery.orQualifier(qualifier);
return this;
}
public Select<T> orQualifier(String qualifier) {
delegateQuery.orQualifier(Expression.fromString(qualifier));
return this;
}
public Select<T> orderAscending(String ordering) {
delegateQuery.addOrdering(ordering, Ordering.ASC);
return this;
}
public Select<T> orderDesending(String ordering) {
delegateQuery.addOrdering(ordering, Ordering.DESC);
return this;
}
// other methods follow here...
}
3. What I haven't done yet is applying this to the new List<Object[]>
results, as the backend is not fully ready yet, and I need a better
understanding of all consequences myself.
So ... does the above sound acceptable, or are there still reservations?
Andrus
Re: performQuery generics
Posted by Lachlan Deck <la...@gmail.com>.
Just catching up on this thread FWIW...
On 31/12/2007, at 7:23 AM, Tore Halset wrote:
> On Dec 28, 2007, at 10:31 AM, Andrus Adamchik wrote:
>
>> Ok, I've played a bit with parameterizing Query... The results are
>> mixed at best. I am trying to avoid the explosion of subclasses
>> (consider that in addition to SelectQuery we have EJBQLQuery,
>> ProcedureQuery, SQLTemplate, NamedQuery, ObjectIdQuery, etc. that
>> can return a ResultSet).
>
> The underlying problem is that the fetchingDataRow flag can change
> the return type of the Query. Right? Trying to look at this from an
> outside view, I think the fetchingDataRow behavior is a good
> candidate to be represented in separate classes instead of if-
> statements.
>
> In cayenne and our projects, setFetchingDataRows(boolean) are
> mostly called right after the Query constructor and almost never
> based on some external (to the method calling the Query
> contrstructor) information. Based on this limited code base, I
> guess that most users know the return type when the Query is
> constructed.
>
>> But we are still left with 6 extra subclasses that the user will
>> have to deal with in the code, as well as the method naming issues
>> that Ari mentioned :-/
>
> Most users will not use that many Query types. In our project (as
> an example), we only use SelectQuery and SQLTemplate query and
> switching those over to typed classes will be very easy.
>
> After making the Query infrastructure typed, the creation of a
> Query can be simplified with a Factory class as some of you have
> mentioned. It could be like the Collections API "Collections" or
> Google Collections "Maps". That factory could then return the
> proper Query, not some new non-Query(-less-usable) wrapper around a
> Query.
That sounds reasonable to me.
I also agree that the following is of no real benefit...
>> <T> List<T> performQuery(Class<T> aClass, Query query);
... as generics is only of use if <T> is guaranteed to be of the same
type as that of the query's entity; not just for subsequent class-
casting help. i.e., I shouldn't be able to do the following and
compile successfully...
SelectQuery query = new SelectQuery(EntityA.class);
List<EntityB> results = context.performQuery(EntityB.class, query);
with regards,
--
Lachlan Deck
Re: performQuery generics
Posted by Tore Halset <ha...@pvv.ntnu.no>.
On Dec 28, 2007, at 10:31 AM, Andrus Adamchik wrote:
> Ok, I've played a bit with parameterizing Query... The results are
> mixed at best. I am trying to avoid the explosion of subclasses
> (consider that in addition to SelectQuery we have EJBQLQuery,
> ProcedureQuery, SQLTemplate, NamedQuery, ObjectIdQuery, etc. that
> can return a ResultSet).
The underlying problem is that the fetchingDataRow flag can change the
return type of the Query. Right? Trying to look at this from an
outside view, I think the fetchingDataRow behavior is a good candidate
to be represented in separate classes instead of if-statements.
In cayenne and our projects, setFetchingDataRows(boolean) are mostly
called right after the Query constructor and almost never based on
some external (to the method calling the Query contrstructor)
information. Based on this limited code base, I guess that most users
know the return type when the Query is constructed.
> But we are still left with 6 extra subclasses that the user will
> have to deal with in the code, as well as the method naming issues
> that Ari mentioned :-/
Most users will not use that many Query types. In our project (as an
example), we only use SelectQuery and SQLTemplate query and switching
those over to typed classes will be very easy.
After making the Query infrastructure typed, the creation of a Query
can be simplified with a Factory class as some of you have mentioned.
It could be like the Collections API "Collections" or Google
Collections "Maps". That factory could then return the proper Query,
not some new non-Query(-less-usable) wrapper around a Query.
- Tore.
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 27, 2007, at 10:55 PM, Tore Halset wrote:
>>> // new
>>> <T> List<T> performQuery(Class<T> aClass, Query query);
>
> +0. I would like to use a specific interface instead if possible.
> public <T> List<T> performQuery(PersistentSelectQuery<T> query);
>
>>> // new
>>> List<DataRow> performDataRowQuery(Query query);
>
> +0. I would like to use a specific interface instead if possible.
> public List<DataRow> performQuery(DataRowQuery query);
Ok, I've played a bit with parameterizing Query... The results are
mixed at best. I am trying to avoid the explosion of subclasses
(consider that in addition to SelectQuery we have EJBQLQuery,
ProcedureQuery, SQLTemplate, NamedQuery, ObjectIdQuery, etc. that can
return a ResultSet). I think I solved the DataRowQuery subclass
problem, by making it a decorator instead of a subclass:
public interface TypedQuery<T> extends Query {
}
public class DataRowQuery implements
TypedQuery<org.apache.cayenne.DataRow> {
protected Query query;
public DataRowQuery(Query query) {
this.query = query;
}
public SQLAction createSQLAction(SQLActionVisitor visitor) {
return query.createSQLAction(visitor);
}
...
}
But we are still left with 6 extra subclasses that the user will have
to deal with in the code, as well as the method naming issues that Ari
mentioned :-/
Andrus
Re: performQuery generics
Posted by Tore Halset <ha...@pvv.ntnu.no>.
On Dec 27, 2007, at 12:29 PM, Andrus Adamchik wrote:
> Ok, so then my next question is should we proceed with the following?
>
>> // existing
>> List performQuery(Query query);
+1
>> // existing
>> QueryResponse performGenericQuery(Query query);
+1
>> // new
>> <T> List<T> performQuery(Class<T> aClass, Query query);
+0. I would like to use a specific interface instead if possible.
public <T> List<T> performQuery(PersistentSelectQuery<T> query);
>> // new
>> List<DataRow> performDataRowQuery(Query query);
+0. I would like to use a specific interface instead if possible.
public List<DataRow> performQuery(DataRowQuery query);
My hope is that SelectQuery would become typesafe, but that would
destroy DataRow support for SelectQuery. That is not too important for
me as I only use DataRow SelectQuery in a couple of places. The second
best alternative (AFAIK) would be to create new Query classes to
handle this as I have written earlier.
Regards,
- Tore.
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
Ok, so then my next question is should we proceed with the following?
> // existing
> List performQuery(Query query);
>
> // existing
> QueryResponse performGenericQuery(Query query);
>
> // new
> <T> List<T> performQuery(Class<T> aClass, Query query);
>
> // new
> List<DataRow> performDataRowQuery(Query query);
Andrus
On Dec 27, 2007, at 1:13 PM, Aristedes Maniatis wrote:
> On 27/12/2007, at 9:18 PM, Andrus Adamchik wrote:
>
>> Here is how I see the main design abstractions of context and query:
>>
>> * "query" is a _descriptor_ of an arbitrary database operation,
>> with no knowledge of the runtime (EOF explicitly calls it
>> EOFetchSpecification, "specification" == "descriptor"). While we
>> compromised on that in the backend by introducing query callback
>> methods ('createSQLAction', 'route'), those methods are extension
>> points that are not visible to the user.
>>
>> * "context" is the environment facade object that executes queries.
>>
>> I don't see how we will benefit by turning this around?
>
> OK. I guess I perceived the purposes of these classes differently:
> what does executing a query which returns datarows have to do with
> manipulating the context (that is it is a function of the context)?
> But philosophical debates are really not the important point here.
> Whether the query is performed on the context or on the query, the
> problems to solve are:
>
> * how does the query find out which persistent class it is operating
> on (the root class)
> * how does it know which type of data to return (datarows, map, list
> of persistent objects, something else we invent in the future)
>
> So let's not get sidetracked by my red herring of which class the
> performQuery function lives in.[1] Let's pretend I didn't raise
> that issue.
>
> Ari
>
>
>
> [1] http://www.bikeshed.com/
>
>
>
> -------------------------->
> ish
> http://www.ish.com.au
> Level 1, 30 Wilson Street Newtown 2042 Australia
> phone +61 2 9550 5001 fax +61 2 9550 4001
> GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
>
>
>
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 27/12/2007, at 9:18 PM, Andrus Adamchik wrote:
> Here is how I see the main design abstractions of context and query:
>
> * "query" is a _descriptor_ of an arbitrary database operation, with
> no knowledge of the runtime (EOF explicitly calls it
> EOFetchSpecification, "specification" == "descriptor"). While we
> compromised on that in the backend by introducing query callback
> methods ('createSQLAction', 'route'), those methods are extension
> points that are not visible to the user.
>
> * "context" is the environment facade object that executes queries.
>
> I don't see how we will benefit by turning this around?
OK. I guess I perceived the purposes of these classes differently:
what does executing a query which returns datarows have to do with
manipulating the context (that is it is a function of the context)?
But philosophical debates are really not the important point here.
Whether the query is performed on the context or on the query, the
problems to solve are:
* how does the query find out which persistent class it is operating
on (the root class)
* how does it know which type of data to return (datarows, map, list
of persistent objects, something else we invent in the future)
So let's not get sidetracked by my red herring of which class the
performQuery function lives in.[1] Let's pretend I didn't raise that
issue.
Ari
[1] http://www.bikeshed.com/
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 27, 2007, at 10:52 AM, Aristedes Maniatis wrote:
>> // existing
>> List performQuery(Query query);
>>
>> // existing
>> QueryResponse performGenericQuery(Query query);
>>
>> // new
>> <T> List<T> performQuery(Class<T> aClass, Query query);
>>
>> // new
>> List<DataRow> performDataRowQuery(Query query);
>>
>> It still makes things a bit more verbose (and redundant, as Class
>> will be specified twice - once in query constructor, and second
>> time in the context), but everything about generics is verbose and
>> redundant after all :-/ I guess after that we can deprecate
>> 'setDataRows' on SelectQuery and SQLTemplate, (as well as
>> "ObjectContext.performQuery(Query)", although we can keep this one
>> around longer as it is heavily used by everybody).
>
> Can't say I'm wild about the idea. I think if we are asking users to
> change their code in some way to suit a new API that we may as well
> design that API to survive the next 5 years without being
> embarrassed about it. I think this is one of the most used and most
> visible parts of the entire Cayenne API and we want users to say
> "that Cayenne is so clear and easy to use". After all, ORM is often
> one of the most complicated libraries an average developer is likely
> to use.
>
> I understand the desire to keep backward compatibility and a simple
> migration path, but if a change needs to be made it should be the
> best possible and not the smallest possible.
I agree with the general sentiment, but I don't agree that it applies
to what I am suggesting.
'performQuery' in Cayenne has been around for 6 years (its analog
'objectsWithFetchSpecification' from EOF world - for much longer). I
don't see how anything we discussed here makes it obsolete or
embarrassing. We are simply porting this API to Java 5. And my
prediction is that it will be as usable in 5 years as it is now.
Here is how I see the main design abstractions of context and query:
* "query" is a _descriptor_ of an arbitrary database operation, with
no knowledge of the runtime (EOF explicitly calls it
EOFetchSpecification, "specification" == "descriptor"). While we
compromised on that in the backend by introducing query callback
methods ('createSQLAction', 'route'), those methods are extension
points that are not visible to the user.
* "context" is the environment facade object that executes queries.
I don't see how we will benefit by turning this around?
Andrus
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 26/12/2007, at 9:49 PM, Andrus Adamchik wrote:
> // existing
> List performQuery(Query query);
>
> // existing
> QueryResponse performGenericQuery(Query query);
>
> // new
> <T> List<T> performQuery(Class<T> aClass, Query query);
>
> // new
> List<DataRow> performDataRowQuery(Query query);
>
> It still makes things a bit more verbose (and redundant, as Class
> will be specified twice - once in query constructor, and second time
> in the context), but everything about generics is verbose and
> redundant after all :-/ I guess after that we can deprecate
> 'setDataRows' on SelectQuery and SQLTemplate, (as well as
> "ObjectContext.performQuery(Query)", although we can keep this one
> around longer as it is heavily used by everybody).
Can't say I'm wild about the idea. I think if we are asking users to
change their code in some way to suit a new API that we may as well
design that API to survive the next 5 years without being embarrassed
about it. I think this is one of the most used and most visible parts
of the entire Cayenne API and we want users to say "that Cayenne is so
clear and easy to use". After all, ORM is often one of the most
complicated libraries an average developer is likely to use.
I understand the desire to keep backward compatibility and a simple
migration path, but if a change needs to be made it should be the best
possible and not the smallest possible.
> Since Query interface is another core Cayenne abstraction, adding
> utility methods to it is causing a similar ripple effect as adding
> them to ObjectContext... E.g. a DeleteBatchQuery would now have a
> 'getResult' method... Also we'd have to add update methods returning
> update counts, select methods returning Object[] (something we are
> implementing now per JPA), etc... In other words, too many loose ends.
Wouldn't they be added just to SelectQuery and not to the interface?
The query interface seems to represent something much more abstract -
the idea of an interaction with the database.
Again, because I don't have the skills or knowledge (not to mention
time) to make the required changes within the heart of the query
classes I feel like a hack making all these suggestions - but I hope
they are useful ideas.
Ari Maniatis
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 26, 2007, at 4:14 AM, Aristedes Maniatis wrote:
>
> On 25/12/2007, at 2:20 AM, Andrus Adamchik wrote:
>
>> - List<?> performQuery(Query query);
>> + @SuppressWarnings("unchecked")
>> + List performQuery(Query query);
>>
>
> I agree. Returning <?> from a function call does nothing for type
> safety at all, so it may as well not be there. Pretty much everwhere
> we return <?> from a function should be considered suspect and some
> of the places <Object> can be found are also candidates for
> refactoring.
Will go ahead with this change.
> On 22/12/2007, at 2:53 AM, Andrus Adamchik wrote:
>
>> The wrapper idea (SelectBuilder) is sort of along the lines of
>> reversing the API that you suggested. But let me comment on why I
>> am not a fan of adding new ways to run a query to the
>> ObjectContext. When implementing ROP and nested contexts I found
>> out that having so many "redundant" or "utility" methods in a
>> context object makes it extremely hard to implement multi-layer
>> contexts. So I'd prefer that we keep ObjectContext API as lean as
>> possible, and pass all query parameters in the query object itself.
>
> What we are discussing comes down to whether the query type
> (datarows or persistent objects) should be injected into the Query
> as part of the constructor (as per your new SelectBuilder or Tore's
> subclass idea), sometime after (as per the existing Query) or when
> you want the query executed (as per my idea with multiple return
> functions).
IMO this is a rather accurate analysis.
> List<Artists> result = query.getResult(Artist.class, context);
> Map<String, Artists> result = query.getResultAsMap(Artist.class,
> context, NAME_PROPERTY);
> List<DataRow> result = query.getResultAsDataRows(Artist.class,
> context);
Since Query interface is another core Cayenne abstraction, adding
utility methods to it is causing a similar ripple effect as adding
them to ObjectContext... E.g. a DeleteBatchQuery would now have a
'getResult' method... Also we'd have to add update methods returning
update counts, select methods returning Object[] (something we are
implementing now per JPA), etc... In other words, too many loose ends.
So, weighing all options, I think we can make a bit of a concession on
the ObjectContext expansion, to provide minimally generics-friendly
API without changing how Cayenne works, and move all utilities (such
as getResultAsMap fetch) to the new Builders:
// existing
List performQuery(Query query);
// existing
QueryResponse performGenericQuery(Query query);
// new
<T> List<T> performQuery(Class<T> aClass, Query query);
// new
List<DataRow> performDataRowQuery(Query query);
It still makes things a bit more verbose (and redundant, as Class will
be specified twice - once in query constructor, and second time in the
context), but everything about generics is verbose and redundant after
all :-/ I guess after that we can deprecate 'setDataRows' on
SelectQuery and SQLTemplate, (as well as
"ObjectContext.performQuery(Query)", although we can keep this one
around longer as it is heavily used by everybody).
Hmm... actually this would allow us to make Class argument optional in
the constructor of SelectQuery, and SQLTemplate, that most often than
not fetches DataRows anyways.
I need to do more analysis how the new JPA related result sets will
play with this... But what is everyone's feeling on that? Seems like
the least invasive change to me....
> I have to confess to not quite understanding the boundaries between
> Expression and Query. 99% of the times that it is used, a Query is
> just an Expression with Ordering and a root entity.
Yes, Expression is a qualifier *part* of the SelectQuery.
> But I don't understand why that root entity isn't part of the
> Expression itself - does an expression have much meaning without a
> root entity? It is always implied within the Expression, since you
> can't create an expression which is meaningful for Artist.class and
> then create a new SelectQuery(Painting.class).
Coincidentally, since Expressions are not bound to a java class, you
can use the same expression with a group of classes that do not
inherit from each other, but simply share property names. I think of
that as a nice side effect. The design idea is that a SelectQuery is a
composition of the parts, Expression being one of them.
Andrus
Typesafe property declarations [Was: ExpressionFactory -> ExpressionBuilder [Was: performQuery generics]]
Posted by Andrus Adamchik <an...@objectstyle.org>.
Sorry, the subject was wrong, as I started writing a message about
something entirely different initially :-)
On Dec 27, 2007, at 12:53 PM, Andrus Adamchik wrote:
> BTW, here is another data point for this discussion. Here is a type-
> safe bean framework written by one of the Cayenne users:
>
> http://ujoframework.pponec.net/
>
> The framework itself has no direct relation to Cayenne, but I wonder
> if with proper code generation, we could use a similar approach of
> type-safe property declaration to achieve type safety in the
> ExpressionFactory methods. The fact that Cayenne doesn't tell you
> that you can't match say a String against a BigDecimal in an
> Expression has been causing some grief to our users already.
>
> Andrus
>
>
> On Dec 27, 2007, at 10:50 AM, Aristedes Maniatis wrote:
>> On 27/12/2007, at 1:29 AM, Mike Kienenberger wrote:
>>
>>> String pathSpec = Artist.ARTIST_NAME_PROPERTY;
>>> or
>>> String pathSpec = Painting.ARTIST_PROPERTY + "." +
>>> Artist.ARTIST_NAME_PROPERTY;
>>>
>>> Note also, that the pastSpec has the same suffix -- all you need
>>> to do
>>> is adjust the prefix to account for a different root:
>>
>> But that is exactly my point: the Expression object is different
>> depending on where the root of the query starts. Sure the end part
>> of the expression is the same, but that doesn't help anyone reuse
>> an Expression object and so it is intrinsically tied to a root
>> entity.
>>
>> Andrus's point is that they can be reused if you happen to name
>> your properties the same between multiple entities, but the thought
>> hadn't even occurred to me that this would be useful - I'd consider
>> it poor practice to make these sorts of assumptions about the
>> model. Someone could change the model and break a whole lot of code
>> without a single compile time exception.
>>
>> Expression e = ExpressionFactory.matchExp(Painting.NAME_PROPERTY,
>> "bob");
>>
>> Sure, the Painting.NAME_PROPERTY and Artist.NAME_PROPERTY might
>> both be equal("name") and the above expression will work for both
>> artists called bob and paintings called bob. But does anyone
>> actually use Cayenne like this? What happens when someone changes
>> Artist.NAME_PROPERTY to Artist.LASTNAME_PROPERTY?
>>
>>
>> Could it then make sense to do this:
>>
>> 1. Expression<Artist> e =
>> ExpressionFactory.matchExp(Artist.NAME_PROPERTY, "bob",
>> Artist.class);
>> 2. e = e.andExpr(ExpressionFactory.matchExp(Artist.SUBURB_PROPERTY,
>> "Newtown", Artist.class));
>> 3. e = e.andExpr(ExpressionFactory.matchExp(Artist.STATE_PROPERTY,
>> "NSW")); <--- no compile time checking here
>> 4. e = e.andExpr(ExpressionFactory.matchExp(Artist.NAME_PROPERTY,
>> "bob", Painting.class)); <--- oops, error here
>>
>> The benefits:
>>
>> * when combining expressions (eg. andExp) type safety can be
>> enforced at compile time (if the root class is passed for each
>> expression which makes up the whole)
>> * the root entity class can flow through to query without needing
>> to be passed again: simpler, cleaner API. Expression and Query get
>> generified together.
>> * works like EJBQL which people will probably get to know over time
>>
>> The cons:
>>
>> * can't use the trick of sharing expressions across properties with
>> identical names from several entities
>> * something makes me think that line 3 may run into problems with
>> generics internal to Cayenne code and we will not be able to force
>> the cast of <?> into <Artist>. But I remain hopeful that there is a
>> way, and even if not it doesn't kill the idea, just make it more
>> awkward to use when building long compound expressions.
>> * significant change to the previous API (but if we are going to
>> force people to make a change, better that it be done as best we
>> can right now)
>>
>>
>> I know this is just half the problem (the other half is how to
>> decide whether you are getting datarows or some other type back
>> from a query), so I just throw this up as an idea. To my mind it is
>> the philosophically appropriate place to inject the root entity.
>>
>>
>> Ari Maniatis
>>
>>
>> -------------------------->
>> ish
>> http://www.ish.com.au
>> Level 1, 30 Wilson Street Newtown 2042 Australia
>> phone +61 2 9550 5001 fax +61 2 9550 4001
>> GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
>>
>>
>>
>
>
ExpressionFactory -> ExpressionBuilder [Was: performQuery generics]
Posted by Andrus Adamchik <an...@objectstyle.org>.
BTW, here is another data point for this discussion. Here is a type-
safe bean framework written by one of the Cayenne users:
http://ujoframework.pponec.net/
The framework itself has no direct relation to Cayenne, but I wonder
if with proper code generation, we could use a similar approach of
type-safe property declaration to achieve type safety in the
ExpressionFactory methods. The fact that Cayenne doesn't tell you that
you can't match say a String against a BigDecimal in an Expression has
been causing some grief to our users already.
Andrus
On Dec 27, 2007, at 10:50 AM, Aristedes Maniatis wrote:
> On 27/12/2007, at 1:29 AM, Mike Kienenberger wrote:
>
>> String pathSpec = Artist.ARTIST_NAME_PROPERTY;
>> or
>> String pathSpec = Painting.ARTIST_PROPERTY + "." +
>> Artist.ARTIST_NAME_PROPERTY;
>>
>> Note also, that the pastSpec has the same suffix -- all you need to
>> do
>> is adjust the prefix to account for a different root:
>
> But that is exactly my point: the Expression object is different
> depending on where the root of the query starts. Sure the end part
> of the expression is the same, but that doesn't help anyone reuse an
> Expression object and so it is intrinsically tied to a root entity.
>
> Andrus's point is that they can be reused if you happen to name your
> properties the same between multiple entities, but the thought
> hadn't even occurred to me that this would be useful - I'd consider
> it poor practice to make these sorts of assumptions about the model.
> Someone could change the model and break a whole lot of code without
> a single compile time exception.
>
> Expression e = ExpressionFactory.matchExp(Painting.NAME_PROPERTY,
> "bob");
>
> Sure, the Painting.NAME_PROPERTY and Artist.NAME_PROPERTY might both
> be equal("name") and the above expression will work for both artists
> called bob and paintings called bob. But does anyone actually use
> Cayenne like this? What happens when someone changes
> Artist.NAME_PROPERTY to Artist.LASTNAME_PROPERTY?
>
>
> Could it then make sense to do this:
>
> 1. Expression<Artist> e =
> ExpressionFactory.matchExp(Artist.NAME_PROPERTY, "bob", Artist.class);
> 2. e = e.andExpr(ExpressionFactory.matchExp(Artist.SUBURB_PROPERTY,
> "Newtown", Artist.class));
> 3. e = e.andExpr(ExpressionFactory.matchExp(Artist.STATE_PROPERTY,
> "NSW")); <--- no compile time checking here
> 4. e = e.andExpr(ExpressionFactory.matchExp(Artist.NAME_PROPERTY,
> "bob", Painting.class)); <--- oops, error here
>
> The benefits:
>
> * when combining expressions (eg. andExp) type safety can be
> enforced at compile time (if the root class is passed for each
> expression which makes up the whole)
> * the root entity class can flow through to query without needing to
> be passed again: simpler, cleaner API. Expression and Query get
> generified together.
> * works like EJBQL which people will probably get to know over time
>
> The cons:
>
> * can't use the trick of sharing expressions across properties with
> identical names from several entities
> * something makes me think that line 3 may run into problems with
> generics internal to Cayenne code and we will not be able to force
> the cast of <?> into <Artist>. But I remain hopeful that there is a
> way, and even if not it doesn't kill the idea, just make it more
> awkward to use when building long compound expressions.
> * significant change to the previous API (but if we are going to
> force people to make a change, better that it be done as best we can
> right now)
>
>
> I know this is just half the problem (the other half is how to
> decide whether you are getting datarows or some other type back from
> a query), so I just throw this up as an idea. To my mind it is the
> philosophically appropriate place to inject the root entity.
>
>
> Ari Maniatis
>
>
> -------------------------->
> ish
> http://www.ish.com.au
> Level 1, 30 Wilson Street Newtown 2042 Australia
> phone +61 2 9550 5001 fax +61 2 9550 4001
> GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
>
>
>
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 27/12/2007, at 1:29 AM, Mike Kienenberger wrote:
> String pathSpec = Artist.ARTIST_NAME_PROPERTY;
> or
> String pathSpec = Painting.ARTIST_PROPERTY + "." +
> Artist.ARTIST_NAME_PROPERTY;
>
> Note also, that the pastSpec has the same suffix -- all you need to do
> is adjust the prefix to account for a different root:
But that is exactly my point: the Expression object is different
depending on where the root of the query starts. Sure the end part of
the expression is the same, but that doesn't help anyone reuse an
Expression object and so it is intrinsically tied to a root entity.
Andrus's point is that they can be reused if you happen to name your
properties the same between multiple entities, but the thought hadn't
even occurred to me that this would be useful - I'd consider it poor
practice to make these sorts of assumptions about the model. Someone
could change the model and break a whole lot of code without a single
compile time exception.
Expression e = ExpressionFactory.matchExp(Painting.NAME_PROPERTY,
"bob");
Sure, the Painting.NAME_PROPERTY and Artist.NAME_PROPERTY might both
be equal("name") and the above expression will work for both artists
called bob and paintings called bob. But does anyone actually use
Cayenne like this? What happens when someone changes
Artist.NAME_PROPERTY to Artist.LASTNAME_PROPERTY?
Could it then make sense to do this:
1. Expression<Artist> e =
ExpressionFactory.matchExp(Artist.NAME_PROPERTY, "bob", Artist.class);
2. e = e.andExpr(ExpressionFactory.matchExp(Artist.SUBURB_PROPERTY,
"Newtown", Artist.class));
3. e = e.andExpr(ExpressionFactory.matchExp(Artist.STATE_PROPERTY,
"NSW")); <--- no compile time checking here
4. e = e.andExpr(ExpressionFactory.matchExp(Artist.NAME_PROPERTY,
"bob", Painting.class)); <--- oops, error here
The benefits:
* when combining expressions (eg. andExp) type safety can be enforced
at compile time (if the root class is passed for each expression which
makes up the whole)
* the root entity class can flow through to query without needing to
be passed again: simpler, cleaner API. Expression and Query get
generified together.
* works like EJBQL which people will probably get to know over time
The cons:
* can't use the trick of sharing expressions across properties with
identical names from several entities
* something makes me think that line 3 may run into problems with
generics internal to Cayenne code and we will not be able to force the
cast of <?> into <Artist>. But I remain hopeful that there is a way,
and even if not it doesn't kill the idea, just make it more awkward to
use when building long compound expressions.
* significant change to the previous API (but if we are going to force
people to make a change, better that it be done as best we can right
now)
I know this is just half the problem (the other half is how to decide
whether you are getting datarows or some other type back from a
query), so I just throw this up as an idea. To my mind it is the
philosophically appropriate place to inject the root entity.
Ari Maniatis
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Mike Kienenberger <mk...@gmail.com>.
On Dec 26, 2007 9:56 AM, Andrus Adamchik <an...@objectstyle.org> wrote:
> Hope you feeling better now.
Hi Andrus,
Merry Christmas!
I'm back to about 90% of where I was before I got sick. It may take
several months before I'm fully recovered.
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
Hi Mike,
Hope you feeling better now.
To mention a related thing, Cayenne actually supports an expression
format that includes the root entity in the expression itself. That
would be EJBQL, and it is much more loaded...
Andrus
On Dec 26, 2007, at 4:29 PM, Mike Kienenberger wrote:
> On Dec 25, 2007 9:14 PM, Aristedes Maniatis <ar...@ish.com.au> wrote:
>> I have to confess to not quite understanding the boundaries between
>> Expression and Query. 99% of the times that it is used, a Query is
>> just an Expression with Ordering and a root entity. But I don't
>> understand why that root entity isn't part of the Expression itself -
>> does an expression have much meaning without a root entity? It is
>> always implied within the Expression, since you can't create an
>> expression which is meaningful for Artist.class and then create a new
>> SelectQuery(Painting.class).
>
> Yes, you can!
>
> Consider this:
>
> select artist where artist_name = 'bob'
> select painting p, artist a where p.artist_id = a.id and artist_name
> = 'bob'
>
> The expression is the same for both -- only the object path is
> different.
>
> Expression e = ExpressionFactory.matchExp(String pathSpec, "bob");
>
> where
>
> String pathSpec = Artist.ARTIST_NAME_PROPERTY;
> or
> String pathSpec = Painting.ARTIST_PROPERTY + "." +
> Artist.ARTIST_NAME_PROPERTY;
>
> Note also, that the pastSpec has the same suffix -- all you need to do
> is adjust the prefix to account for a different root:
>
> String pathSpec = rootPathSpecPrefix + Artist.ARTIST_NAME_PROPERTY;
>
> where
>
> rootPathSpecPrefix = "" for Artist, Painting.ARTIST_PROPERTY + "." for
> painting, Gallery.PAINTING_LIST_PROPERTY + "." +
> Painting.ARTIST_PROPERTY + "." for gallery.
>
Re: performQuery generics
Posted by Mike Kienenberger <mk...@gmail.com>.
On Dec 25, 2007 9:14 PM, Aristedes Maniatis <ar...@ish.com.au> wrote:
> I have to confess to not quite understanding the boundaries between
> Expression and Query. 99% of the times that it is used, a Query is
> just an Expression with Ordering and a root entity. But I don't
> understand why that root entity isn't part of the Expression itself -
> does an expression have much meaning without a root entity? It is
> always implied within the Expression, since you can't create an
> expression which is meaningful for Artist.class and then create a new
> SelectQuery(Painting.class).
Yes, you can!
Consider this:
select artist where artist_name = 'bob'
select painting p, artist a where p.artist_id = a.id and artist_name = 'bob'
The expression is the same for both -- only the object path is different.
Expression e = ExpressionFactory.matchExp(String pathSpec, "bob");
where
String pathSpec = Artist.ARTIST_NAME_PROPERTY;
or
String pathSpec = Painting.ARTIST_PROPERTY + "." + Artist.ARTIST_NAME_PROPERTY;
Note also, that the pastSpec has the same suffix -- all you need to do
is adjust the prefix to account for a different root:
String pathSpec = rootPathSpecPrefix + Artist.ARTIST_NAME_PROPERTY;
where
rootPathSpecPrefix = "" for Artist, Painting.ARTIST_PROPERTY + "." for
painting, Gallery.PAINTING_LIST_PROPERTY + "." +
Painting.ARTIST_PROPERTY + "." for gallery.
Re: performQuery generics
Posted by Aristedes Maniatis <ar...@ish.com.au>.
On 25/12/2007, at 2:20 AM, Andrus Adamchik wrote:
> - List<?> performQuery(Query query);
> + @SuppressWarnings("unchecked")
> + List performQuery(Query query);
>
I agree. Returning <?> from a function call does nothing for type
safety at all, so it may as well not be there. Pretty much everwhere
we return <?> from a function should be considered suspect and some of
the places <Object> can be found are also candidates for refactoring.
On 22/12/2007, at 2:53 AM, Andrus Adamchik wrote:
> The wrapper idea (SelectBuilder) is sort of along the lines of
> reversing the API that you suggested. But let me comment on why I am
> not a fan of adding new ways to run a query to the ObjectContext.
> When implementing ROP and nested contexts I found out that having so
> many "redundant" or "utility" methods in a context object makes it
> extremely hard to implement multi-layer contexts. So I'd prefer that
> we keep ObjectContext API as lean as possible, and pass all query
> parameters in the query object itself.
What we are discussing comes down to whether the query type (datarows
or persistent objects) should be injected into the Query as part of
the constructor (as per your new SelectBuilder or Tore's subclass
idea), sometime after (as per the existing Query) or when you want the
query executed (as per my idea with multiple return functions). Right
now I am looking at it from the point of view of an end-user : that is
really what we are talking about here. I really don't know the Query
code in enough depth to contribute to the decision about structuring
the code for multi-layer contexts. But I would have thought that any
functions like
List<Artists> result = query.getResult(Artist.class, context);
Map<String, Artists> result = query.getResultAsMap(Artist.class,
context, NAME_PROPERTY);
List<DataRow> result = query.getResultAsDataRows(Artist.class, context);
are just a thin veneer over the existing code much like your
SelectBuilder. What I like about it instead of making the choice at
constructor time is that user code might build a query in one place
but decide whether to return DataRows, PersistentObjects or even a
Map, Set, etc right at the point where the data is needed. That is,
queries could be more reusable than they are now. On the other hand
SelectQuery has oddities like addCustomDbAttribute() which imply the
need for a subclass such as Tore suggested (or a separate
implementation of the Query interface) - they have no place in a
SelectQuery which returns PersistentObjects.
I have to confess to not quite understanding the boundaries between
Expression and Query. 99% of the times that it is used, a Query is
just an Expression with Ordering and a root entity. But I don't
understand why that root entity isn't part of the Expression itself -
does an expression have much meaning without a root entity? It is
always implied within the Expression, since you can't create an
expression which is meaningful for Artist.class and then create a new
SelectQuery(Painting.class).
Ari Maniatis
-------------------------->
ish
http://www.ish.com.au
Level 1, 30 Wilson Street Newtown 2042 Australia
phone +61 2 9550 5001 fax +61 2 9550 4001
GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 21, 2007, at 2:30 PM, Tore Halset wrote:
>
>> The more I think about it, the more I feel like we'll have to do
>> some kind of Context/Query wrapper that supports generics
>> specifically for select queries, similar to what was suggested here:
>>
>> https://issues.apache.org/cayenne/browse/CAY-877
>
> A wrapper is fine, but do you think this is fixable in Query as well?
Regardless of what I said elsewhere on the topic (the ideas I am still
planning to pursue), current version of performQuery is soooo painful
to use. We MUST do something about it. Here is one idea - revert
generics version of "performQuery":
--- framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/
cayenne/ObjectContext.java (revision 606412)
+++ framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/
cayenne/ObjectContext.java (working copy)
@@ -156,7 +156,8 @@
/**
* Executes a selecting query, returning a list of persistent
objects or data rows.
*/
- List<?> performQuery(Query query);
+ @SuppressWarnings("unchecked")
+ List performQuery(Query query);
This would at least allow users to assign results without warnings:
List<Artist> x = context.performQuery(q);
So while there's no added type-safety, at least such API won't result
in a downgrade in functionality.
Comments?
Andrus
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
On Dec 21, 2007, at 2:30 PM, Tore Halset wrote:
> A wrapper is fine, but do you think this is fixable in Query as well?
Unfortunately I can't figure out how to make it work without breaking
core Cayenne abstractions :-/
BTW, here is a skeleton of a parameterized wrapper that I tried to
sketch out. Not complete of course, but the general idea. In a way
aligned with the JPA query (so we can reuse it in the JPA/Cayenne
bridge), only more user-friendly, as we can supply all the "hints" via
dedicated methods.
// usage:
ObjectContext context = ...
SelectBuilder<Artist> query = SelectBuilder.create(context,
Artist.class);
List<Artist> artists = query.setQueryName("XYZ").addParameter("X",
"1").fetch();
// wrapper class:
public class SelectBuilder<T> {
static enum ResultType {
OBJECT, DATA_ROW, OBJECT_ARRAY, SCALAR
};
protected Class<?> entityClass;
protected ObjectContext context;
// factory methods
public static <T> SelectBuilder<T> create(ObjectContext context,
Class<T> resultClass) {
return new SelectBuilder<T>(context, resultClass, ResultType.OBJECT);
}
public static SelectBuilder<DataRow> createForDataRows(
ObjectContext context, Class<?> resultClass) {
return new SelectBuilder<DataRow>(context, resultClass,
ResultType.DATA_ROW);
}
private SelectBuilder(ObjectContext context, Class<?> entityClass,
ResultType type) {
...
}
// configuration methods
public SelectBuilder<T> setQueryName(String mappedQueryName) {
...
return this;
}
public SelectBuilder<T> addParameter(String key, Object value) {
...
return this;
}
// .... etc.
// execution methods
public List<T> fetch() {
...
}
public T fetchSingleObject() {
...
}
public T fetchByPk(int id) {
...
}
}
Andrus
Re: performQuery generics
Posted by Tore Halset <ha...@pvv.ntnu.no>.
Hello.
On Dec 21, 2007, at 11:01 , Andrus Adamchik wrote:
> This is something I wanted to improve as well, and as you correctly
> pointed out, data rows option makes it hard...
Is it possible to reconsider the data rows flag and use a separate
class for such queries instead? We could keep the existing SelectQuery
for backward compatibility and add PersistentSelectQuery and
DataRowSelectQuery (and perhaps others for the new stuff I do not
know) that have a fixed expected query return type.
SelectQuery would be a Query<Object>
DataRowSelectQuery would be a Query<DataRow>
PersistentSelectQuery would be a Query<? extends Persistent>
Similarly, we could keep NamedQuery and introduce NamedDataRowQuery
and NamedPersistentDataRowQuery if needed.
That way, ctxt.performQuery would know what to return.
> There are a few other stumbling blocks as well, such as the new
> scalar or mixed scalar/object queries, updating queries, etc.
Is that a problem? If the query is expected to return something, it
should be possible to know something about the return type before
executing the query. Or am I way off here? I have not tried the new
scalar stuff or EJBQL Query.
> The more I think about it, the more I feel like we'll have to do
> some kind of Context/Query wrapper that supports generics
> specifically for select queries, similar to what was suggested here:
>
> https://issues.apache.org/cayenne/browse/CAY-877
A wrapper is fine, but do you think this is fixable in Query as well?
Regards,
- Tore.
Re: performQuery generics
Posted by Andrus Adamchik <an...@objectstyle.org>.
This is something I wanted to improve as well, and as you correctly
pointed out, data rows option makes it hard... There are a few other
stumbling blocks as well, such as the new scalar or mixed scalar/
object queries, updating queries, etc.
The more I think about it, the more I feel like we'll have to do some
kind of Context/Query wrapper that supports generics specifically for
select queries, similar to what was suggested here:
https://issues.apache.org/cayenne/browse/CAY-877
With EJBQL support by Cayenne, we can include a bunch of interesting
functions in such wrapper (such as aggregates, etc.)
Andrus
On Dec 21, 2007, at 11:41 AM, Tore Halset wrote:
> Hello.
>
> I typically do a lot of
>
> SelectQuery query = new SelectQuery(MyEntity.class, qual);
> List entities = ctxt.performQuery(query);
>
> What I want instead is something like
>
> SelectQuery<MyEntity> query = new SelectQuery<MyEntity>(qual);
> List<MyEntity> entities = ctxt.performQuery(query);
>
> How can I get rid of the "MyEntity.class, " argument to the
> SelectQuery constructor?
> What about SelectQuery#setFetchingDataRows that changes the expected
> return type?
> What do you think?
>
> Regards,
> - Tore.
>