I am using Hibernate 3.0 with annotations
My problem is that I am trying to create a simple tree structure for a basic CRM system and I wish to enforce the rule that no two siblings may have the same name.
I have a base-class Content and e.g. two subclasses Page and Folder using the one-table per class-hierarchy model (i.e. with a Discriminator column).
Every Content object has a parent property and a String name property.
It is illegal to have two objects with the same parent and the same name.
There seem to be two solutions to my problem:
1) Implement an Interceptor which checks for the pre-existence of another Content object with the same name and parent as the entity being persisted or updated.
2) Implement @PrePersist et al methods on the entities themselves.
If I use 1) I am binding my implementation to Hibernate and, in the general case (i.e. if I wish to enforce more complicated rules for other properties) I will need a switch statement on the class of the entity to choose the correct DAO to which the checks should be forwarded. Thus every time I add a new Content subclass to the system I need to modify the Interceptor to be aware of the subclass.
If I use 2) then I am forced either to allow Hibernate to load all siblings so that I can iterate over them to determine if one already has the name I wish to assign, or the entity must have access to the relevant DAO in order that it can determine the existence of a same-named sibling through a simple query rather than having to load the entire child set into memory. Viz:
Code:
/**
* I don't like this because if there are many children it is very wasteful of
* memory and CPU intensive
**/
@PrePersist
public void onPrePersist() throws Exception {
Folder p = this.getParent();
/**
* This is the root folder so it has no parent
**/
if(p == null) {
return;
}
for(Content c : p.getChildren()) {
if(c.getName().equals(this.getName())) {
throw new Exception("Synonyms are not allowed);
}
}
/**
* This is preferable for efficiency
**/
@PrePersist
public void onPrePersistUsingDAO() throws Exception {
ContentDAO dao = ContentDAOFactory.getDaoFor(this);
Content sibling = dao.getContentByNameAndParentId(this.getName(), this.getParent().getId()); // BTW. How can I get access to the parent_id property??
if(content != null) {
throw new Exception("Synonyms not allowed");
}
}
Sidenote: I use the onPrePersist method and annotations and call it from my Interceptor so that the code will carry on working if deployed within an EJB3 or JPA EntityManager.
So what I wish to know is:
a) Is there a recommended way of doing this sort of thing e.g. using some sort of collection for a Folder's children which is keyed on name and which Hibernate understands so that I can rewrite the above method as:
Code:
@PrePersist
public void onPrePersist() throws Exception {
Content sibling = this.getParent().getChildren().findByKeyValue("name",this.getName());
if(sibling != null) {
throw new Exception("Synonyms not allowed");
}
}
With the hope that Hibernate will turn the findByKeyValue() call into a query.
or
b) Is there some way of asking the PersistenceManager to redirect all persist and update calls through a DAO so that the entity itself does not need to know what is going on? (If you say 'that is what the Interceptor is for' I will understand)
Finally, my preference is for the onPrePersistUsingDAO() approach since it encapsulates entity class specific code where I believe it belongs. I do understand that this breaks the "POJOs are dumb and don't know they are persistent" philosophy. Do I get a slap on the wrist for even thinking this? Or is it acceptable to admit that persistent entities should know that they are persistent and have a storage mechanism?
I have to admit that to me the POJO+DAO model seems to be a thinly disguised struct+function call. It breaks inheritance and polymorphism and requires code using the entities to do a lot of type-checking to determine which DAO to use.
I hope I am just being, um, let's say - unimaginative? - about this!
Thanks for any help and advice you can give me.
Duncan
}