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.
>