| Sign In/My Account | View Cart |
Lightweight R/O Mapping
Pages: 1, 2, 3, 4, 5, 6, 7
As with any tool or technique, the approach we've discussed has limitations.
This article has demonstrated a reversed R/O mapping approach compared to conventional O/R mapping. The complexity of the mapping task, the so-called impedance mismatch between object-oriented and relational systems, has been reduced by defining the relational data model as the reference model for the object domain and by the utilization of stored procedures especially for task of writing objects into a RDBMS (Relational Database Management System). The mapping is implemented by annotations, a language feature of Java 1.5. This approach is supported and demonstrated by a framework called Amber.
Amber is a small framework, easy to learn and easy to use. There are just a couple of classes to be handled and they are very close to JDBC. The interfacing between database and JavaBean classes is accomplished via annotations. There is no need for XML descriptors anymore. Since XML is not easily readable by humans, this is a plus. That also means the mapping description between database and application lies in one single location, the bean class. Amber also provides a constraint checking mechanism to allow content validation, which is not discussed here to keep this article short.
Amber does one thing and it does it well: map database columns and query parameters to JavaBean properties. No more, no less. Amber is no silver bullet, and it does not solve the problems that are tackled by large, industry O/R frameworks.
Amber has proven its worth in a business environment. At Impetus, we've developed a sales force solution for one of Germany's largest mail order companies, in Java using MS SQL Server, that handles all database interaction with Amber. We've had no change in the API since its inception this spring (with the coming of J2SE 5.0) and there were no significant problems in using it.
Norbert Ehreke is a consultant and software developer currently affiliated as a senior development lead with Impetus Unternehmensberatung GmbH in Frankfurt, Germany.
Return to ONJava.com.
Showing messages 1 through 12 of 12.
List<Foo> list1 = ArrayList<Foo>();
List<Bar> list2 = ArrayList<Bar>();
List list1 = ArrayList();
List list2 = ArrayList();
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( "..." );
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?
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
}
So, when I wrote that
ArrayList<T> list = reader.executeCreateBeanList( "..." );
will have the compiler issue a warning, I am right.
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.
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?
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.
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?
Et voila, there you have all of the Jedi with a collection of their respective fighters. Note that we have not coded the reading of the Fighter collection into the Jedi class. This would mean tight coupling between the Jedi and the Fighter.
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.
BeanReader<Class<Jedi>> reader = new BeanReader<Class<Jedi>>(Jedi.class, connector);
Collection<Jedi> jediList = reader.executeCreateBeanList("blah");
Middlegen also generates O/R mappings based on the database.