Heiko,

I have updated my document a bit. Find it enclosed.

Maybe you should put it somewhere it gets archived (sourceforge doesn't 
remember it, sot it's kind of lost...)

stF



Value Objects and self references.

We want to answer the following question : "how do I represent a tree-like structure with entity EJB and Value Objects using XDoclet ?".

So, here we choose to go for entity bean. We won't discuss that further (freedom of choice :)). As you may know, entity EJB access through RMI can be costly and that's why one wants to use Value Objects. However, we introduce a small catch here : we want the entity EJB to have some relationships with other entity EJB of the same class. This might seem easy but it makes life a little bit harder when we want to create the Value Objects.

We choose to implement a family tree. The family tree is made of Persons. Each person has a name and an age. Moreover, a Person knows its offspring. Each child is itself a Person. We just described a 1-N (1 person can have 0..N children) uni-directional relationship (we don't implement the fact that a child knows its father, nor brothers).

Coding this, we end up with :

/** 
 * @ejb.interface-method
 * @ejb.relation name="offspring"
 *               role-name="father"
 *               target-ejb="Person"
 *               target-role-name="child"
 * 
 * @jboss.target-relation related-pk-field="name"
 *                        fk-column="father"
 * 
 * @ejb.value-object
 *     type="java.util.Collection"
 *     relation="external"
 *     aggregate="foundryserver.beans.interfaces.PersonLightValue"
 *     aggregate-name="Offspring"     
 *     members="foundryserver.beans.interfaces.PersonLocal"
 *     members-name="Offspring" 
 * 
 * @return Returns the offspring. */

public abstract Collection getOffspring();

/** 
 * @ejb.interface-method */

public abstract void setOffspring( Collection offspring);
    
/** 
 * @ejb.interface-method */
    
public abstract PersonLightValue getPersonLightValue();

/**  
 * @ejb.interface-method */

public abstract PersonValue getPersonValue();

To be able to run XDoclet on the EJB the first time, pay attention to the following points :

  • Make sure you import the PersonLightValue class in the code, although it doesn't exist yet (XDoclet will generate it itself). This also allows you tell XDoclet in which package that class must be generated.
  • Add an abstract getter to your entity to get the PersonLightValue object. Read the "Exposing Generated Methods" part for more info. Keep in mind that the PersonValue object will require PersonLightValue to represent the offspring. Because PersonValue is generated by XDoclet, XDoclet will need to have an access to PersonLightValue and the only place where you can allow this access is in the bean declaration itself.

Now a bit of explanation. The most important thing is to understand that a Value Object cannot reference other Value Objects of the same class. Think about this situation : one wants to retrieve a person that has some children. He gets the Value Object for that person. Now, if that Value Object can have references to the children, then those children's Value Objects are added to the person's Value Object. However, to be complete, those children's Value Objects must in turn have the Value Objects of their children, etc. This is a recursive loop and doing so can lead us to have a graph of Value Object covering the whole database. That is, the retrieval of one Value Object could imply the retrieval of the whole database. Not good. To prevent that, we break the loop. The strategy is to represent the children with a different class of Value Object than the father : the PersonLightValue (it's just a name we choose, there's no particular naming convention). The particularity of this one is that it doesn't have any kind of reference to its own children, thus avoiding any uncontrollable recursive construction. Of course this is limiting a bit because we'll have to retrieve the children of Person manually.

To summarize, there will be two kinds of Value Objects. The "main" one that has all information you need, including references to the offspring. The references will point to other Value Objects, the "light" one, representing the children, without any reference to their own children (break the loop).

An example of code to access the whole thing :

PersonLocal stef = PersonUtil.getLocalHome().findByPrimaryKey("Stefan");
PersonValue spv = stef.getPersonValue();
out.println("<br/> "+spv.getName()+" is "+spv.getAge());

PersonLightValue offspring[] = spv.getOffsprings();

if( offspring != null)
    for( int i=0; i<offspring.length; i++)
        out.print( offspring[i].getName() + " - ");

To be complete, just have a look at the relationship declaration (@ejb.relation tag). It uses the target-ejb and target-role attributes. It's because we define a uni-directional relationship (see "How Relationships Work" for more information). For the same set of reasons, we also have to add the @jboss.target-relation tag. Finally, note that we used an aggregation. This is arbitrary.

Reply via email to