Introduction

MinuteProject 4 JPA2 generates:
  • JPA2 entities with annotations, embeddedId classes for composite key. The entities can be generated in java or in groovy.
  • JPA2 metamodel associated to entities to enable the user to build compilable queries.
  • Enumeration
  • Other maven pom.xml, querydsl integration...

The purpose of this track is to propose simple, fast, no-intrusive (no additional framework) JPA2 entities insertable in your architecture.

The generation propose standard output based on some naming convention that people may like to customize. JPA2 Smart Reverse Engineering explains how to customize the resulting output.

Related tracks

This track is used by other Minuteproject tracks that scales upon it

  • JPA2 entities (annotated) with OpenXava annotations on the OpenXava track (with other OX framework specific files).
  • JPA2 entities (annotated) with Playframework annotations on the Playframework track (with other Playframework specific files).
  • BSLA (Basic Spring Layer Architecture) & BELA (Basic Enterprise Layer Architecture) are DAO built on top of JPA2.
  • Primefaces, Vaadin and REST tracks built on top of BSLA or BELA

Step

One step is needed:
  1. The first 'Generation step' consists of configuring MP kernel either via a configuration file or via the console.

Prequisites

Have Java 6 setup.
Download MinuteProjectlast version.
Check that your database connection is available

Step: Generation

Via the console
  1. Start the MP console (<MP_HOME>/bin/start-console.(cmd/sh). How to use the console.
  2. Fill the connection information to the datamodel
  3. Fill the model general information (model, package names, version primary key policy, scope).
  4. Choose JPA2 target
  5. Click generate

Via the configuration file
An example can be found <MP_HOME>/demo/config/mp-config-JPA2.xml.
To run it: execute demo-JPA2.(cmd/sh)
The configuration allows you to pass additional configuration like
  • querydsl integration
  • JPA2 vendor implementation by default hibernate. On minuteproject 0.5.5 only hibernate is available.
<!DOCTYPE root>
<generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="mp-config.xsd">
    <configuration>
    <model name="petshop" version="1.0" package-root="net.sf.mp.demo.jpa2">
         <data-model>
        <driver name="hsqldb" version="1.8.0.7" groupId="hsqldb" artifactId="hsqldb"></driver>
        <dataSource>
            <driverClassName>org.hsqldb.jdbcDriver</driverClassName>
<!--                     <url>jdbc:hsqldb:hsql://127.0.0.1:9001/petshop</url> -->
            <url>jdbc:hsqldb:file:../../sample/hsql/petshop</url>
            <username>sa</username>
            <password></password>
        </dataSource>
                <!-- for Oracle and DB2 please set the schema
        <schema></schema>
                 -->
          <primaryKeyPolicy oneGlobal="true" oneForEachTable="false">
            <primaryKeyPolicyPattern prefix="" suffix="" name="sequencePattern" sequenceName="hibernate_sequence"></primaryKeyPolicyPattern>
          </primaryKeyPolicy>
        </data-model>
        <business-model>
               <generation-condition>
            <condition type="exclude" startsWith="DUAL"></condition>
            <condition type="exclude" startsWith="ID_GEN"></condition>
            </generation-condition>
            <business-package default="pet">
            <condition type="package" startsWith="PRODUCT" result="product"></condition>
            <condition type="package" startsWith="ITEM" result="product"></condition>
            </business-package>
            <enrichment>
            <conventions>
                <entity-naming-convention type="apply-strip-table-name-prefix" pattern-to-strip="SYS,FIN"/>
                <column-naming-convention type="apply-fix-primary-key-column-name-when-no-ambiguity" default-value="ID"/>
                <reference-naming-convention type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true"></reference-naming-convention>
            </conventions>
            <package name="product">
                 <entity-group entities="PRODUCT"></entity-group>
                 <entity-group entities="ITEM"></entity-group>
            </package>
            <entity name="PRODUCT" alias="MY_GOOD_PRODUCT">
            </entity>
            <entity name="ITEM" alias="MY_GOOD_ITEM" comment="my item table">
                <field name="PRODUCTID" alias="THIS_IS_MY_PRODUCT" comment="my product field reference"></field>
            </entity>
            <entity name="CATEGORY" content-type="reference-data" >
               <semantic-reference>
                <sql-path path="NAME"/>
                </semantic-reference>
            </entity>
           </enrichment>
         </business-model>
                 <statement-model>
                   <queries>
                     <query name="get address street">
                         <query-body><value><![CDATA[select * from address where addressid > ?]]></value></query-body>
                         <query-what><value>select street1, street2 from address</value></query-what>
                         <query-where><value><![CDATA[addressid > ?]]></value></query-where>
                         <query-params>
                             <query-param name="identifier_Address" is-mandatory="false" type="INT" sample="1"></query-param>
                         </query-params>
                     </query>
                 </queries>
               </statement-model>
       </model>
    <targets catalog-entry="JPA2" />
    </configuration>
</generator-config>
 
 

JPA2 with groovy


In order to generate groovy artifact the targets catalog-entry node must be set to JPA2-groovy
<targets catalog-entry="JPA2-groovy" />
The pom.xml file is adapted to handle groovy syntax/compilation.

Generated artifacts


MP generates the following artifacts
  • JPA2 entities with annotations in java or groovy
  • JPA2 metamodel associated to entities to enable the user to build compilable queries
  • Enumeration
  • Maven pom.xml artifact with:
    • JPA2 vendor implementation by default Hibernate
    • Querydsl criteria API integration (dependency and build)


Sample

Input

Petshop DB is reverse-engineered into a JPA2 entity layer.
Here is the Petshop entity schema

petshop.jpg

Generation

You can obtain the resulting code by:
  1. Downloading it:
    1. The latest demo-src-generated-<version>.zip
  2. Generating it:
    1. Start the shipped Petshop Hsqldb
      1. in /sample execute start-petshop-database.(bat/sh)
    2. Generate via
      1. Console
        1. in /bin execute start-console.(cmd/sh)
        2. Choose HSQLDB as DB
        3. Fill URL with jdbc:hsqldb:hsql://127.0.0.1:9001/petshop
        4. Fill username with 'sa'
        5. Fill package with 'net.sf.mp.demo.jpa2'
        6. Fill model name with 'petshop'
        7. Select target technology 'JPA2'
        8. Optional Step: go on Customization tab and associate entities to business packages
          1. Example ITEM and PRODUCT go into package product
        9. Click on 'Generate'
        10. => the generated code will go in <MP_HOME>/output/petshop/JPA2
      2. Via scripts
        1. in /demo/config execute demo-JPA2.(cmd/sh)
        2. => the generated code will go in <MP_HOME>/demo/output/JPA2

Results

Package

It generates the following package structure.
Remark: since 0.5.5 it generate in a maven like project structure.
JPA2-petshop-generation.jpg
  • There is no entity generated for ManyToMany relationship tables such as TAG_ITEM.
  • Package your model as you want and not into a single directory: make things readable (Ex: entity starting with FIN goes to 'finance package'....)

Entity detail

Here is an example of the resulting java code for the entity 'Item'
  • It has @ManyToOne and @ManyToMany relationships
  • It imports other entities although not in the same business package (due to MP model enrichment)
  • It provides getters, setter
  • It provides a primary key policy (sequence(unique/one per entity)/autoincrement)
  • unambiguous variable name for @ManyToOne and @ManyToMany variable
    • Although it may result in long name, it prevent having duplicates in case of multiple 'similar' relationship
    • Enrichment of a relationship can be used to provide a specific variable name
package net.sf.mp.demo.jpa2.petshop.product;
 
import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
 
import javax.persistence.*;
 
import net.sf.mp.demo.jpa2.petshop.pet.Address;
import net.sf.mp.demo.jpa2.petshop.product.Product;
import net.sf.mp.demo.jpa2.petshop.pet.Sellercontactinfo;
import net.sf.mp.demo.jpa2.petshop.pet.Tag;
 
/**
 *
 * <p>Title: Item</p>
 *
 * <p>Description: Domain Object describing a Item entity</p>
 *
 */
@Entity (name="Item")
@Table (name="ITEM")
public class Item {
 
    @SequenceGenerator(name = "ITEMSEQ", sequenceName ="hibernate_sequence", allocationSize=1 )
    @Id @Column(name="ITEMID" )
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="ITEMSEQ")
    private Integer itemid;
 
 
    @Column(name="NAME",  length=30, nullable=false,  unique=false)
    private String name;
    @Column(name="DESCRIPTION",  length=500, nullable=false,  unique=false)
    private String description;
    @Column(name="IMAGEURL",  length=55,  nullable=true,  unique=false)
    private String imageurl;
    @Column(name="IMAGETHUMBURL",  length=55,  nullable=true,  unique=false)
    private String imagethumburl;
    @Column(name="PRICE",   nullable=false,  unique=false)
    private java.math.BigDecimal price;
    @Column(name="TOTALSCORE",   nullable=false,  unique=false)
    private Integer totalscore;
    @Column(name="NUMBEROFVOTES",   nullable=false,  unique=false)
    private Integer numberofvotes;
    @Column(name="DISABLED",   nullable=false,  unique=false)
    private Integer disabled;
 
    @ManyToOne (fetch=FetchType.LAZY , optional=false)
    @JoinColumn(name="ADDRESS_ADDRESSID", nullable=false,  unique=false )
    private Address addressAddressid;
 
    @ManyToOne (fetch=FetchType.LAZY , optional=false)
    @JoinColumn(name="PRODUCTID", nullable=false,  unique=false )
    private Product productid;
 
    @ManyToOne (fetch=FetchType.LAZY , optional=false)
    @JoinColumn(name="CONTACTINFO_CONTACTINFOID", nullable=false,  unique=false )
    private Sellercontactinfo contactinfoContactinfoid;
 
 
 
    @ManyToMany
    @JoinTable(name="TAG_ITEM",
        joinColumns=@JoinColumn(name="ITEMID"),
        inverseJoinColumns=@JoinColumn(name="TAGID")
    )
    private Set <Tag> tagViaTagItemByTagids = new HashSet();
 
 
    /**
    * Default constructor
    */
    public Item() {
    }
 
 
    public Integer getItemid() {
        return itemid;
    }
 
    public void setItemid (Integer itemid) {
        this.itemid =  itemid;
    }
 
 
    public String getName() {
        return name;
    }
 
    public void setName (String name) {
        this.name =  name;
    }
    public String getDescription() {
        return description;
    }
 
    public void setDescription (String description) {
        this.description =  description;
    }
    public String getImageurl() {
        return imageurl;
    }
 
    public void setImageurl (String imageurl) {
        this.imageurl =  imageurl;
    }
    public String getImagethumburl() {
        return imagethumburl;
    }
 
    public void setImagethumburl (String imagethumburl) {
        this.imagethumburl =  imagethumburl;
    }
    public java.math.BigDecimal getPrice() {
        return price;
    }
 
    public void setPrice (java.math.BigDecimal price) {
        this.price =  price;
    }
    public Integer getTotalscore() {
        return totalscore;
    }
 
    public void setTotalscore (Integer totalscore) {
        this.totalscore =  totalscore;
    }
    public Integer getNumberofvotes() {
        return numberofvotes;
    }
 
    public void setNumberofvotes (Integer numberofvotes) {
        this.numberofvotes =  numberofvotes;
    }
    public Integer getDisabled() {
        return disabled;
    }
 
    public void setDisabled (Integer disabled) {
        this.disabled =  disabled;
    }
 
    public Address getAddressAddressid () {
        return addressAddressid;
    }
 
    public void setAddressAddressid (Address addressAddressid) {
        this.addressAddressid = addressAddressid;//this.addressAddressid = address;
    }
    public Product getProductid () {
        return productid;
    }
 
    public void setProductid (Product productid) {
        this.productid = productid;//this.productid = product;
    }
    public Sellercontactinfo getContactinfoContactinfoid () {
        return contactinfoContactinfoid;
    }
 
    public void setContactinfoContactinfoid (Sellercontactinfo contactinfoContactinfoid) {
        this.contactinfoContactinfoid = contactinfoContactinfoid;//this.contactinfoContactinfoid = sellercontactinfo;
    }
 
    public Set<Tag> getTagViaTagItemByTagids() {
        if (tagViaTagItemByTagids == null){
            tagViaTagItemByTagids = new HashSet();
        }
        return tagViaTagItemByTagids;
    }
 
    public void setTagViaTagItemByTagids (Set<Tag> tagViaTagItemByTagids) {
        this.tagViaTagItemByTagids = tagViaTagItemByTagids;
    }
 
}
 
 

Here is the Item_ associated metamodel
package net.sf.mp.demo.jpa2.petshop.product;
 
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
import javax.persistence.metamodel.SetAttribute;
 
import net.sf.mp.demo.jpa2.petshop.pet.Address;
import net.sf.mp.demo.jpa2.petshop.product.Product;
import net.sf.mp.demo.jpa2.petshop.pet.Sellercontactinfo;
import net.sf.mp.demo.jpa2.petshop.pet.Tag;
 
@StaticMetamodel(Item.class)
public class Item_ {
 
    public static volatile SingularAttribute<Item, Integer> itemid;
 
    public static volatile SingularAttribute<Item, String> name;
    public static volatile SingularAttribute<Item, String> description;
    public static volatile SingularAttribute<Item, String> imageurl;
    public static volatile SingularAttribute<Item, String> imagethumburl;
    public static volatile SingularAttribute<Item, java.math.BigDecimal> price;
    public static volatile SingularAttribute<Item, Integer> totalscore;
    public static volatile SingularAttribute<Item, Integer> numberofvotes;
    public static volatile SingularAttribute<Item, Integer> disabled;
 
    public static volatile SingularAttribute<Item, Address> addressAddressid;
    public static volatile SingularAttribute<Item, Product> productid;
    public static volatile SingularAttribute<Item, Sellercontactinfo> contactinfoContactinfoid;
 
 
    public static volatile SetAttribute<Item, Tag> tagViaTagItemByTagids;
 
}
 
<!DOCTYPE root>
<generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="mp-config.xsd">
<configuration>
<model name="petshop" version="1.0" package-root="net.sf.mp.demo.jpa2">
<data-model>
<driver name="hsqldb" version="1.8.0.7" groupId="hsqldb" artifactId="hsqldb"></driver>
<fileSource name="petshop" dir="../../MinuteProject/data">
</fileSource>
<dataSource>
<driverClassName>org.hsqldb.jdbcDriver</driverClassName>
<!-- <url>jdbc:hsqldb:hsql://127.0.0.1:9001/petshop</url> -->
<url>jdbc:hsqldb:file:../../sample/hsql/petshop</url>
<username>sa</username>
<password></password>
</dataSource>
<!-- for Oracle and DB2 please set the schema
<schema> </schema>
-->
<primaryKeyPolicy oneGlobal="true" oneForEachTable="false">
<primaryKeyPolicyPattern prefix="" suffix="" name="sequencePattern" sequenceName="hibernate_sequence"></primaryKeyPolicyPattern>
</primaryKeyPolicy>
</data-model>
<business-model>
<generation-condition>
<condition type="exclude" startsWith="DUAL"></condition>
<condition type="exclude" startsWith="ID_GEN"></condition>
</generation-condition>
<business-package default="pet">
<condition type="package" startsWith="PRODUCT" result="product"></condition>
<condition type="package" startsWith="ITEM" result="product"></condition>
</business-package>
<enrichment>
<conventions>
<entity-naming-convention type="apply-strip-table-name-prefix" pattern-to-strip="SYS,FIN"/>
<column-naming-convention type="apply-fix-primary-key-column-name-when-no-ambiguity" default-value="ID"/>
<reference-naming-convention type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true"></reference-naming-convention>
</conventions>
<package name="product">
<entity-group entities="PRODUCT"></entity-group>
<entity-group entities="ITEM"></entity-group>
</package>
<entity name="PRODUCT" alias="MY_GOOD_PRODUCT">
</entity>
<entity name="ITEM" alias="MY_GOOD_ITEM" comment="my item table">
<field name="PRODUCTID" alias="THIS_IS_MY_PRODUCT" comment="my product field reference"></field>
</entity>
<entity name="CATEGORY" content-type="reference-data" >
<semantic-reference>
<sql-path path="NAME"/>
</semantic-reference>
</entity>
</enrichment>
</business-model>
<statement-model>
<queries>
<query name="get address street">
<query-body><value><![CDATA[select * from address where addressid > ?]]></value></query-body>
<query-what><value>select street1, street2 from address</value></query-what>
<query-where><value><![CDATA[addressid > ?]]></value></query-where>
<query-params>
<query-param name="identifier_Address" is-mandatory="false" type="INT" sample="1"></query-param>
</query-params>
</query>
</queries>
</statement-model>
</model>
<targets catalog-entry="JPA2">
<!-- old config still working

<target refname="JPA2"
name="JPA2"
fileName="catalog/mp-template-config-JPA2.xml"
outputdir-root="../output/JPA2"
templatedir-root="../../template/framework/jpa">
<property name="add-querydsl" value="2.1.2"></property>
<property name="add-jpa2-implementation" value="hibernate"></property>
</target>
<target refname="CACHE-LIB"
fileName="catalog/mp-template-config-CACHE-LIB.xml"
templatedir-root="../../template/framework/cache">
</target>
<target refname="SDD-beans"
outputdir-root="../output/JPA2"
fileName="catalog/mp-template-config-SDD-beans.xml"
templatedir-root="../../template/framework/bean">
</target>
<target refname="LIB"
fileName="catalog/mp-template-config-bsla-LIB-features.xml"
templatedir-root="../../template/framework/bsla">
</target>
<target refname="COMMON-LIB"
fileName="catalog/mp-template-config-COMMON-LIB.xml"
templatedir-root="../../template/framework/common">
</target>
-->
</targets>
</configuration>
</generator-config>