views:

116

answers:

3

If I have a Tree that has Apples, how should I model the fact that the Apples are had by Tree. Consider that there would be 3 database tables: tree, apple, tree_apples.

It seems to me that there would be a AppleDecorator class so that Tree can have multiple AppleDecorators and call ->save() for each one which would write the association to tree_apples. Apple does not know that it is owned by Tree.

It seems wrong to make references to the tree_apples table from the Tree class other than getting the ids of all trees because then the Tree class is referencing one table for each type of object that it has (and needs to store the fact that it has one). Even getting the Ids could be offloaded into something like an Iterator.

How should the situation where an application needs to store the fact that an object owns N other objects? (In this case my class needs to store associations for 5 other types of objects).

+2  A: 

Put the apples in a list or a set on the tree.

If you are using an O/R mapper, there will be a way to annotate or indicate that the list is a one-to-many to apples using the tree_appes table as a join table. The join can be saved using a cascading save of the list of apples (in the tree).

stevedbrown
What about without an ORM?
Dewayne
I don't think it changes very much without an ORM, that was probably not even a good thing too include. When you save the object, you would persist all of the apples in the list after you persist the tree. I think that all languages have some kind of ORM at this point though - they really are easier.
stevedbrown
Yes, but the whole question here is WHERE do you put the code that persists the objects in the list to table tree_apples?
Dewayne
The persistence logic should be in a data access object (DAO) class - you would have a method on this class along the lines of DAO.storeTree(tree) which would have the logic for the tree and subs.
stevedbrown
I think I am asking the question incorrectly. Lets assume we were using direct SQL for simplicity's sake. Would the "UPDATE tree_apples" query exist in Tree, or another class such as "Tree Apples"? If it exists in Tree it seems a bit strange to have Tree doing updates to so many other tables. If it is offloaded to another class, say TreeApples, would that new class be derived from Tree, or contain Tree?
Dewayne
I meant to say "would that new class be derived from Apple or contain an Apple".Class TreeApples extends Apple implements Listable { private _uid; public construct(treeId,appleId); // will also call parent constructor to load up the Apple public save(); public delete();}orClass TreeApples implements Listable { private _uid; private Apple _apple; public construct(treeId,appleId); // will load an Apple into this container object public save(); public delete();}
Dewayne
Apple should just be an apple. Tree would have a data structure containing apples (a list or whatever). You should check out the book the fight club guy (his name is robert paulson) recommended.
stevedbrown
+1  A: 

I would have another construct altogether for doing this:

public interface ITreeSaver {

  public void save(Tree t);
  public Tree load(String treeId);

}

You can then implement this (a primitive DAO) in any way you want. With Hibernate, raw calls to the MySQL driver, XStream, or anything else.

There is no need for Tree, Apple, or any other model object to know/understand how it gets saved or loaded.

To implement this with straight SQL calls would take something like the following:

public class SQLTreeSaver implements ITreeSaver {

 public void save(Tree t) {

  String id = t.getId();
  if(id == null || id.isEmpty()) {
   id = /*generate Id here*/;
  }

  SQL.execute("delete from TREES where id="+id);
  SQL.execute("insert into TREES values (id, prop1, prop2) ("+id+",'"+t.getProp2()+"','"+t.getProp3()+"'");

  SQL.execute("delete from APPLES where treeId="+id);

  for(Apple a : t.getApples()) {
   String appleId = a.getId();
   if(appleId == null || appleId.isEmpty()) {
    appleId = /*generate Id here*/;
   }   
   SQL.execute("insert into APPLES values (id, tree, prop1) ("+appleId+","+id+",'"+a.getProp1()+"'");
  }

 }

 public Tree load(String id) {

  Tree t = new Tree();

  if(id == null || id.isEmpty()) return t;

  ResultSet treeSet = SQL.execute("select top 1 * from TREES where id="+id);

  while(treeSet.hasNext()) {
   t.setId(treeSet.getString("id"));
   t.setProp1(treeSet.getString("prop1"));
   t.setProp2(treeSet.getString("prop2"));

   ResultSet appleSet = SQL.execute("select * from APPLES where tree="+id);

   ArrayList<Apple> appleList = new ArrayList<Apple>();

   while(appleSet.hasNext()) {
    Apple a = new Apple();
    a.setId(appleSet.getString("id");
    /* omit the following if your apples have no idea who they belong to */
    a.setTree(id);
    a.setProp1(appleSet.getString("prop1"));

    appleList.add(a);
   }

   if(appleList.size() > 0) {
    treeSet.setApples(appleList);
   }

  }

  return t;
 }

}

Please excuse bad SQL calls because I'm just trying to illustrate the point. The idea is that you've abstracted how the objects get saved from the saving/loading interface. You could easily slip in some Hibernate.

public class HibernateTreeSaver implements ITreeSaver {

 public void save(Tree t) {
  HibernateHelper.getSession().save(t);
 }

 public Tree load(String id) {
  Tree t = (Tree)HibernateHelper.getSession.load(id);
  return t;
 }

}

Now... you can see what I'm going for. You put some sort of method for choosing which implementation of ITreeSaver to use and then you've got some flexibility or adaptability. What if your client uses a database that isn't supported by Hibernate? What if they use flat-files? For a very little bit more effort I've got what I feel is a pretty good separation of concerns and the ability to easily respond and change to adapt to new situations or needs on the system.

angryundead
I'm not entirely sure why I got down-voted for this. I was pointing out that you're not going to use the Decorator pattern for this. You don't want to add the responsibility of saving/loading to the Apples or the Tree, but to some other object entirely.It's obvious that the person asking the question has a knowledge of the sql schema and the intermediary join table. As Robert Paulson pointed out: this looks like an m:n relation though. (To echo him: it may just be a bad metaphor.) In any case I've edited my response to be more appropriate.
angryundead
+4  A: 

tree_apples is only valid if an apple can belong to more than one tree. (an m:n relation)

It may be the case of just a bad metaphor, but if we stick with 1 Tree has many Apples, in a relational database typically the apple stores the reference to the tree it came from.

Tree
  TreeId
  TreeName

Apple
  AppleId
  IsRotten
  TreeId (foreign key)

For the in-memory model of the data (ie object-oriented) you may or may not have a back-pointer from Apple to Tree. i.e. you normally have tree.Apples where Apples is some sort of a collection of the Apple object, but you don't often have apple.Tree.

Robert Paulson
You're right, it's not the best metaphor. The question I'm asking is WHERE do you put the code that saves the apples that a Tree has to table tree_apples?
Dewayne
@Dewayne are you looking for something that is m:n, because the Tree-Apple relationship isn't m:n and doesn't require the TreeApple join table, which is why I ignored it (hence my comment on the appropriateness of the chosen Apple-Tree metaphor because I can't tell if you need help with a join table, or if you need help with modelling the relationship)
Robert Paulson
I can however recommend the book "Patterns of Enterprise Architecture" (Fowler). There's quite a lot devoted to the topic of loading and saving data (e.g. Data Mapper)
Robert Paulson