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
