You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user-java@ibatis.apache.org by Alexander Schatten <al...@gmx.at> on 2006/12/28 19:21:45 UTC

Modeling objects - Best practices wanted ;-)

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

Re: Modeling objects - Best practices wanted ;-)

Posted by Alexander Schatten <al...@gmx.at>.
Larry Meadors wrote:
> Holy crap that *was* a long email.
>
yes, I am very sorry for tha *g*

some additional questions, if I may :-)))
>
> So far, so good. I always feel warm and fuzzy when people design the
> database first. :-)
>
I do not know: I am very conservative to that respect, I feel, that the 
most precious thing in todays world is (consistent) data, not programs 
or objects, and this is why I start like so. moreover I find it easier 
to play around with a data model, fill it with test data and stuff. this 
is also why I could never really get warm with systems like db4o as 
interesting as they are indeed!

>>
>
> Hmm, OK, problem #1: can a person exist without a project? Can a
> project exist without a person? If the answer is ever yes to those, I
> would not create my objects like that. A person is one thing, a
hm... have to think about this. however, I am not sure if this argument 
is really striking; a list can also be "NULL" moreover, the connection 
is potentially here, so...

> Lazy-loading rhymes with lazy-coding for a reason: it is what it is.
> When you are writing an application and you need a person object, you
> know if you will need a list of the projects for that person or not.
> If you need them, get them, if you don't need them, don't get them.
>
Well, but this has consequences! this was the reason for my example: I 
would, e.g., want to know the persons of a project and vice versa; now: 
when I retrieve one project instance, I would (according to your 
suggestion) also retrieve the list of persons associated. that is fine: 
but now comes the problem: these person objects also have lists for 
projects and so on (the recursive issue). What to do? keep the lists 
NULL? but then this is really bad too! because then I have to know (in 
the business objects) where a person object is coming from: when it was 
retrieved via project, it might be NULL in projectslist because of 
loading, not because the user is not associated to any projects. this is 
really fishy. here comes in the concept of lazy loading, no?


>>
>> 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.
>
> OO is a tool, not a goal.
>
yes, for sure, but we use OO models for good reasons: object hierarchies 
help us understanding how entities are related plus they allow easier 
navigation within connected objects.

>
> OK, so to recap my opinions on this are:
>
> 1) Design your database first. If it's right, the object model will
> fall in line.
> 2) Make data-access fine-grained, then let your service layer pull
> them together.
> 3) OO is a tool, not a goal - use it if it makes things easier or
> better, not because you need it to be cool.
>
> If you need to have a Person with a list of projects, you can easily
> create an aggregate object:
>
> PersonAndProjects{
>  private Person person;
>  private List<Project> projectList;
> }
>
> You can even use the groupBy attribute to get that object (or even a
> list of them) using only 1 select statement - super fast.
>
yes, this is an option I too I have to consider.

however, I still feel not very comfortable. What is your idea about my 
accessor idea (accessing the DAO on "getProjects" method in the Person 
object for example)?


thank you very much for reading my long email and giving me your ideas.


However, I would really appreciate it very much, if I could get some 
more opinions?



thank you very much, and have a great 2007




Alex





Re: Modeling objects - Best practices wanted ;-)

Posted by Larry Meadors <lm...@apache.org>.
Holy crap that *was* a long email.

I just got back from X-mas vacation, but I'll add some comments
inline, and add the disclaimer now that this is sort of like talking
about religion or politics, very subjective...

On 12/28/06, Alexander Schatten <al...@gmx.at> wrote:
> 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.
>

So far, so good. I always feel warm and fuzzy when people design the
database first. :-)

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

Hmm, OK, problem #1: can a person exist without a project? Can a
project exist without a person? If the answer is ever yes to those, I
would not create my objects like that. A person is one thing, a
project is another. I'd not link them that way just because they are
linked that way in the database, and this is where the ORM guys start
squawking on me.

IMO, a database model and an object model are two very different
things. Trying to make them match 100% for anything but the most
trivial application is a profoundly bad idea, and why ORM is a
retarded technology looking for a place to happen. Period.

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

This is IMO another problem that really only rears it's head when you
try to make your application be an in-memory database. I generally
write fine-grained data access methods, then use a service layer to
make them more application specific.

Lazy-loading rhymes with lazy-coding for a reason: it is what it is.
When you are writing an application and you need a person object, you
know if you will need a list of the projects for that person or not.
If you need them, get them, if you don't need them, don't get them.

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

I agree. This approach is simpler and more efficient.

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

OO is a tool, not a goal.

>
>                                          ***
>
> 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?

OK, so to recap my opinions on this are:

1) Design your database first. If it's right, the object model will
fall in line.
2) Make data-access fine-grained, then let your service layer pull
them together.
3) OO is a tool, not a goal - use it if it makes things easier or
better, not because you need it to be cool.

If you need to have a Person with a list of projects, you can easily
create an aggregate object:

PersonAndProjects{
  private Person person;
  private List<Project> projectList;
}

You can even use the groupBy attribute to get that object (or even a
list of them) using only 1 select statement - super fast.

YMMV,
Larry