Greetings!
o.k., I start with an apology for a lengthy email, but I am searching
for some best-practices, that I could not find addressed properly, and I
would like to get the ideas from the iBatis experts about "iBatis
best-practices" in designing model/transfer objects:
Maybe I should start with an example to be concrete and get to the point
(1): say, we have persons and projects, a project can have multiple
persons assigned, and one person can work in multiple projects, hence a
classical m:n relationship on database level.
Now my "simple" question is, how the iBatis experts would model this. I
personally see (if I understand iBatis correctly) some options, I want
to express to two "most radical" ones I see and ask for suggestions:
(the relational model is clear: one table PERSON, one table PROJECT one
table PERSON_PROJECT containing references to PERSON and PROJECT.) O.k.
lets move to the objects:
(Remark: the Java code here is mostly pseudo code for illustration)
***
(Option A)
Person and Project objects, both contain various "primitive" variables
like "name, email, projectname" and so on. Now, the relation between
projects and persons is modeled by a list in each object like so:
class Person {
...
List<Project> projects;
...
}
class Project {
...
List<Person> persons;
...
}
ok, now the problem starts. I additionally assume, that we have a DAO
that should encapsule the database access (using Spring templates in the
iBatis implementation). the DAO might look like so:
class IBatisPersonDAO implements PersonDAO {
...
public Person getPerson(String email) {...}
...
}
When I call this method (and allow to directly access the lists, or via
getPersons() accessor) what happens then? Several issues: again the most
extreme (and obviously not desired one): the DAO goes recursively
through persons and projects and returns the full tree of objects. This
is obviously a bad idea, as this could potentially retrieve all persons
and projects (not to mention the complexity in a more realistic
example). So we can forget this one.
Now, Hibernate has a lazy load feature here (as far as I understand it),
where you actually get a proxy object from Person back and this object
loads the projects "on demand"; but this is problematic too; first the
lazy load has to be configured properly (in a more complex setting...)
and second, this only works as long as we have an established connection
or you preload the stuff using appropriate hql statements...
ok, I think iBatis does not support such Proxy objects. What would be
the solution then? I see several options: manually create such a proxy
object and on getting the list of persons perform the actual SQL query
using iBatis.
Alternatively, an option might be that this call to the DAO could be
done in the Person object approx like so:
class Person {
...
List<Project> projects;
...
public List getProjects() {
...
personDAO = spring.getBean("PersonDAO");
return personDAO.getProjects(personID);
}
}
This would have the advantage, that only objects are loaded when they
are needed, but the disadvantage, that I have a connection from the
model/transfer object to the DAO, comments?? (Plus the issue: should the
whole list of objects/person been loaded, what if this is a huge list...)
(similar problems arise with 1:m connections in principle)
Despite for all these issues also the saving is a logical problem. What
happens when I call personDAO.save(person). Does it recursively check
the project list on changes?
My argument here is, that when we model something like so, then it is
not natural OO style anyway and the developer has to be "persistence
aware", or it gets very complex in implementation, because after all,
the OO developer has to keep in mind when and what to save by explicit
calls. So is this probably just a "pseudo" OO model?
***
(Option B)
ok, I see a radical different implementation style: Person and Project
object ONLY hold the primitive variables, no lists, hence are NOT aware
by themselves, that there is a connection between them, however, I
implement additional DAO methods like so:
class IBatisPersonDAO implements PersonDAO {
...
public Person getPerson(String email) {...}
public List<Project> getAssignedProjects(personId/email/...) { ... }
...
}
And vice versa in the Project DAO. What I like about this solution is,
that both models stay very simple (the objects and the database schema
plus the DAO logic), we have a clean decoupling between transfer
objects/model and persistence technology. Everything that happens is
clear and transparent: it is clear what is loaded, and it is clear what
is saved and it is also clear in which object is loaded at what time
(namely when the appropriate DAO method is used, not when the proxy
object does something).
Currently I feel attracted to this option by the named reasons.
However, the clear drawback is, that we do not use OO "features" at all
and this is just a bunch of flat transfer objects then without obvious
references.
***
Obviously there are some options in between these two and also some
options more I would think. I am searching for what iBatis experienced
developers feel are best-practices. I already search for some time and
practically found nothing of use. Book and Internet examples are
typically trivial and do not really give a clue about a proper design
here. Or they deal with inheritance, which is also an interesting
problem field.But I feel that this is of utmost importance also for the
documentation to lead newbies to a proper track!
(btw.: the iBatis manual mentions lazy loadings but actually never
explains properly how to use it)
ok, this was very lengthy. Maybe still some iBatis experts have an
opinion and would want to share it with me?
thank you very much in advance!
Alex
|