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.