Women in Technology

Hear us Roar



Article:
  Lightweight R/O Mapping
Subject:   Generics and Roundtrips
Date:   2005-12-08 22:01:09
From:   Norbert.Ehreke
Sean,


thank you for your candid thoughts. Let me clarify some of the issues you have raised.


It's important to understand Java Generics are a compiletime feature. This


List<Foo> list1 = ArrayList<Foo>();
List<Bar> list2 = ArrayList<Bar>();

becomes that

List list1 = ArrayList();
List list2 = ArrayList();

after compilation. The purpose of a BeanReader is to construct an object of an arbitrary class at runtime via reflection. The code BeanReader<T> can give me no information about class T at runtime, because T does not exist then. Assuming that your code might work I'll still need the specific argument T.class when instantiating the BeanReader. So, when I wrote that

ArrayList<T> list = reader.executeCreateBeanList( "..." );

will have the compiler issue a warning, I am right. Please
check "Tiger: A Developers Notebook" on page 29.


Still, you've raised a philosophical question: Suppose your
code works, I'd agree that it is explicit, but is it more concise? Considering type erasure and the fact that I need T.class to dynamically construct the required object (say, of type Jedi) would it be better to have a generified class BeanReader<Class<T>> to get rid of the unchecked type warning even though this information cannot be accessed for reflection? It might be nice to read, but seems cumbersome to code, wouldn't you say?


Then there is the issue you raised about roundtripping. You are
right: In the example you brought up, the code I showed would
perform poorly. What I did not show is that Amber supports you,
if you wanted to join the Jedis and the Fighters on the database return a result set that contains Jedis (redundantly in their respective columns) and their Fighters (individually). You'd then create a class that holds a Jedi plus one single Fighter, load a list of this class (1 database query) and wrap it in mapper code where you'd extract the Jedis with their Fighter list. Roughly, wrapper class might look like this.


class JediFighter {
private Jedi _jedi;
pirvate Fighter _fighter;
public JediFighter() {
_jedi = new Jedi();
_fighter = new Fighter();
}
// annotated Jedi accessors map to _jedi
// annotated Fighter accessors map to _fighter
}

The mapper code (in the client) would take a number of JediFighters and give you back a list of Jedis with their Fighters. It's not automatic and it requires an additional loop in the client, but when you need to minimize database calls, it does work with no roundtrips.


However it seems to me that we agree that table joins should be done in the database. The same goes for filtering. This begs the question, why would you want to load 10000 Jedis into the client? If the user will filter them, this should be done in the database, right?


I hope I shed some light on the issues you raised. Thanks again
for contributing your thoughts.

Main Topics Oldest First

Showing messages 1 through 1 of 1.

  • Generics and Roundtrips
    2005-12-12 10:11:37  SeanReilly [View]


    So, when I wrote that

    ArrayList<T> list = reader.executeCreateBeanList( "..." );

    will have the compiler issue a warning, I am right.


    Ahem. While technically true that you are able to understand why the compiler has an issue with your code, that doesn't change the fact that you weren't correctly dealing with the issue.

    From your article:


    The compiler will issue a warning here, since the reader only knows at runtime what kind of class it is going to create. Because of type erasure in the generics implementation of J2SE 5.0, it is not possible to safely cast the result. And, sadly, it is also not possible to write the BeanReader class as BeanReader<Jedi> for the same reason. In short, using Java reflection and generics don't mix.


    The original article claims that this kind of operation is "not possible". I provided you with a code example that proves that it is, if you use the correct syntax. You then replied that you were correct in describing the original compiler warning. This doesn't have much to do with the very relevant fact that the framework and example use the wrong syntax.


    you've raised a philosophical question: Suppose your code works, I'd agree that it is explicit, but is it more concise? Considering type erasure and the fact that I need T.class to dynamically construct the required object (say, of type Jedi) would it be better to have a generified class BeanReader<Class<T>> to get rid of the unchecked type warning even though this information cannot be accessed for reflection? It might be nice to read, but seems cumbersome to code, wouldn't you say?


    Your philosophical objections to the correct syntax are between you and the people at Sun responsible for Java language syntax. Until such time as the offical language spec changes to accomodate your preference, choosing convenient syntax over over correct syntax is a bug. That is precisely why the compiler warns you about it.


    You are
    right: In the example you brought up, the code I showed would
    perform poorly. What I did not show is that Amber supports you,
    if you wanted to join the Jedis and the Fighters on the database return a result set that contains Jedis (redundantly in their respective columns) and their Fighters (individually). You'd then create a class that holds a Jedi plus one single Fighter, load a list of this class (1 database query) and wrap it in mapper code where you'd extract the Jedis with their Fighter list.


    Ah, I see. By "supports you", you really mean "allows you to do all of the work yourself". The primary requirement of an O/R framework is to convert relations into object hierarchies (hence the name). A framework that doesn't do this is basically useless. All this code provides is the simple ability to use reflection to unmarshall a simple result set into a simple object (or list of objects), but not a hierarchy. In other words, this is something that one could eventually use to build an O/R framework, but it isn't one itself.


    This begs the question, why would you want to load 10000 Jedis into the client? If the user will filter them, this should be done in the database, right?


    10000 was used as an example to illustrate the ridiculous absurdity of the example's performance. The code is still bad even if it returns only a half dozen results, but it is less obvious to the untrained eye.