Generating the Persistence Layer


The example takes the Customer data model directly from the database and generates a persistence layer of POJOs. Once the persistence layer has been generated, we reverse-engineer the Java into a UML class diagram and compare the result with the original ER diagram.

The code generation process involves several steps:

1.

Produce an Ant build file for running Middlegen.

2.

Run Middlegen against the database and fine-tune the code generation options using the GUI.

3.

Generate Hibernate mapping documents.

4.

Use the Hibernate tool hbm2java to generate Java classes from the Hibernate documents.

Listing 7-2 shows the relevant extracts from the build.xml file required to run Middlegen.

Listing 7-2. Database-Generated Ant Build File
 <?xml version="1.0" encoding="UTF-8"?>   .   .   .   <!-- Use Middlegen to create hibernate files from DB -->   <target name="generate"           description="Run Middlegen">     <taskdef name="middlegen"              classname="middlegen.MiddlegenTask"              classpathref="project.class.path"/>     <middlegen appname="${name}"                prefsdir="${gen.dir}"                gui="true"                databaseurl="${database.url}"                driver="${database.driver}"                username="${database.userid}"                password="${database.password}"                schema="${database.schema}"                catalog="${database.catalog}">       <hibernate destination="${gen.dir}"                  package="${name}.hibernate"                  genXDocletTags="true"                  genIntergratedCompositeKeys="false"/>     </middlegen>   </target>   <!-- Generate Java from mapping files -->   <target name="hbm2java"           depends="generate"           description="Creates Java classes from hbm files">     <taskdef name="hbm2java"              classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"              classpathref="project.class.path" />     <hbm2java output="${gen.dir}">       <fileset dir="${gen.dir}">         <include name="**/*.hbm.xml"/>       </fileset>     </hbm2java>   </target> </project> 

The build file controls the entire code generation process. These next sections cover each step.

Running Middlegen from Ant

The Middlegen tool does the hard work of creating the Hibernate mapping documents for us. The Middlegen tool is invoked from an Ant build file, and the <middlegen> task is provided for this purpose. From the example build.xml, we have the following:

 <middlegen appname="customer"            prefsdir="gen_src"            gui="true"            databaseurl="jdbc:mysql://localhost/customer"            driver="org.gjt.mm.mysql.Driver"            username=""            password=""            schema=""            catalog=""> 

This task provides Middlegen with the information it requires to use the MySQL JDBC driver to connect to the Customer database through the attributes of databaseurl and driver. The attributes username, password, schema, and catalog provide further connection detail. These attributes are not needed if MySQL is running locally on a Windows platform.

The use of the gui attribute becomes clear shortly but basically tells Middlegen to open its visual configuration tool.

The next step is to tell Middlegen what to generate. Middlegen uses plug-ins for controlling code generation, and we use the Hibernate plug-in, which requires the nested element <hibernate> for configuration:

 <hibernate destination="gen_src"             package="customer.hibernate"             genXDocletTags="true"             genIntergratedCompositeKeys="false"/> 

The package attribute provides the Hibernate plug-in with Java package information. The next two attributes require a little more description. The genXDocletTags tells the plug-in to generate XDoclet tags in the XML mapping documents. The XDoclet tags are ignored by the mapping documents. However, the Java generation tool later in the process picks up this additional metadata.

Where we have a composite primary key, Hibernate provides the option to generate an external primary key class. The choice of either an external or an internal primary key class is specified with the genIntegratedCompositeKeys attribute. We generate our own keys externally and set this attribute to false.

With this minimal Middlegen configuration complete, we can now invoke the build file and start the tool.

The Middlegen GUI

By setting the gui attribute to TRue, we have informed Middlegen we wish to use the GUI tool to configure the output from the plug-ins. Running the example build file brings up the screen shown in Figure 7-2.

Figure 7-2. The Customer schema in the Middlegen GUI.


The GUI allows us to assist Middlegen in correctly determining the relationships between entities. From the metadata available in the database, Middlegen is unable to infer the type of relationships established by a foreign key constraint.

Tip

In preference to running the GUI, it is possible to define relationship information within the build script.


With the configuration used in our minimal build file, Middlegen assumes all foreign key constraints are modeling one-to-many relationships. Thus, on first opening the GUI, one-to-many relationships are defined between entities where a one-to-one relationship was expectedfor example, Customer and Account, Product and Item. Thankfully, the GUI allows this initial interpretation to be corrected. By pressing the <ctrl> key and clicking with the mouse on the relationship, the cardinality of the association can be corrected.

The GUI also enables the output from the Hibernate plug-in to be further fine-tuned. Selecting either an entity or property in the ER diagram displays a configuration dialog for the element in focus. In this way, we can control exactly what is to be generated for the persistence layer. This dialog is shown in the lower pane of Figure 7-2.

Warning

By default, all configuration settings made with the GUI tool are stored in the generated source directory. Cleaning all files from this directory results in these configuration settings being lost.


When satisfied with all settings, pressing the Generate button on the toolbar causes the Hibernate plug-in to generate mapping documents for each database entity. This process is visible in the console from which Middlegen was launched and provides a visual cue when the generation process has completed.

Let's assess what Middlegen has produced.

Hibernate O/R Mapping Documents

As stated earlier, the output from the <middlegen> task is not Java source but Hibernate XML mapping documents. These documents instruct Hibernate how the mapping between a Java class and a database entity is to be orchestrated. A mapping document is produced for each entity in the Customer schema. Listing 7-3 shows the XML mapping document generated for the Purchase_Order database entity.

Listing 7-3. Middlegen-Generated PurchaseOrder.hbm.xml
 <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC     "-//Hibernate/Hibernate Mapping DTD 2.0//EN"     "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" > <hibernate-mapping> <!--     Created by the Middlegen Hibernate plugin     http://boss.bekk.no/boss/middlegen/     http://hibernate.sourceforge.net/ --> <class     name="customer.hibernate.PurchaseOrder"     table="purchase_order" >     <meta attribute="class-description" inherit="false">        @hibernate.class         table="purchase_order"     </meta>     <id        name="orderId"        type="short"        column="order_id"     >        <meta attribute="field-description">           @hibernate.id            generator-            type="short"            column="order_id"        </meta>        <generator  />    </id>    <property        name="deliveryDate"        type="java.sql.Date"        column="delivery_date"        length="10"    >        <meta attribute="field-description">           @hibernate.property            column="delivery_date"            length="10"        </meta>    </property>  <!-- associations -->  <!-- bi-directional many-to-one association to Account -->  <many-to-one      name="account"            not-null="true"  >      <meta attribute="field-description">         @hibernate.many-to-one          not-null="true"         @hibernate.column name="account_id"      </meta>      <column name="account_id" />  </many-to-one>  <!-- bi-directional one-to-many association to Item -->  <set      name="items"      lazy="true"      inverse="true"  >      <meta attribute="field-description">         @hibernate.set          lazy="true"          inverse="true"        @hibernate.collection-key         column="order_id"          @hibernate.collection-one-to-many                 </meta>       <key>           <column name="order_id" />       </key>       <one-to-many                  />    </set> </class> </hibernate-mapping> 

In Listing 7-3, it is evident how the configuration is used to map Java types to database types. This ease-of-use configuration is one reason why Hibernate is proving a favorite with developers. Many of the attributes are self-explanatory. For example, the mapping of a class to a table is achieved with the <class> element:

 <class name="customer.hibernate.PurchaseOrder"        table="purchase_order"> 

Likewise, the <property> element maps properties to columns:

 <property   name="deliveryDate"   type="java.sql.Date"   column="delivery_date"   length="10"/> 

The <id> element is similar to that of the <property> element but identifies the property as being a primary key:

 <id   name="orderId"   type="short"   column="order_id">   <generator  /> </id> 

The <generator> element specifies how the primary key will be produced. A value of assigned indicates that we will supply Hibernate with the key. Alternatively, Hibernate can generate the key on our behalf from a variety of unique key-generation algorithms it provides.

Not all configuration settings are quite so easily understood, and some elements warrant further description. The <meta> element looks a little out of place: it appears to be using Javadoc notation and repeats configuration detail that is already specified in other tags. For example,

 <meta attribute="field-description">   @hibernate.many-to-one     not-null="true"   @hibernate.column name="account_id" </meta> 

These are XDoclet tags and are parsed by the XDoclet Hibernate plug-in when encountered in Java source. These tags exist because the genXDocletTags attribute was set for the nested <hibernate> element in the Ant build file. This information is picked up in the next step when Java source is generated from the Hibernate mapping documents. These XDoclet tags are written out to the Java classes that are mapped by the Hibernate XML mapping documents. Using XDoclet, should we wish, we can regenerate the mapping documents from the Java instead of from the database. XDoclet provides developers with the option to adopt an object-driven rather than data-driven generation approach.

Once the Hibernate mapping documents have been produced, we can move to the next stage of the process: generating Java from the mapping information.

From Mapping Documents to Java

Closing the GUI causes the Ant build file to run to completion. Hibernate comes with the hbm2java tool, used to generate Java from mapping documents, and the build file invokes this tool once Middlegen has completed its part of the process. The relevant section of the build.xml file is the <hbm2java> task:

 <hbm2java output="gen_src">   <fileset dir="gen_src">     <include name="**/*.hbm.xml"/>   </fileset> </hbm2java> 

This task generates a Java class for each Hibernate mapping document. Listing 7-4 shows an extract from the PurchaseOrder class produced from the PurchaseOrder.hbm.xml mapping file.

Listing 7-4. PurchaseOrder.java Extract Generated from PurchaseOrder.hbm.xml
 /**  *        @hibernate.class  *         table="purchase_order"  * */ public class PurchaseOrder implements Serializable {   /** identifier field */   private Short orderId;   /** persistent field */   private customer.hibernate.Account account;   /** persistent field */   private Set items;   /** full constructor */   public PurchaseOrder(Short orderId,                        Date deliveryDate,                        customer.hibernate.Account account,                        Set items) {     this.orderId = orderId;     this.deliveryDate = deliveryDate;     this.account = account;     this.items = items;   }   /**     *            @hibernate.many-to-one     *             not-null="true"     *            @hibernate.column name="account_id"     *     */   public customer.hibernate.Account getAccount() {     return this.account;   }   public void setAccount(customer.hibernate.Account account) {      this.account = account;   }   /**     *             @hibernate.set     *              lazy="true"     *              inverse="true"     *           @hibernate.collection-key     *            column="order_id"     *             @hibernate.collection-one-to-many     *                   *     */   public Set getItems() {     return this.items;   }   public void setItems(Set items) {     this.items = items;   }   .   .   .  } 

Comparing the Hibernate mapping documents with generated Java gives an insight into how O/R mapping is handled by Hibernate. Of note are the embedded XDoclet tags, denoted with the @hibernate namespace. The XDoclet Ant task <hibernatedoclet> can be used to drive the generation of the mapping documents from the Java source if required.

Completing the Round Trip

The generation of the persistence layer was initiated from an ER diagram. Despite a few manual changes to the DDL for producing the database schema, the entire persistence layer was constructed by code generators. The result is a set of lightweight Java objects that provide an object view of the database entities.

Reverse engineering the generated Java code into a UML class diagram enables the object relationships to be compared with the original ER diagram. Figure 7-3 shows the UML class diagram for the persistence layer.

Figure 7-3. UML class diagram mapping to Customer database schema.


Note

Adornments have been added to the diagram to show multiplicity between classes that have not been detected as part of the reverse-engineering process. However, these are only small refinements that describe what is already expressed in the generated code.


At the conclusion of the code generation process, we now have a transparent, object-based persistence layer with which to work. Best of all, other than an Ant build file, it wasn't necessary to write a line of code to produce it.

Reverse-Engineering Fallibilities

If you try to reverse-engineer the output from Hibernate using a modeling tool, you will not see the same UML class diagram as is shown in Figure 7-3. Some of the relationships will be missing.

This is not a shortcoming in the reverse-engineering capabilities of the modeling tool, but rather one of the common problems inherent in reverse engineering, even from languages as clean and elegant as Java.

The problem is that Java simply does not contain enough information for the modeling tool to be able to establish the relationship. Examining the PurchaseOrder class, we can guess that a relationship exists between PurchaseOrder and Item from the property Set items.

While we can make the assumption this attribute will house a collection of Item objects, it could contain any object type. Consequently, the modeling tool failed to show any relationship between the two classes, and the relationship had to be added manually.

This problem should be solved with the support for generics in J2SE 5.0. Thus, we can now write

 Set<Item> items; 

This will give modeling tools the hint they need to correctly identify the relationship between the two classes.




    Rapid J2EE Development. An Adaptive Foundation for Enterprise Applications
    Rapid J2EEв„ў Development: An Adaptive Foundation for Enterprise Applications
    ISBN: 0131472208
    EAN: 2147483647
    Year: 2005
    Pages: 159
    Authors: Alan Monnox

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net