I was continuing to brainstorm on this issue of modeling a
many-to-many relationship in a DAO and I wanted to share my
thoughts.  If this makes sense to anyone, I am considering posting
it as an article on my website, but I want to make sure I am talking
logically.  I seem to run into this problem everytime I try to use
the DAO pattern and I think it might be helpful for a lot of people.
Mind that this discussion does not consider the possibility of using
lazy loading, which circumvents the whole issue since relationships
are estabished at request time with Hibernate (unless open session
in view is not used).

---------------

To model a many-to-many relationship in a database, you have to
create an intermediate table (also known as a correlation or join
table).  Additionally, you may have other information associated
with the relationship (apart from the relationship itself) that is
stored in this table which describes the relationship.  It is when
these extra attributes exist that it becomes necessary to introduce
"Enterprise Objects," which are not realworld objects, but rather
represent the relationship between two entities and the metadata
associated with that relationship.

A realworld example to consider is a movie and its cast members.
Actors will play in many movies in a single career, each of those
jobs representing a different role in a movie.  On the other hand, a
single movie will star many different aspiring actors, each which
play those same roles.  The relationship is as follows:

Movie -> MovieRole -> Actor

A MovieRole will contain both a reference to the movie and a
reference to the actor along with the role which that actor played
in that movie (additionally data could potentially include the
salary of the actor in the movie, order of appearance, etc).
However, modeling this in the object oriented world becomes
incovenient when it comes to data reading.  Say we need to retrieve
all the MovieRoles for a movie using the following interfaces:

MovieDAO
getMovie(movieId)
getMovieRoles(movieId)

ActorDAO
getActor(actorId)
getMovieRoles(actorId)

In theory, this proves to be very flexible: If you want to discover
what actors played in a particular movie, or the movies which star a
particular actor, you can simply write join queries (using the
primary and foreign keys) to return the information that interests
you.

However, you'll start to run into a snag when trying to retrieve
this information to the model. You might have a Movie element that
contains MovieRole elements, but then what? If you include the
actor information in the MovieRole element, it will be repeated for
every movie containing that actor! Similarly, if you choose to have
an Actor element that contains MovieRole elements, the movie data is
duplicated for each actor in the movie.  This is not so much of a
problem of memory based on the fact that object references will be
used, but it becomes a resource issue when more information is
retrieved than necessary (and that retrival requires data access
crossover betwen DAOs).

This can be solved simply by splitting up the many-to-many
relationship into two seperate objects, CastMember and ActingGig,
with the following model

 +------------+
 | CastMember |
 |------------|
 | role       |
 | salary     |
 | actor      |
 +------------+

 +------------+
 | ActingGig  |
 |------------|
 | role       |
 | salary     |
 | movie      |
 +------------+

(The question arises, what happens when there is a large amount of
metadata.  The metadata could become another object called
ActingJob)

These two objects will be "owned" by movie and actor respectively.
Now, mind where we place them:

+ MovieDAO

getMovie(movieId)

/**
 * Get all the movie+role objects (ActingGig) for this actor.
 * In essence, we answer "what movies has this actor starred in
 * and what role did that actor play in the movie?"
 */
getActingGigs(actorId)

+ ActorDAO

getActor(actorId)

/**
 * Get all the actor+role objects (CastMember) in this movie.
 * In essence, we answer "what actors star in this movie and
 * what role do they play?"
 * /
getCastMembers(movieId)

At first glance, these arrangements may seem backwards, until the
model above is studied.  The CastMember object contains an actor,
which we can get from our ActorDAO class and the ActingGig contains
a movie which we can get from our MovieDAO.

The main issue:
--------------

Consider if we had to populate MovieRole in each of the two DAOs.
In our MovieDAO, we could get the movie information for movieId and
the associated actorIds...but then we have to make a
behind-the-scenes call to ActorDAO to populate the actor
information.  Parallel to that problem, if we retrieve information
for an actor and that actor's movieIds, it is necessary to make a
behind-the-scenes call to MovieDAO to populate the movie
information.  This can be circumvented in several ways.  One is that
we can require a Movie object be passed to getMovieRoles in ActorDAO
and similarly require a Actor object be passed to getMovieRoles in
MovieDAO.

...I am still searching for a silver bullet, but at least I have
several ideas that will work.

-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Daniel Allen, <[EMAIL PROTECTED]>
http://www.mojavelinux.com/
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
[Frodo]: "I wish it need not have happened in my time." 
[Gandalf]: "So do I, and so do all who live to see such times. 
But that is not for them to decide. All we have to decide is 
what we do with the time that is given to us.'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


-------------------------------------------------------
This SF.net email is sponsored by: IBM Linux Tutorials.
Become an expert in LINUX or just sharpen your skills.  Sign up for IBM's
Free Linux Tutorials.  Learn everything from the bash shell to sys admin.
Click now! http://ads.osdn.com/?ad_id=1278&alloc_id=3371&op=click
_______________________________________________
Struts-apps mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/struts-apps

Reply via email to