Home > Articles > Programming > Java

Java Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

A Modular Example of Model-Driven Architecture

Last updated Mar 14, 2003.

Before I get into the details of the modeler plug-in that I have built and how to use it, know that this is not a complete solution, but rather describes the steps and provides a basis from which you can build your own MDA solution.

Here are some observations about the generated classes as they apply to my environment (and which you can customize to suit your needs):

  • I am using Hibernate as my persistence technology and I try to use the JPA interfaces instead of Hibernate interfaces whenever possible
  • For my data access layer I am using Hibernate as it integrates with Spring, so all of the DAO implementation classes that I generate are annotated with Spring's @Repository annotation, extend HibernateDaoSupport, and access Hibernate's session through Spring's HibernateTemplate
  • I am using Maven as my build manager, so I chose to integrate the code generation through a Maven plug-in
  • I am using ArgoUML and exporting an XMI document that is used by by the plug-in to read the class diagrams

For a sample project we will build the domain model and DAO classes for a simple blogging tool. There are four entities:

  • User: a user has a name, email, address and password, and can write blogs and comment on blogs
  • Blog: contains a title, summary, and contents of a blog, the date that it is published, references a User as its author, and can have zero or more Comments
  • Comment: a comment has a title and content, is a comment about a single blog, and is written by a single user
  • Category: Blogs can have one or more category that describe what it is about, such as “Java” or “High Powered Lamps”

Figure 1 shows a screen shot of the class diagram that defines these entities.

Figure 1. Domain Model Class Diagram

Much of the class diagram should look familiar to you, with the main difference being all of the stereotypes assigned to the classes, to some of the fields, and to the associations. Stereotypes are denoted by the format “<<stereotypeName>>”, and figure 1 defines the following stereotypes:

  • Entity: each class is annotated with an <<Entity>> annotation denoting that it is a domain entity. This will later be contrasted with <<Repository>> classes that represent data access objects; note that this stereotype is expected to exist in the same package as the classes that implement it
  • pk: the primary key for each entity is identified by the <<pk>> stereotype; this stereotype is expected to exist outside of all packages
  • date: the Blog's publishDate field is identified by a <<date>> stereotype denoting that it represents a date, as opposed to a <<time>> or a <<timestamp>>; this stereotype is expected to exist outside of all packages
  • lazy: association ends that reference objects lazily, meaning that the referenced objects should be loaded only if they are requested, are denoted by the <<lazy>> stereotype; this stereotype is expected to exist outside of all packages
  • eager: association ends that reference objects eagerly, meaning that the referenced objects should be loaded at the same time that the originating objects are loaded, are denoted by the <<eager>> stereotype; this stereotype is expected to exist outside of all packages

The second class diagram, shown in figure 2, shows the data access objects defined in the UML model.

Figure 2. Data Access Object Class Diagram

The DAO classes need a little explanation. They are annotated with the <<Repository>> annotation so that the modeler identifies them as DAO classes and generates the correct interfaces and classes for them. But what might appear different, at first, is that the classes do not define any attributes and do define a set of methods. The code generator that I built reads the method names and generates Hibernate queries from them. It is still somewhat simplistic at this point as the following finder criteria is defined:

  • findById(): this is special reserved method that resolves to a Hibernate get() method call to load an object by its primary key
  • findByFieldName(), returning a list: builds a query that finds all objects with the matched field value, e.g. findByColor( “red” ) would return all red cars
  • findByFieldName(), returning a single value: same as above, but returns the first item found in the result set or null if the result set is empty
  • findAll(): returns all objects of the requested type in the database

In the future I would like to add additional finders that provide better “simple” queries, such as:

  • findByField1AndField2...AndField3: fields would be separated by “And” and would result in compound conditions, such as findByColorAndMaxSpeed( “red”, 150 ), which would return all red cars with a max speed of 150 mph
  • findByFieldGreaterThan() / findByFieldLessThan(): would return objects greater than or less than the specified value, for example: findByMaxSpeedGreaterThan( 120 )
  • findByField1OrField2(): same as “And” condition above, but implementing as an “Or”
  • findByFieldBetween(): searches by a range of values, e.g. findByMaxSpeedBetween( 120, 150 )
  • Mix and match all of the above

Currently you can define the following persistence methods:

  • save(): invokes the Hibernate save() method to persist an object the database
  • saveOrUpdate(): invokes the Hibernate saveOrUpdate() method to safely persist an object to the database
  • update(): invokes the Hibernate update() method to update an object that is currently in the database
  • delete(): invokes the Hibernate delete() method to remove an object from the database

So while far from complete, it is pretty feature rich at this point.

To use the modeler plug-in from Maven, you need to install it locally (you can download it with the article source code):

mvn install:install-file -DgroupId=com.geekcap -DartifactId=modeler -Dversion=1.0-SNAPSHOT
 -Dpackaging=jar -Dfile=/path/to/file

And then add the following plug-in to your POM file:

    <build>
        <plugins>
            <!-- Generate our model classes -->
            <plugin>
                <groupId>com.geekcap</groupId>
                <artifactId>geekmodeler</artifactId>
                <version>1.0-SNAPSHOT</version>
                <executions>
                    <execution>
                        <id>generateModel</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>execute</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

In the next section I'll review more about what the plug-in is doing, but for now know that it reads an XMI file and generates code from it. Speaking of which, when you open the attached source code, you'll find the src/main/uml directory that contains a binary version of the ArgoUML project (a .zargo file) and a corresponding XMI file. If you want to make changes to the model, from ArgoUML, choose “File” -> “Export XMI...” and save the XML file in your project's src/main/uml directory.

You can generate the source code from the XMI file explicitly by telling Maven to execute the generate-sources phase in your project directory:

mvn generate-sources

Or, when you build the project, either through a clean install or clean package, it will automatically generate the source code. After executing one of these commands, you'll find a target/generated-sources/uml directory that has a package structure that mimics the package structure in your class diagrams.

Listings 1, 2, and 3 show examples that were generated from the class diagrams in figures 2 and 3.

Listing 1. Blog.java

package com.geekcap.geekblog.model;

import java.io.Serializable;
import java.util.*;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;
import javax.persistence.OneToMany;
import javax.persistence.ManyToOne;
import javax.persistence.ManyToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

/**
 * Hibernate entity representing the Blog domain object
 *
 */
@Entity
@Table( name = "BLOG" )
public class Blog implements Serializable
{
    /**
     * Serialization id
     */
    private static final long serialVersionUID = 1L;

    /**
     * The Blog's id attribute
     */
    @Id
    @GeneratedValue( strategy = GenerationType.AUTO ) 
    @Column( name = "ID" )
    private long id;

    /**
     * The Blog's title attribute
     */
    @Column( name = "TITLE" )
    private String title;

    /**
     * The Blog's publishDate attribute
     */
    @Column( name = "PUBLISH_DATE" )
    @Temporal( TemporalType.DATE )
    private java.util.Date publishDate;

    /**
     * The Blog's summary attribute
     */
    @Column( name = "SUMMARY" )
    private String summary;

    /**
     * The Blog's content attribute
     */
    @Column( name = "CONTENT" )
    private String content;

    /**
     * Defines a many-to-one relationship with the User object
     */
    @ManyToOne( fetch = FetchType.EAGER )
    @JoinColumn( name="USER_ID" )
    private User author;

    /**
     * Defines a many-to-many relationship with the Category object
     */
    @ManyToMany( mappedBy = "categories", fetch =  FetchType.EAGER )
    private List<Category> categories;

    /**
     * Defines a one-to-many relationship with the Comment object
     */
    @OneToMany( mappedBy = "blog", targetEntity = Comment.class, fetch = FetchType.EAGER )
    private List<Comment> comments;

    /**
     * Default constructor
     */
    public Blog()
    {
    }

    /**
     * Returns the Blog's id attribute
     *
     * @return              The Blog's id attribute
     */
    public long getId()
    {
        return id;
    }

    /**
     * Sets the Blog's id attribute
     *
     * @param id   The Blog's id attribute
     */
    public void setId( long id )
    {
        this.id = id;
    }
    /**
     * Returns the Blog's title attribute
     *
     * @return              The Blog's title attribute
     */
    public String getTitle()
    {
        return title;
    }

    /**
     * Sets the Blog's title attribute
     *
     * @param title   The Blog's title attribute
     */
    public void setTitle( String title )
    {
        this.title = title;
    }
    /**
     * Returns the Blog's publishDate attribute
     *
     * @return              The Blog's publishDate attribute
     */
    public java.util.Date getPublishDate()
    {
        return publishDate;
    }

    /**
     * Sets the Blog's publishDate attribute
     *
     * @param publishDate   The Blog's publishDate attribute
     */
    public void setPublishDate( java.util.Date publishDate )
    {
        this.publishDate = publishDate;
    }
    /**
     * Returns the Blog's summary attribute
     *
     * @return              The Blog's summary attribute
     */
    public String getSummary()
    {
        return summary;
    }

    /**
     * Sets the Blog's summary attribute
     *
     * @param summary   The Blog's summary attribute
     */
    public void setSummary( String summary )
    {
        this.summary = summary;
    }
    /**
     * Returns the Blog's content attribute
     *
     * @return              The Blog's content attribute
     */
    public String getContent()
    {
        return content;
    }

    /**
     * Sets the Blog's content attribute
     *
     * @param content   The Blog's content attribute
     */
    public void setContent( String content )
    {
        this.content = content;
    }

    /**
     * Returns the Blog's author attribute
     *
     * @return          The Blog's author attribute
     */
    public User getAuthor()
    {
        return author;
    }

    /**
     * Sets the Blog's author attribute
     *
     * @param author       The Blog's author attribute
     */
    public void setAuthor( User author )
    {
        this.author = author;
    }

    /**
     * Returns the Blog's categories attribute
     *
     * @return          The Blog's categories attribute
     */
    public List<Category> getCategories()
    {
        return categories;
    }

    /**
     * Sets the Blog's categories attribute
     *
     * @param categories       The Blog's categories attribute
     */
    public void setCategories( List<Category> categories )
    {
        this.categories = categories;
    }

    /**
     * Returns the Blog's list of comments
     *
     * @return          The Blog's list of comments
     */
    public List<Comment> getComments()
    {
        return comments;
    }

    /**
     * Sets the Blog's list of comments
     *
     * @param comments          The Blog's list of comments
     */
    public void setComments( List<Comment> comments )
    {
        this.comments = comments;
    }
}

Listing 2. BlogDao.java

package com.geekcap.geekblog.dao;

// Custom imports
import com.geekcap.geekblog.model.Blog;
import com.geekcap.geekblog.model.User;
import java.util.List;

/**
 * BlogDao Data Access Object Interface
 */
public interface BlogDao
{
    public Blog findById( Long id );
    public List<Blog> findByAuthor( User author );
    public void save( Blog blog );
    public void update( Blog blog );
    public void delete( Blog blog );
    public void saveOrUpdate( Blog blog );
}

Listing 3. BlogDaoImpl.java

package com.geekcap.geekblog.dao;

import org.apache.log4j.Logger;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

// Custom imports
import com.geekcap.geekblog.model.Blog;
import com.geekcap.geekblog.model.User;
import java.util.List;

/**
 * BlogDao Data Access Object Implementation
 */
@Repository( "blogDao" )
public class BlogDaoImpl extends HibernateDaoSupport implements BlogDao
{
    private static Logger logger = Logger.getLogger( BlogDaoImpl.class );

    @Override
    public Blog findById( Long id )
    {
        if( logger.isDebugEnabled() )
        {
            logger.debug( "Executing operation: findById with parameters: " + id   );
        }
        return ( Blog )getHibernateTemplate().get( Blog.class, id );
    }

    @Override
    public List<Blog> findByAuthor( User author )
    {
        if( logger.isDebugEnabled() )
        {
            logger.debug( "Executing operation: findByAuthor with parameters: " + author   );
        }
        return getHibernateTemplate().find( "from com.geekcap.geekblog.model.Blog
         blog where blog.author = ?", author );
    }

    @Override
    public void save( Blog blog )
    {
        if( logger.isDebugEnabled() )
        {
            logger.debug( "Executing operation: save with parameters: " + blog   );
        }
        getHibernateTemplate().save( blog );
    }

    @Override
    public void update( Blog blog )
    {
        if( logger.isDebugEnabled() )
        {
            logger.debug( "Executing operation: update with parameters: " + blog   );
        }
        getHibernateTemplate().update( blog );
    }

    @Override
    public void delete( Blog blog )
    {
        if( logger.isDebugEnabled() )
        {
            logger.debug( "Executing operation: delete with parameters: " + blog   );
        }
        getHibernateTemplate().delete( blog );
    }

    @Override
    public void saveOrUpdate( Blog blog )
    {
        if( logger.isDebugEnabled() )
        {
            logger.debug( "Executing operation: saveOrUpdate with parameters: " + blog   );
        }
        getHibernateTemplate().saveOrUpdate( blog );
    }
}

You can download the entire project and source code to see everything that gets generated, but these three listings should give you a flavor for what the plug-in is doing. In the next section I'll dive deeper into the plugin and show you what it is doing.