I haven't found a single, complete example of a circular relationship in an entity. I promise that any information I get during this thread, I'll add to a document in the wiki or somewhere.
What I have works, but it is extremely inefficient. Consider a simple Category object that contains a circular reference to itself. This allows a Category to have children, and those categories can have categories, etc. This is basically a tree structure - it could be a list of folders on a hard drive, or an organizational structure in a LDAP directory. Anything like that.
Code:
class Category {
private String _id;
private String _name;
private Category _parent;
private Set _children = new HashSet();
public void addChildCategory(Category childCategory) {
Category parent = childCategory.getParent();
if (parent != null)
parent.removeChildCategory(childCategory);
childCategory.setParent(this);
_children.add(childCategory);
}
public void removeChildCategory(Category childCategory) {
childCategory.setParent(null);
_children.remove(childCategory);
}
// Obligitory equals, hashcode, and toString method.
}
The table definition looks something like:
Code:
create table CATEGORY (
CATEGORY_ID VARCHAR(40) not null,
PARENT_ID VARCHAR(40) null,
CATEGORY_NAME VARCHAR(255) not null,
constraint PK_CATEGORY primary key (CATEGORY_ID)
)
I have been using the following mapping file:
Code:
<class
name="us.oh.state.dot.jpp.domain.Category"
table="CATEGORY"
dynamic-update="false"
dynamic-insert="false">
<id
name="id"
column="CATEGORY_OID"
type="java.lang.String"
unsaved-value="null">
<generator class="uuid.hex" />
</id>
<property
name="name"
type="java.lang.String"
update="true"
insert="true"
column="NAME_TXT" />
<many-to-one
name="parent"
class="my.package.Category"
cascade="save-update"
outer-join="auto"
update="true"
insert="true"
column="PARENT_OID" />
<many-to-one
name="children"
class="my.package.Category"
cascade="all"
outer-join="true"
update="true"
insert="true"
column="PARENT_OID" />
So, what's the problem. Well, at first nothing seemed to be out of the ordinary until I was doing some load testing.
I created the top level category object (parent = null), and then I inserted 500 additional category objects using code similar to the following:
Code:
parentCategory = new Category("Parent");
categoryDAO.createCategory(parentCategory);
Category lastCategory = parentCategory;
for (int i = 0; i < 500; i++) {
Category cat = new Category("Category " + i);
categoryDAO.createCategory(cat);
lastCategory.addChildCategory(cat);
categoryDAO.updateCategory(lastCategory);
lastCategory = cat;
}
If the mapping is functionally correct, it may be excessive. As anyone who looks at the table definition, the entire relationship is built on the PARENT_ID being set on the original insert of a child category. The step of updating a Set of children to a parent category is just an unnecessary database activity, since the parent category is relatively unaffected by the action.
Where should I focus my effort to reduce the amount of database activity? The DAO design requires all database activity to take place in the DAO, therefore the getChildren() method cannot be lazily loaded.