Using UML 2 & Model Driven Architecture (MDA) Transforms to Generate a Persistence Layer for Java Web Applications and Web Services

by Alistair Miles

Agile Development and Rapid Prototyping

I’m doing some rapid prototyping of Web applications and Web services in Java, and because I need to redesign often, I want the design and implementation processes to be as agile and lightweight as possible. I.e. each change I make to the design should take little or no effort to implement & deploy.

Persistent Data

The biggest problem I’ve encountered so far is persistence. I need to implement applications with persistent data. However, I’m coding (at least on the server side) in Java. Implementing persistence for Java applications typically requires an object-relational mapping, which if coded by hand, costs effort. There are alternatives for handling persistence more-or-less automatically, like J2EE’s annotations. However, I’m also very much in favour of coding with POJOs, injecting dependencies wherever possible, and decoupling application code from any implementation-specific considerations to do with persistence.

Data Access Objects (DAOs)

A popular design pattern for persistence is the use of Data Access Objects (DAOs) which abstract the data access and manipulation operations from the implementation-specific details, allowing the persistence platform to be swapped out without affecting application logic.

UML 2 & MDA Transforms

I’ve also been working recently with UML 2 and Model Driven Architecture (MDA) transforms. The tool I’ve been using is Enterprise Architect, which encourages the development of a Platform Independent Model (PIM), which can then be transformed into class models tailored to various programming languages (Java, C# etc.). A PIM can also be transformed into database models tailored to various database platforms (Oracle, Postgres, MySQL etc.). From these platform-specific models, you can then generate actual code or DDL scripts.

The Dream

So the dream is this: I design the data model for my application in a totally platform-independent way, using UML; I then use MDA transforms to generate a database model, a Java class model, and at least the interfaces for a set of Java DAOs (ideally the implementations as well); I then use code generation to automatically generate the DDL scripts and Java class/interface definitions.

I.e. I want to go from a PIM to a fully-implemented persistence layer at the press of one or two buttons.

Oh, and I don’t want to pay any money for any of this 🙂

This where I’ve got to so far…

Designing the PIM

Let’s start with a really simple application, for storing and manipulating data about people, organisations, and employment. The PIM class model is below:

Platform-Independent Model (PIM)

Transforming the PIM to a DDL Model

EA has a built-in model transformation for generating a Database Definition (DDL) model. I used this transformation to generate the following DDL model:

DDL Model

Note that I’ve set Postgres as the default database in EA, so this DDL model is tailored for Postgres. The DDL model can easily be converted in EA for other databases e.g. Oracle or MySQL.

Generating DDL Scripts

From the DDL Model I can generate a DDL script for my database of choice.

There is a minor issue here, which is that the DDL script doesn’t contain any useful triggers. I.e. using the DDL script generated from the model above, I cannot delete a person if I haven’t previously deleted any employment associations involving that person (because of the foreign key constraint). Typically I would want a database trigger which would handle these dependencies for me — i.e. if I delete a person, then all of that person’s associations are also deleted. Currently, I have to write these triggers by hand. I haven’t figured out how to get inside EA’s DDL generation code to solve this yet.

Transforming the PIM to a Java Model

Anyway, returning to the PIM, I’m also going to want some Java classes to represent data objects in my application. Using EA’s built-in Java model transform, I generated the following class model from the PIM:

Java Model

Basically, the transform creates the get/set methods for the attributes and associations in the PIM.

Java Collections and Generics

You’ll notice that the parameter and return types for some of the methods are the class java.util.Set. The class that EA should use for handling collections is configurable, and I’ve set this to the java.util.Set class; however, I’m using Java 5, and I’d like to use generics, but I couldn’t find a way to insert the right generic parameters — so this has to be done by hand.

Also, I’d like to add a field in each of these classes to store the value of the primary key in the underlying database, for use in multiple queries — that *should* be possible by tweaking EA’s built-in Java transform, although I haven’t tried it yet.

Transforming the PIM to a Java DAO Model

Finally, I want to transform the PIM to a model defining at least some DAO interfaces for my data model. For this I had to define an entirely new model transform in EA. Here’s the result of my Java DAO model transform:

Java DAO Model

As you can see, the typical select, insert, update and delete operations required for each type of entity and association have been generated.

Writing a Custom Model Transform in EA

EA uses a sort of template language for defining model transforms. When you create a new transformation type in EA, it gives you a default template for each type of artefact in the input (File, Namespace, Class, Attribute, Operation etc.) which simply copies everything over from input to output. You can then tweak these to get what you want. For my Java DAO transform, I modified the File template to the following:


Package
{
  name="Java DAO Model"
  namespaceroot="true"
%list="Namespace" @separator="\n\n" @indent="  "%
}

… I modified the Namespace template to:


%if packagePath == ""%
%list="Namespace" @separator="\n"%
%list="Class" @separator="\n"%
%list="Connector" @separator="\n"%
%endTemplate%

Package
{
  %TRANSFORM_CURRENT()%
  %list="Namespace" @separator="\n" @indent="  "%
  %list="Class" @separator="\n" @indent="  "%
  %list="Connector" @separator="\n"%
}

… I modified the Class template to:


%if elemType != "Class"%
%endTemplate%

Interface
{
  %TRANSFORM_REFERENCE()%
  name=%qt%%className%Dao%qt%
  language="Java"

  %if elemType != "Association"%

  $COMMENT="handle class"

  $COMMENT="count operation"
  Operation
  {
    name=%qt%count%className%s%qt%
    scope="public"
    type="int"
  }

  $COMMENT="select operation"
  Operation
  {
    name=%qt%select%className%%qt%
    scope="public"
    type=%qt%%className%%qt%
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(className, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  $COMMENT="select all operation"
  Operation
  {
    name=%qt%selectAll%className%s%qt%
    scope="public"
    type=%qt%java.util.Set%qt%
  }

  $COMMENT="insert operation"
  Operation
  {
    name=%qt%insert%className%%qt%
    scope="public"
    type="void"
    Parameter
    {
      type=%qt%%className%%qt%
      name=%qt%%CONVERT_NAME(className, "Pascal Case","Camel Case")%%qt%
    }
  }

  $COMMENT="update operation"
  Operation
  {
  	name=%qt%update%className%%qt%
  	scope="public"
  	type="void"
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(className, "Pascal Case","Camel Case")%ID%qt%
    }
    Parameter
    {
      type=%qt%%className%%qt%
      name=%qt%%CONVERT_NAME(className, "Pascal Case","Camel Case")%%qt%
    }
  }

  $COMMENT="delete operation"
  Operation
  {
  	name=%qt%delete%className%%qt%
  	scope="public"
  	type="void"
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(className, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  %endIf%

}

… and finally, I modified the Connector template to:


Interface
{
  %TRANSFORM_REFERENCE()%
  name=%qt%%connectorName%Dao%qt%
  language="Java"

  $COMMENT="insert operation"
  Operation
  {
    name=%qt%insert%connectorName%%qt%
    scope="public"
    type="void"
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorSourceRole, "Pascal Case","Camel Case")%ID%qt%
    }
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorDestRole, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  $COMMENT="delete operation"
  Operation
  {
    name=%qt%delete%connectorName%%qt%
    scope="public"
    type="void"
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorSourceRole, "Pascal Case","Camel Case")%ID%qt%
    }
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorDestRole, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  $COMMENT="select source role"
  Operation
  {
    name=%qt%select%connectorSourceElemName%s%qt%
    scope="public"
    type=%qt%java.util.Set%qt%
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorDestRole, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  $COMMENT="count source role"
  Operation
  {
    name=%qt%count%connectorSourceElemName%s%qt%
    scope="public"
    type="int"
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorDestRole, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  $COMMENT="select dest role"
  Operation
  {
    name=%qt%select%connectorDestElemName%s%qt%
    scope="public"
    type=%qt%java.util.Set%qt%
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorSourceRole, "Pascal Case","Camel Case")%ID%qt%
    }
  }

  $COMMENT="count dest role"
  Operation
  {
    name=%qt%count%connectorDestElemName%s%qt%
    scope="public"
    type="int"
    Parameter
    {
      type="int"
      name=%qt%%CONVERT_NAME(connectorSourceRole, "Pascal Case","Camel Case")%ID%qt%
    }
  }
}

Everything else I left the same.

Living the Dream?

So as you’ve probably realised, I’m not quite living the dream, but I’m getting there 🙂

There are still places where I have to do some work to get from PIM to implementation. These include defining triggers to handle deletion dependencies in the database, and handling generics in the Java classes.

Of course, I still have to write implementations of my DAO interfaces by hand. However, it *should* be possible to either create another UML model transform which will do that automatically for a specific persitence platform, using the initial code sections for each DAO class, or create some code generation templates (or both). I’ve got a plan to do this for Spring’s JDBC classes, but I haven’t got round to it yet …

Advertisements