JPA Code Generation
|
How to create source code of Entity classes for
JPA (Java Persistence API) 2.x from UML model |
JPA & UML
JPA is a Java EE standard O/R(Object-relational) mapping framework for mapping the POJO(Plain old Java object) and relational database and has been considered as a technology that works well with Domain-driven design.
Traditionally the data-oriented approach involves with ER models, so when you use JPA, you would probably use tools like Dali Java Persistence Tools of Eclipse WTP's or Hibernate Tools of JBoss Tools to generate Entity classes by reversing the physical DB scheme. However I find that those reversed classes are less flexible as they are tied with restrictions of its related models such as the expressions in many-to-many relationships..etc.
On the other hand, UML is often used in the Domain-driven design practice for designing domain models. Using JPA entity models whose expressions are closer to UML, makes it easier to maintain the implementations of domains close as the original Entity models.
In this way, you can also add annotations to Entity classes and customize DDL which JPA generates for mapping physical DB scheme in detailed level.
In this tutorial, I'd like to introduce my approach to generate initial JPA Entity Classes from UML model to develop them.
JPA is a Java EE standard O/R(Object-relational) mapping framework for mapping the POJO(Plain old Java object) and relational database and has been considered as a technology that works well with Domain-driven design.
Traditionally the data-oriented approach involves with ER models, so when you use JPA, you would probably use tools like Dali Java Persistence Tools of Eclipse WTP's or Hibernate Tools of JBoss Tools to generate Entity classes by reversing the physical DB scheme. However I find that those reversed classes are less flexible as they are tied with restrictions of its related models such as the expressions in many-to-many relationships..etc.
On the other hand, UML is often used in the Domain-driven design practice for designing domain models. Using JPA entity models whose expressions are closer to UML, makes it easier to maintain the implementations of domains close as the original Entity models.
In this way, you can also add annotations to Entity classes and customize DDL which JPA generates for mapping physical DB scheme in detailed level.
In this tutorial, I'd like to introduce my approach to generate initial JPA Entity Classes from UML model to develop them.
Generate Code from UML Model
In this tutorial, I will use Astah Professional for creating UML diagram and any<code/> for code generation.
Astah Professional is a software design tool which supports UML 2.x diagrams and ER Diagram by Change Vision and any<code/> is a plug-in developed by Jose Carreno to generate various programming languages like Java, JPA, PHP, C#, Objective-C..etc from UML Class diagrams using templates.
Astah Professional is a software design tool which supports UML 2.x diagrams and ER Diagram by Change Vision and any<code/> is a plug-in developed by Jose Carreno to generate various programming languages like Java, JPA, PHP, C#, Objective-C..etc from UML Class diagrams using templates.
Sample UML Model
Let's use this sample purchase order system model as an example.
- Customer has a name, and the name is used to identify the Entities
- PurchaseOrder is related to the Customer with "orderedBy" and "deliverTo"
- PurchaseOrder aggregates multiple Items
Get Templates ready for Code Generation
To generate source code using any<code/>, you need to create template files first and this is the most important step in this tutorial.
Create these two template files and save them in a new folder.
Create these two template files and save them in a new folder.
JPA-ENTITY-name.mda
${targetDir}/${c.getFullyQualifiedName("/")}.java
JPA-ENTITY-content.mda
<% if(c.hasStereotype("entity")) { %> package ${c.owner.getFullyQualifiedName(".")}; @javax.persistence.Entity ${jpa.classifierSignature(c)} { ${jpa.primaryKey(c)} <% def atts = c.attributes.findAll({it.name && !jpa.isIdentifier(it)}) %> <% atts.each() { %> ${jpa.attribute(it)} <% } %> <% atts.each() { %> ${jpa.getter(it)} ${jpa.setter(it)} <% } %> } <% } %>
Specify conditions to generate code for
In the "JPA-ENTITY-content.mda" file, we specify to generate code of classes only if they have "entity" stereotypes.
By specifying this condition, you can model JPA classes and other classes all together in one same project without worrying about generating unnecessary codes..etc.
By specifying this condition, you can model JPA classes and other classes all together in one same project without worrying about generating unnecessary codes..etc.
Generate primary keys
The jpa.primaryKey() method detects primary keys from each class and generates a field, getter and setter. It detects primary keys following the guideline below:
- For a class which doesn't have any attributes with "id" stereotype, a primary key(Long) will be generated by default
- For a class which has a single "id" stereotyped attribute, an unique primary key will be generated based on it
- For a class which has multiple attributes with "id" stereotypes, it will look into its PK class. (We need to make a template for this but we are skipping this in this tutorial.)
Attributes and relationships
By using the Groovy findAll() which takes closure, it can loop the persistent classes and relationships to the JPA Entities. Navigable or unspecified-navigability associations will be included in attributes, so that annotations will be added accordingly and automatically by default.
Navigability is very important to indicate to JPA that the association is either one way or both ways, also it is used to identify the ownership of the relationship, in the one-to-one, one-to-many or many-to-many relationships. A is the owner of all the cases below:
In this sample model, "PurchaseOrder" owns "Item", so the appropriate "mappedBy" option will be included in the generated source code.
Navigability is very important to indicate to JPA that the association is either one way or both ways, also it is used to identify the ownership of the relationship, in the one-to-one, one-to-many or many-to-many relationships. A is the owner of all the cases below:
- A x-> B
- A -> B
- A x- B
In this sample model, "PurchaseOrder" owns "Item", so the appropriate "mappedBy" option will be included in the generated source code.
Generated Source Code
Since I used simple default methods for exporting fields, getters and setters such as the jpa.attribute()...etc, the exported code is very simple (You can see the source code at the end of this tutorial). However there are abundant ways you can do with this any<code/>, for example when a property is added to an association, you can make the the other side of association to update with this change automatically by creating a template to do so.
Summary
Hope this tutorial helps you understand how you can generate initial JPA Entity classes code from UML models to develop with.
So how about you try this out if you prefer to have higher expressions in the Entity models. (Download is available under [Try].)
To see what more you can do with this any<code/> such as JPA directives, how to create Repository Interface for Spring Data.. etc, please refer to the any<code/> website and tutorials.
At last, we'd like to send a big thank-you to Mr. Jose Carreno for creating such significant plug-in.
- Entity classes created by reversing from database are NOT flexible in its expression as they are tied with related models restrictions
- Using Entity classes generated by UML models makes it easier to maintain the implementations close to the original entity models
So how about you try this out if you prefer to have higher expressions in the Entity models. (Download is available under [Try].)
To see what more you can do with this any<code/> such as JPA directives, how to create Repository Interface for Spring Data.. etc, please refer to the any<code/> website and tutorials.
At last, we'd like to send a big thank-you to Mr. Jose Carreno for creating such significant plug-in.
Try
Download Astah for free and try go through this tutorial by yourself.
Appendix : Exported Source code through this tutorial
PurchaseOrder.java
package com.example.po; @javax.persistence.Entity public class PurchaseOrder { @javax.persistence.Id private java.lang.Long id; public final java.lang.Long getId() { return this.id; } public final void setId(final java.lang.Long someId) { this.id = someId; } @javax.persistence.OneToMany(mappedBy = "purchaseOrder") private java.util.List- items; @javax.persistence.ManyToOne private Customer orderedBy; @javax.persistence.ManyToOne private Customer deliverTo; public final java.util.List
- getItems() { return this.items; } public final void setItems(final java.util.List
- someItems) { this.items = someItems; } public final Customer getOrderedBy() { return this.orderedBy; } public final void setOrderedBy(final Customer someOrderedBy) { this.orderedBy = someOrderedBy; } public final Customer getDeliverTo() { return this.deliverTo; } public final void setDeliverTo(final Customer someDeliverTo) { this.deliverTo = someDeliverTo; } }
Item.java
package com.example.po; @javax.persistence.Entity public class Item { @javax.persistence.Id private java.lang.Long id; public final java.lang.Long getId() { return this.id; } public final void setId(final java.lang.Long someId) { this.id = someId; } private String product; private int quantity; private double price; @javax.persistence.ManyToOne private PurchaseOrder purchaseOrder; public final String getProduct() { return this.product; } public final void setProduct(final String someProduct) { this.product = someProduct; } public final int getQuantity() { return this.quantity; } public final void setQuantity(final int someQuantity) { this.quantity = someQuantity; } public final double getPrice() { return this.price; } public final void setPrice(final double somePrice) { this.price = somePrice; } public final PurchaseOrder getPurchaseOrder() { return this.purchaseOrder; } public final void setPurchaseOrder(final PurchaseOrder somePurchaseOrder) { this.purchaseOrder = somePurchaseOrder; } }
Customer.java
package com.example.po; @javax.persistence.Entity public class Customer { @javax.persistence.Id private String name; public final String getName() { return this.name; } public final void setName(final String someName) { this.name = someName; } private String address; private String phoneNumber; private String email; public final String getAddress() { return this.address; } public final void setAddress(final String someAddress) { this.address = someAddress; } public final String getPhoneNumber() { return this.phoneNumber; } public final void setPhoneNumber(final String somePhoneNumber) { this.phoneNumber = somePhoneNumber; } public final String getEmail() { return this.email; } public final void setEmail(final String someEmail) { this.email = someEmail; } }