Using Value ObjectsThe <valueobject> task is the XDoclet embodiment of the Value Object pattern, as described in Core J2EE Patterns . This pattern is also published in many other books and community sites around the world, and has proven to be an essential pattern to solve some EJB shortcomings. Motivation: every remote enterprise bean call may go through the network, implying a heavy performance penalty. For entity beans this is specially dangerous: every entity bean has a getter and a setter for its properties, and each of these calls would incur a remote, networked call. If your application is filling a form using the bean properties it would make a remote call for each property. No, no good. The solution is to create a coarse-grained object that contains some or all the bean properties and provide a method to create this object from the bean properties and another method to set the bean properties from such an object. This object is what we call the Value Object, a POJO (plain old java object) containing the enterprise bean property values. The following diagram illustrates this approach. {{{@todo: INSERT UML DIAGRAM HERE}}} Multiple Value Objects Strategy
Suppose you have defined a
In a scenario like the one above, it would be useful to define
multiple value objects, each one containing a different set of
properties. You could have a
Your entity bean would provide methods to obtain both value objects, and perhaps methods to update the entity from both of them. The following diagram illustrates this approach. {{{INSERT UML DIAGRAM HERE}}} Using Value Objects in XDocletXDoclet helps you to define and create your value objects. Below you can see a XDoclet-tagged Entity Bean source code, with @tags related to Value Object generation. Let's take a look at a bean marked up to generate Value Objects. Please note that this is note a complete example: there are many tags missing, only the ones related to Value Objects are presented here. package example.ejb; import javax.ejb.EntityBean; import java.util.Collection; import java.util.Date; /** * This is a Value Object usage example for XDoclet. It demonstrates the use * of most value object features like property selection, aggregation and * composition. * * @ejb.bean * type="CMP" * name="Customer" * * @ejb.value-object * name="Customer" * match="*" * * @ejb.value-object * name="CustomerLight" * match="light" */ public abstract class CustomerEJB implements EntityBean { /** * @ejb.value-object match="light" * @ejb.persistent-field */ public abstract String getId(); public abstract void setId(String id); /** * @ejb.value-object match="light" * @ejb.persistent-field */ public abstract String getName(); public abstract void setName(String name); /** * @ejb.value-object * aggregate="example.vo.ProductValue" * aggregate-name="PreferredProducts" * members="example.interfaces.Product" * members-name="PreferredProduct" * relation="external" * type="Collection" * @ejb.relation * name="Customer-Product" * role-name="customer-prefers-products" */ public abstract Collection getPreferredProduts(); public abstract void setPreferredProducs(Collection products); /** * @ejb.value-object * compose="example.vo.AddressValue" * compose-name="Addresses" * members="example.interfaces.Address" * members-name="Address" * relation="external" * type=Collection" * @ejb.relation * name="Customer-Address" * role-name="customer-has-addresses" */ public abstract Collection getAddresses(); public abstract void setAddresses(); /** * @ejb.pesistent-field */ public abstract int getNumberOfChildren(); public abstract void setNumberOfChildren(int nof); /** * @ejb.persistent-field */ public abstract boolean getLikesFruitLoops(); public abstract void setLikesFruitLoops(boolean loops); /** * @ejb.persistent-field */ public abstract boolean getLikesCheerios(); public abstract void setLikesCheerios(boolean cheerios); /** * @ejb.interface-method */ public abstract CustomerValue getCustomerValue(); /** * @ejb.interface-method */ public abstract CustomerLightValue getCustomerLightValue(); /** * @ejb.interface-method */ public abstract void setCustomerValue(CustomerValue value); }
As you can see, there is only one tag you need to write to instruct
XDoclet to generate value objects:
Value Object Declaration
The first thing to do is to inform XDoclet which value objects it
will generated for your bean. At class level, introduce the
The
How Relationships WorkStarting with a given source entity and the target on the other side of a relation, there are 1:1, 1:n, n:1, and n:n relations. Internally to XDoclet, a relation is tracked separately of either side of the relation using the name attribute of ejb.relation. If both sides of a relation have an @ejb.relation tag with a matching name attribute (the "name" attribute is the key that is used to uniquely identify the relationship), information that would otherwise be incomplete can be inferred. But when the target relation does not have corresponding reverse visibility of the source in the relation and there is no @ejb.relation tag in the target with a matching name attribute, there are *some* cases where additional information needs to be provided to XDoclet in lieu of the missing information. The @ejb.target-relation tag provides the ability to provide this information. Thus, it is only needed or used when there is one-way visibility on a relation and the information that would normally be provided at the target is critical to the complete set of information that is necessary to generate the information about the relation as a whole. The primary case of where this is necessary is where you have an 1:n unidirectional link (there is no link visibility on the n side of the link). Since 1:n links are modeled with the n side keeping a foreign key reference of the 1 side, attempting to generate a 1:n link without reverse visibility leaves XDoclet without the critical information it needs to know: the related-pk-field attribute. Using the target-relation tag allows this information to be provided. It's possible to model a 1:1 link this way, where the schema visibility is target-to-source, but the desired design visibility is source to target (for example, creating a new application on top of a legacy schema.) In this case the target-relation tag is used to describe the schema details, even though the generated APIs are reversed of that and under normal 1:1 circumstances, it would be fine to store the foreign key in the source entity. n:n and n:1 relations are the easy cases, because the source entity contains the foreign key of the related entity. Whether or not there is reverse visibility is irrelevant to XDoclet since it has all the information it needs about the relation in the ejb.relation tag. Aggregation and Composition
Value objects can have
Aggregation should be used when the related object is not directly
dependant on the one we're declaring the value object.
Composition should be used when the related object is directly
dependent on the one we're declaring the value object, and cannot
exist without it. The creation of the related object is
responsibility of the entity we're coding.
Aggregation and composition may be confusing at first. As a rule of thumb, considers object creation responsibility: if the entity we're coding isn't responsible for creating the related entity, then it's aggregation. If it is responsible for this, then it's composition. We ask you to take a look at the generated code so you can really grasp the difference between the two.
Now let's break down the many parameters that are needed to
accomplish a multi valued property on a value object. The
The
The
The
The
Last, the
Exposing Generated Methods
Please notice the last three methods in this example. The Bean
implementation class generated by XDoclet will contains them, but by
default they won't be included in the remote or local interface for the
bean. If you need to change that, you should declare them as abstract
in your beans an tag them as you wish. In this example, we want the
methods exposed in the remote interface, so we tag them with
@ejb.interface-method. We don't want the
Customizing the &lt;valueobject&gt; task
The
You can change the target package using a nested
<valueobject> <packageSubstitution packages="ejb" substituteWith="valueobjects"/> </valueobject>
This tells XDoclet to substitute all packages names ending in "ejb" with
a package name ending in "valueobjects". The
Now, if you don't like the default naming pattern for value objects, you
can change it by setting the
<valueobject pattern="{0}VO"> <packageSubstitution packages="ejb" substituteWith="valueobjects"/> </valueobject> Modifying the generated value objectXDoclet provides merge points in the value object template that you can use to add custom code to the generated value objects. If you don't know about merge points, you should read about them now .
The merge point is just before the closing brace of the generated
class and is called
|