Showing posts with label rad. Show all posts
Showing posts with label rad. Show all posts

Monday, January 6, 2014

how-to cloudbees minuteproject 4 openxava

Minuteproject releases a demo Openxava application to be hosted on cloudbees at http://petshopox.minuteproject.cloudbees.net/

Following those steps, you can recreate an Openxava application with minuteproject and host it on cloudbees.
All those steps are IDE-free (i.e. productivity oriented) all is done by command line!
This is a requirement for automation, since it is very easy to integrate with a CI tool such as Jenkins.
Meanwhile the code is compatible with Eclipse (for java), Mysql Workbench (for DB design + release) and Cloudbees website for manual release.

Principles

Generate an openxava application with minuteproject base on an DB structure.
The database use here is petshop; for the generation the database can be local or already hosted on cloudbees PaaS. At running the database is the one of the cloudbees PaaS.

The generated Openxava compliant artifacts are built into an Openxava application.
The application is to be deployed on a tomcat server.
The application relies on a JNDI Datasource on the tomcat server.

After setting the database on cloudbees and the connection pool cloudbees tomcat server, the application can be deployed.

Ingredients

Infrastructure

JDK 1.6+
Minuteproject last version
Openxava 4.9
Mysql DB
Tomcat
Cloudbees

  • sdk
  • account

Set up Cloudbees

Database

Create a mysql db schema on cloudbees
bees db:create petshopDB
   
Retrieve the DB info (connection info: server, port, user...) all you need to create you connection pool

bees app:info -db petshopDB 


Application

Create application

bees app:create -petshopox
     

Datasource

This is use get the alias use in the persistence.xml datasource (java:comp/env/jdbc/petshopDS)

bees app:bind -db petshopDB -a petshopox -as petshopDS 


Database setup

Run you database script from the information receive via $bees db:info -db petshopDB

Sample
mysql --host=ec2-50-19-213-178.compute-1.amazonaws.com --user=xxxx --password=xxxx --port=3306 xxx < petshop.sql
petshop-mysql.sql can be found in MP_HOME/sample/schema/

Generation

Full details at http://minuteproject.wikispaces.com/OpenXava
The generation is done by Minuteproject.
Minuteproject generation by command line comes by passing a configuration file name to model-generation.

Sample
model-generation petshop.xml

Sample petshop.xml configuration
<!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">
	<!-- adapted for cloudbees petshop model -->
	<configuration>
		<conventions>
			<target-convention type="enable-updatable-code-feature" />
		</conventions>
		<model name="petshop" version="1.0" package-root="net.sf.mp.demo">
			<data-model>
				<driver name="mysql" version="5.1.16" groupId="mysql"
					artifactId="mysql-connector-java"></driver>
				<dataSource>
					<driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
					<url>jdbc:mysql://localhost:3306/petshop</url>
					<username>root</username>
					<password>mysql</password>
				</dataSource>
				<primaryKeyPolicy oneGlobal="false">
					<primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
				</primaryKeyPolicy>
			</data-model>
			<business-model>
				<generation-condition>
					<condition type="exclude" startsWith="DUAL"></condition>
					<condition type="exclude" startsWith="ID_GEN"></condition>
					<condition type="exclude" startsWith="SEQUENCE"></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" />
						<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">
						<field name="DESCRIPTION" ordering="asc" label="my description"
							is-searchable="true"></field>
						<field name="NAME" ordering="asc"></field>
					</entity>
					<entity name="TAG" content-type="reference-data">
						<field name="TAG" ordering="asc">
							<semantic-reference>
								<sql-path path="NAME" />
							</semantic-reference>
						</field>
					</entity>
				</enrichment>
			</business-model>
			<statement-model>
				<enrichment>
					<conventions>
						<sdd-dummy-primarykey-convention add="true" />
					</conventions>
				</enrichment>
				<queries>
					<query name="get address abstract" id="dashAddress" type="dashboard"
						category="pie-chart">
						<query-body> <!-- dimensions column first -->
							<value>
<![CDATA[select city, count(*) as nb from address group by city order by count(*) desc limit ?]]>
                            </value>
						</query-body>
						<query-params>
							<query-param name="top city" is-mandatory="false"
								type="INT" sample="37" default="10"></query-param>
						</query-params>
					</query>
					<query name="get address summary" id="dashCity" type="dashboard"
						category="bar-chart">
						<query-body> <!-- dimensions column first -->
							<value>
<![CDATA[select city, count(*) as nb, count(*) as nb2 from address group by city order by count(*) desc]]>
                            </value>
						</query-body>
					</query>
					<query name="get addresses by criteria" id="c">
						<query-body>
							<value>
<![CDATA[select * from address where lcase(city) like ?]]>
                            </value>
						</query-body>
						<query-params>
							<query-param name="city" type="STRING" sample="'S'"
								convert="lowercase,append%" default="%">
							</query-param>
						</query-params>
					</query>
				</queries>
			</statement-model>
		</model>
		<targets catalog-entry="OpenXava" >
			<property name="environment" value="remote"></property>
			<property name="cloud-platform" value="cloudbees"></property>
			<property name="embed-driver" value="true"></property>
		</targets>
	</configuration>
</generator-config>

Appart from the 'classical' enrichment facilities, the interesting point are the properties under the targets node:
name="environment" value="remote" 
Implies to use a connection pool (for persistence.xml)
name="cloud-platform" value="cloudbees" 
Performs additional build facilities for Openxava on cloudbees
The OX war has to hold jta.jar, ejb.jar, mysql driver jar.
  • jta.jar and ejb.jar are embedded in tomcat/lib on the OX delivery.
name="embed-driver" value="true" 

  • mysql driver jar is shipped on MP delivery

  • Remark: the last 2 options are available on Minuteproject 0.8.6

    Build

    On the generated directory
    set/export OX_HOME
    set/export MP_HOME
    Run

    build-petshop.cmd/sh

    The application petshop.war goes into OX_HOME/workspace/petshop.dist

    The application is ready to be deployed on Cloudbees.

    Deploy

    bees app:deploy -a petshopox -t tomcat7 -Rjava_version=1.7 petshop.war

    petshopox is the name of the application on cloudbees

    Enjoy

    Friday, August 23, 2013

    SDD as a productivity weapon 4 openxava

    Under construction (need 0.8.5 for generation) but can be used in the meantime as a OX tutorial to work with any jdbc sql statement (here stored-procedure)

    Productivity challenge

    With SDD (Statement driven development) what is important is I/O and functionality.
    The model is secondary.
    To experience it, let's have a store procedure that perform a required operation and get a web application out of it with Minuteproject 4 Openxava.

    Mission statement

    UC general statement

    You have information to pass to a DB via a web application to perform a function.
    You would like the input to be validated base on type, presence, stereotype, membership.

    UC specific

    You need a functionality to ask for role in the application.
    As input you pass 3 params:
    • username
    • email
    • requested role
     UserName is:
    • mandatory
    • string
    Email is:
    • mandatory
    • stereotype (format)
     Request role is:
    • mandatory
    • should be one of the application specific role

    The function shall also:
    • register the time of creation
    • status=TO_TREAT
    The function is provided in format of a stored procedure.

    Dev environment constraints

    A schema containing
    • a table SEC_ROLE containing roles
    • a table BUS_USER_ROLE_REQUEST
    • a store procedure ASK_FOR_ROLE
    • CREATE PROCEDURE ask_for_role(
        IN username VARCHAR(255),
        IN email VARCHAR(255),
         IN role VARCHAR(255)
      )
      BEGIN
      Insert into BUS_USER_ROLE_REQUEST (USERNAME, EMAIL, ROLE_REQUESTED, STATUS, REQUEST_DATE)
      values (username, email, role, 'TO_TREAT', NOW());
      
      END
      
    • SEC_ROLE and  BUS_USER_ROLE_REQUEST are not linked by any relationships (no FK nor m2m).

    Configuration

    This configuration focus on the SDD part
    <!DOCTYPE root>
    <generator-config>
     <configuration>
            <conventions>
                <target-convention type="enable-updatable-code-feature" />
            </conventions>
    
    <!-- other configuration, data-model where sec_role and bus_user_role_request are present... -->
    
      <model name="porphyry" version="1.0" package-root="net.sf.mp.demo">
       <statement-model>
        <queries>
                <query name="ask_for_role" id="ask_for_role" >
                             <query-body>
                             <value>
    <![CDATA[call ask_for_role (?,?,?)]]>
                                </value>
                             </query-body>
                             <query-params>
                                 <query-param name="username" is-mandatory="true" type="string" sample="'a'" is-id="true"></query-param>
                                 <query-param name="email" is-mandatory="true" type="string" sample="'b'">
                                  <stereotype stereotype="EMAIL" />
                                 </query-param>
                                 <query-param name="role" is-mandatory="true" type="string" sample="'c'">
                                    <query-param-link entity-name="sec_role" field-name="role"/>
                                 </query-param>
                             </query-params>
                         </query>
                    </queries>
                </statement-model>
             </model>
      <targets catalog-entry="OpenXava" >
      </targets>
     </configuration>
    </generator-config> 
    The main points are:
    • call ask_for_role with 3 parameters
    • description of the parameters (name, type, presence, stereotype)
    • restrict to a set of value coming from a table and field (query-param-link)

    Openxava design flow

    The input is in format of an Openxava/JPA2 entity to enable binding and link to other entity.
    But this entity will never be persisted and never lookup.
    The input screen will be accessed directly, and button match the action. After the action is performed a message is display.
    To enable this flow OX controllers.xml and application.xml nodes are generated.
    The action binding the input data from the form; validating and calling the store procedure call are also generated.

    Generated code

    Input/Output bean generated


    import javax.persistence.*;
    import org.openxava.annotations.*;
    
    import net.sf.mp.demo.porphyry.domain.security.Role;
    
    @Entity (name="AskForRoleIn")
    @Table (name="ask_for_role")
    @Views({
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @view-base-ask_for_role@
     @View(
      name="base",
      members=
            ""  
            + "username  ; "
            + "email  ; "
            + "role  ; "
      ),
    //MP-MANAGED-UPDATABLE-ENDING
     @View(
      name="Create", 
      extendsView="base"
     ),
     @View(
      name="Update", 
      extendsView="base",
            members=
              ""  
     ),
     @View(extendsView="base",
            members=
              ""  
     ),
        @View(name="askForRoleDEFAULT_VIEW", 
        members=
              " username ;"  
            + "email  ; "
            + "roleTransient  ; "
     ),
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @view-reference-ask_for_role@
        @View(name="reference", 
           extendsView="askForRoleDEFAULT_VIEW"
    //MP-MANAGED-UPDATABLE-ENDING
        )
    })
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @class-annotation@
    //MP-MANAGED-ADDED-AREA-ENDING @class-annotation@
    public class AskForRoleIn {
    
         @Id @Column(name="username" ,length=255)
        private String username; 
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @email-field-annotation@
    //MP-MANAGED-ADDED-AREA-ENDING @email-field-annotation@
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @ATTRIBUTE-email@
        @Column(name="email",  length=255, nullable=false,  unique=false)
        @Required
        @Stereotype ("EMAIL")
        private String email;
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @role_TRANSIENT-field-annotation@
    //MP-MANAGED-ADDED-AREA-ENDING @role_TRANSIENT-field-annotation@
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @ATTRIBUTE-role_TRANSIENT@
     @Transient
     @ReadOnly
        private String roleTransient;
    //MP-MANAGED-UPDATABLE-ENDING
    
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @parent-Role-ask_for_role@
        @ManyToOne (fetch=FetchType.LAZY ,optional=false) 
        @JoinColumn(name="role", referencedColumnName = "ID", nullable=false,  unique=false  )
        @ReferenceView ("reference") 
        private Role role;
    
    ...
    } 
    Although not persisted and never looked up AskForRoleIn can be used by Openxava:
    •  to pass by information as a DTO
    • perform validation
    • perform assignment (it is linked to table Role)
    • it contains a transient field roleTransient that will be used by the Openxava action to copy the 'role' field of the 'role' table (not the pk)
    • offers a view with
      • simple input field
      • associated entities
    • @Id is associated to one field (otherwise JPA/Hibernate complains)

    Action


    /**
     * template reference : 
     * - name      : ActionOX.SDD.query
     * - file name : ActionOX.SDD.query.vm
     * - time      : 2013/08/22 AD at 12:29:41 CEST
    */
    package net.sf.mp.demo.porphyry.sdd.action.statement;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    
    import org.openxava.jpa.*;
    import org.openxava.model.*;
    import org.openxava.util.*;
    import org.openxava.validators.*;
    import org.openxava.actions.*;
    import java.util.*;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.Query;
    
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    
    import net.sf.mp.demo.porphyry.sdd.out.statement.AskForRoleOutList;
    import net.sf.mp.demo.porphyry.sdd.out.statement.AskForRoleOut;
    import net.sf.mp.demo.porphyry.sdd.in.statement.AskForRoleIn;
    
    public class AskForRoleAction extends ViewBaseAction {
    
        public static final String QUERY_NATIVE = "call ask_for_role (?,?,?)";
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-ask_for_role@
        public AskForRoleOutList execute (AskForRoleIn askForRoleIn) {
            AskForRoleOutList askForRoleOutList = new AskForRoleOutList();
            List list = executeJDBC (askForRoleIn);
            askForRoleOutList.setAskForRoleOuts (list);
            return askForRoleOutList;
        }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_JDBC-ask_for_role@
     public List<askforroleout> executeJDBC(AskForRoleIn askForRoleIn) {
      if (askForRoleIn==null)
       askForRoleIn = new AskForRoleIn();
      List<askforroleout> list = new ArrayList<askforroleout>();
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      Connection conn = null;
      try {
       conn = getConnection();
       pstmt = conn.prepareStatement(QUERY_NATIVE);
                if (askForRoleIn.getUsername()==null) {
                   pstmt.setNull(1, java.sql.Types.VARCHAR);
                } else {
                   pstmt.setString(1, askForRoleIn.getUsername()); 
                }
                if (askForRoleIn.getEmail()==null) {
                   pstmt.setNull(2, java.sql.Types.VARCHAR);
                } else {
                   pstmt.setString(2, askForRoleIn.getEmail()); 
                }
                if (askForRoleIn.getRoleTransient()==null) {
                   pstmt.setNull(3, java.sql.Types.VARCHAR);
                } else {
                   pstmt.setString(3, askForRoleIn.getRoleTransient()); 
                }
       rs = pstmt.executeQuery();
      } catch (Exception e) {
            e.printStackTrace();
         } finally {
           try {
             rs.close();
             pstmt.close();
             conn.close();
           } catch (Exception e) {
             e.printStackTrace();
           }
         }
      return list;
     }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //if JPA2 implementation is hibernate
        @SuppressWarnings("deprecation")   
        public Connection getConnection() throws HibernateException {  
            Session session = getSession();  
            Connection connection = session.connection();  
            return connection;  
        } 
        
        private Session getSession() {  
            Session session = (Session) XPersistence.getManager().getDelegate();  
            return session;  
        }
    
     public void execute() throws Exception {
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @execute-porphyry@
            //super.execute();
            //TODO
            Messages errors = 
                MapFacade.validate("AskForRoleIn", getView().getValues());
            if (errors.contains()) throw new ValidationException(errors);
            AskForRoleIn e = new AskForRoleIn();
     e.setUsername((String)getView().getValue("username"));
     e.setEmail((String)getView().getValue("email"));
    
     // parent to copy to transient field
            Map roleMap = (Map)getView().getValue("role");
            if (roleMap!=null) {
      e.setRoleTransient ((String)roleMap.get("role"));
            }
      
            try {
                execute(e);
            } catch (Exception ex) {
                errors = new Messages();
                errors.add(ex.getMessage());
                throw new ValidationException(errors);
            }
            //TODO return list
            addInfo("call AskForRoleAction done!");
    //MP-MANAGED-UPDATABLE-ENDING
    
     }
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    
    } 
    • perform validation
    • retrieve simple type as well as complex (Role object type)
    • copy values a input of the store proc call
      • here the store proc does not return anything so there is no parsing of the resultset.
    Here one can argue that we do not need the transient field 'roleTransient' in AskForRoleIn.
    This is true. It is present because it was easier from a generation point of view to keep the parameter order of the stored procedure call.

    Controller.xml


    <controllers> 
        <!-- statement driven development SDD -->
      <!-- $table.name -->
     <controller name="AskForRoleController">
      <action name="askForRole" mode="detail" class="net.sf.mp.demo.porphyry.sdd.action.statement.AskForRoleAction" >
       <use-object name="xava_view" />
      </action>     
     </controller>
    </controllers> 
    
    Provides controller and action

    Application.xml


    <application name="porphyry"> 
    
        <!-- statement driven development SDD -->
     <module name="AskForRoleIn" >
         <model name="AskForRoleIn"/>
         <view name="base"/> 
         <controller name="AskForRoleController"/>
         <mode-controller name="DetailOnly"/>
     </module>
     
    </application>
    
    
    Wiring between Model/View/Controller and mode
    Detail mode selected (ie no lookup)

    Screens

    Input screen 

    Available at ${yourcontext}/xava/home.jsp?application=porphyry&module=AskForRoleIn
    Is also available as a menu entry under statement

     Sub select Use case


     

     

    Performing the action

    Viewing the result


    Since it is store in table USER_ROLE_REQUEST
    Minuteproject generates also a CRUD access on this table.
    By filtering we check that the input of the store proc+ additional business field are stored correctly.



     

    Conclusion

    Statement Driven Development - SDD
    • provides tooling for analyst
    • RAD for developer 
      • any sql statement could now be an advance business UC (sub affection, validation)
      • necessary OX gearing is generated.
    • is a pillar of development productivity
    Minuteproject is also generating portlet.xml in order to deploy 'Transient-definition portlet' on portal such as Liferay.


    Monday, August 19, 2013

    Northwind DB revisited with MP 4 OX

    Minuteproject (0.8.4) now supports ms-sqlserver.
    It has been tested with MS Northwind DB on sqlserver 2014.
    Here are the steps to follow to get an OpenXava application from the northwind DB.

    Goal:
    Get a working Northwind Openxava application in couple of seconds.
    For hungry minds please find the resulting code on googlecode.

    Northwind DB

    Northwind database is a sample DB provided by Microsoft.
    It can be found at http://northwinddatabase.codeplex.com/releases/view/71634

    The sql provided to create the schema has DB objects (tables/views) containing space.
    This sql has been revisited to remove those spaces a version can be found here.

    • Install SQLServer
    • Create an account
    • Run the script.

    MinuteProject with SQLSERVER

    Minuteproject
    • uses com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc driver
    • associates 'identity' as a primary key strategy by default on the console
    • associates org.hibernate.dialect.SQLServer2008Dialect for hibernate dialect
    • proposes maven artifact-id=sqljdbc4; group-id=com.microsoft.sqlserver; version=4.0 for pom configuration
    Furthermore when retrieving the metadata of your model, set 'dbo' of the schema node.

    Minuteproject configuration



    <!DOCTYPE root>
    <generator-config>
     <configuration>
      <model name="nortewind" version="1.0" package-root="net.sf.mp.demo">
       <data-model>
        <driver name="sqlserver" version="4.0" groupId="com.microsoft.sqlserver"
         artifactId="sqljdbc4"></driver>
        <dataSource>
         <driverClassName>com.microsoft.sqlserver.jdbc.SQLServerDriver</driverClassName>
         <url>jdbc:sqlserver://localhost:1433;databaseName=northwind</url>
         <username>sqlserver</username>
         <password>xxxxxxxx</password>
        </dataSource>
        <!-- for Oracle and DB2 please set the schema <schema> </schema> -->
        <schema>dbo</schema>
        <primaryKeyPolicy oneGlobal="true">
         <primaryKeyPolicyPattern name="identityPattern"></primaryKeyPolicyPattern>
        </primaryKeyPolicy>
       </data-model>
       <business-model>
        <business-package default="business">
            <condition type="package" database-object-type="VIEW" result="review"></condition>   
        </business-package>
        <enrichment>
         <conventions>
              <view-primary-key-convention 
                type="apply-default-primary-key-otherwise-first-one" 
                default-primary-key-names="ID" /> 
             <column-naming-convention
           type="apply-fix-primary-key-column-name-when-no-ambiguity-and-not-natural"
           default-value="ID" />
             <entity-naming-convention type="apply-field-alias-based-on-camelcase"/>
             <column-naming-convention type="apply-field-alias-based-on-camelcase"/>
    
          <reference-naming-convention
           type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="false" />
          <reference-naming-convention type="apply-many-to-many-aliasing" is-to-plurialize="false"/>
         </conventions>
        </enrichment>
       </business-model>
    
      </model>
    <!-- -->    <targets catalog-entry="OpenXava">
      </targets>  
    <!--    <targets catalog-entry="JPA2" >
      </targets>   -->
     </configuration>
    </generator-config>
    


    This configuration will allow you to

    • retrieve sqlserver information for the model northwind (do not forget to specify 'dbo' in schema)
    • assign identity as primary key strategy
    • separate table package from view package. (tables go in package business, views in package review)
    • apply convention
      • use camel case for field and entity (table/view)
      • associate a primary key if not present. The primary key would then be attribute to the field ID if present otherwise the first found.
      • have clean name (here not plurialized because northwind already use plural in DB object name) when possible (i.e. there is no variable name collisition - this occurs when you have more than one relationship between two objects).
    • use the track OpenXava from the catalog.

    Steps

    Copy this configuration to /mywork/config as northwind-OX.xml 
    From a command line run model-generation.cmd/sh northwind-OX.xml 
    The result will go in /mywork/output/nortewind/OpenXava

    Open a prompt in /mywork/output/nortewind/OpenXava
    set OX_HOME and MP_HOME (where you install Openxava and Minuteproject)
    • set OX_HOME= path-to-Openxava // export OX_HOME in linux
    • set MP_HOME= path-to-Minuteproject // export MP_HOME in linux

    Run build-nortewind.cmd/sh

    This should be enough to get a

    Enjoy!

    Screenshots

    You have Openxava CRUD on all tables and selection on views

    Menu entries


    Select Order

     Region details
    List of products (view)

    MinuteProject with console

    The code can also be generated with Minuteproject console, it is faster since you do not have to write any configuration. Meanwhile it is more limited since not all the convention are available on the console.

    click /bin/start-console.sh/cmd
    apply the following parameters



    Click on generate and the output will go to /output/northwind/Openxava

    You can them process with the same steps as with the configuration.


    Conclusion

    Minuteproject 0.8.4 offers sqlserver generation and simplifies the configuration of the tracks.
    Feel free to pick up others by picking them from the drop down list or by changing the attribute catalog-entry of the node targets by one of the following value:

    JPA2
    JPA2-ABL
    BSLA-JPA2
    REST-JEE
    REST-SpringMVC
    REST-CXF-Spring
    REST-CXF-JEE
    WS-JEE
    JOOQ
    Primefaces-Spring
    Primefaces-JEE
    FitNesse
    Solr
    OpenXava
    Grails
    Play
    Vaadin
    Roo
    Maven Spring Hibernate
    Maven Spring JPA/Hibernate
    SpringService

    Remark some tracks are under construction.



    Thursday, August 8, 2013

    Minuteproject facets for analysts

    This article resumes the features Minuteproject offers to the analyst to perform a reverse-analysis of the application model.

    Core

    Minuteproject core functionality is smart reverse-engineering from a database.
    • Reverse-engineering means that you can deduce lots of artifacts based on the database schema structure.
    • Smart means that you can enrich the database elements individually or globally by convention to get the resulting artifacts finer (with added-value) instead of a default bulk solution. Examples:
      •  Your database objects can be:
        • packaged
        • secured
        • aliased (the name of your development object (in java) can follow different convention than those of your DB
    Sample
    http://minuteproject.blogspot.be/2012/05/rigajug-demo-2-jpa2.html
    But Minuteproject is even more.

    Views and alternative models

    You can work with views conveniently by enriching them with primary key (a unique field will do) and foreign key (pointing to another table or view). This feature enables you to get quickly alternative model with a graph (parent-child relationship) that you can query.
    An illustration is when you want to get a report grouping information per country/dept on one side and on the other side you have some report details.

    SDD - Statement driven development

    Why just reverse-engineer the structure? We can also reverse-engineer the statement (queries, store-proc calls etc...)
    Statement-driven-development focuses on I/O: it goes back to the fundamentals and follow the philosophy WYSIWYG (What You State Is What You Get)
    If you know how-to write sql, you are not far from having a REST application or any application in the track that support this feature (at the moment of writing Primefaces, ADF).

    Sample
    http://minuteproject.blogspot.be/2013/08/sdd-as-productivity-weapon-4-openxava.html

    Transfer Objects

    Sometimes, you need to have object that may not be persisted or completely persisted.
    Example you want ad-hoc forms and actions where some info comes from the database some not.
    Minuteproject offers you the possibility to describe and generate for those objects.

    Sample
    http://minuteproject.blogspot.be/2013/08/transient-definition-as-productivity.html



    Friday, August 17, 2012

    sql query to primefaces app with sdd

    Intro

    Primefaces provides cool rendering facilities on one hand.
    Minuteproject provides reverse-engineering facilities base on relational database but also on sql statement, on the other hand.

    Now you have a query and you want to have a primefaces app!... without writing any (ANY) line of code.
    This article is for YOU.

    If you download Minuteproject 0.8.2+ you can have an example on how-to do it (delivered as a demo).
    This article tracks the steps to follow as well as explaining the demo.

    Enrichment

    You can consult the entire configuration in /demo/config/mp-config-JSF-Spring.xml

    statement-driven-development

    Minuteproject provides a enrichment area where you can set up your sql queries.
    Here is the snippet part correspondinng to the query enrichment inside the statement-model node.

            ....
       <statement-model>
                    <queries>
                         <query name="get addresses by criteria" id="c">
                             <query-body><value>
    <![CDATA[select * from address where latitude between ? and ? and longitude between ? and ? and lcase(city) like ?]]>
                                </value></query-body>
                             <query-params>
                                 <query-param name="latitude_lower_limit" is-mandatory="false" type="DOUBLE" sample="37"></query-param>
                                 <query-param name="latitude_upper_limit" type="DOUBLE" sample="38"></query-param>
                                 <query-param name="longitude_lower_limit" type="DOUBLE" sample="-122"></query-param>
                                 <query-param name="longitude_upper_limit" type="DOUBLE" sample="-123"></query-param>
                                 <query-param name="city" type="STRING" sample="'S'" convert="lowercase,append%" default="%"/>
                             </query-params>
                         </query>
                    </queries>
                </statement-model>
      </model> 
    

    You specify the name of the query which will be used to create
    • DTO class
    • JSF menu entry
    • JSF URL
    • JSF form name
    You specify in question marks the input parameters you want and in query-param what is the name to give them. Those name with be used to create
    • DTO variable
    • JSF form entries with validation

    Empower primefaces with minuteproject sdd templates

    Primefaces needs to have new templates about what to generate coming from SDD.
    This is given in the track 
                <target refname="SDD-beans" 
                    outputdir-root="../output/JSF-Spring/JPA2"
                   fileName="mp-template-config-SDD-beans.xml" 
                   templatedir-root="../../template/framework/bean">
                </target> 
    

     

    Steps

    Set-up

    • Download last version of Minuteproject.
    • Unzip in directory
    • Start sample petshop DB: /sample/start-petshop-database.cmd/sh
    • Go to /config/demo
      • Run demo-JSF-Spring-primefaces.cmd/sh
    • Go to /target/mp-bsla
      • Run install-maven.cmd (it installs a MVN dependency for spring DAO that is not yet (sic) in mvn-central)
    • Go to /demo/output/JSF-Spring
      • Run 'mvn clean package'
    Alternative run all demos

     

    Generated code

    You have a JSF primeface 3.3 app with cupertino theme offering Create, List, Delete on entities and with I/O screen of each query here (GetAddressesByCriteriaInput).

    The generated code is decomposed into 3 maven project:
    • You have JPA2 backend+ DTOs for each SDD query I/O
    • You have Spring 3 integration with CRUD DAO on top of entity and DAO for SDD query DTOs.  
    • You have a JSF front-end integrated with spring
    This way you can easily work with minuteproject Updatable Code feature to get what you customize your artifact without losing the power of consecutive generations.

    Deployment

    Setup

    The resulting application is petshopApp.war in /demo/output/JSF-Spring/JSF/target
    It is ready to be dropped on tomcat or other JEE container.
     
    But before take care that your EL (expression language jar spec and impl) are 2.2.
    On tomcat (check that /lib contains 
    • el-api-2.2.jar
    • el-impl-2.2.jar
    Check the stackoverflow entries
    Note: that there is no connection pool dependency on the container. By default in nothing is specified in the JPA2 target 'environment' property. The environment is considered has local so no reference to a JNDI CP.

    Deploy

    Start tomcat (/bin/startup.cmd/sh)
    Drop petshopApp.war in /webapps

     

    Result

    Here is a little UC where we create an address and we retrieve it base on the ad-hoc sdd query.

    Create address

    List addresses

    SDD in action: Use specific criteria to address

    Check validation

     

    Future

    This is not enough...
    Why not having multiple statement that works together to produce for example a master-detail or dashboard-master-detail?... primefaces provides nice toolset for that.

    Break current limitations
    • SDD in the current form is sql oriented, but nothing prevent from having it REST URL resource oriented.
    • Add validations, mapping, convertions on I/O params.
    • Provide presentation defaulting
    • Provide navigation between SDD components
    • Add filtering and improved query builder

    Wednesday, May 30, 2012

    RigaJUG demo - REST - SDD

    On the model
    You want to extract some info via sql such as

    select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email 
    from translation t, language l, user tl, translation_key k
    where
    t.language_id = l.idlanguage
    and tl.idUser = t.translator_id
    and k.id = t.key_id
    order by key_name
    

    And you want to parametarize the extraction by passing some filtering input
    select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email 
    from translation t, language l, user tl, translation_key k
    where
    t.language_id = l.idlanguage
    and tl.idUser = t.translator_id
    and k.id = t.key_id
    and k.key_name like ?
    and l.code like ?
    order by key_name
    limit ?
    

    And you want to have a REST access!
    And you want it NOW!

    Good news SDD Statement Driven Development is there for you!

    From the above statement you can extract 3 info:
    • an input (in java a DTO bean) with keyName, code, limit as params
    • an output  (in java a DTO bean) with keyName, translation, dateFinalization, code, description, firstName, lastName, email
    • a functionality to name
    This is enough to get a REST-CXF application instantly!
    This page will show you how to reach it!

    Intro

    This page correspond to a demo to belonging to a more global presentation.
    To be able to demostrate it there are couple of prerequisits:
    • download minuteproject 0.8.1+
    • install maven
    • install tomcat
    • install mysql and create the schema (see demo JPA2)
    This page will show you how to:
    • configure
    • generate
    • build
    • deploy
    • test

    Configuration

    Minuteproject works with a configuration file that indicates:

    • where is the model
    • how to enrich it
      • enable Statement Driven Development declartion
    • against which technologies to generate (targets)

    Here is the configuration TRANXY-JPA2-Spring-REST-CXF-SDD.xml
    <!DOCTYPE root>
    <generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" 
    xs:noNamespaceSchemaLocation="../config/mp-config.xsd">
     <configuration>
      <conventions>
       <target-convention type="enable-updatable-code-feature" />
      </conventions>
      <model name="tranxy" version="1.0" package-root="net.sf.mp.demo">
       <data-model>
        <driver name="mysql" version="5.1.16" groupId="mysql"
         artifactId="mysql-connector-java"></driver>
        <dataSource>
         <driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
         <url>jdbc:mysql://127.0.0.1:3306/tranxy</url>
         <username>root</username>
         <password>mysql</password>
        </dataSource>
        <primaryKeyPolicy oneGlobal="false" >
         <primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
        </primaryKeyPolicy>
       </data-model>
       <business-model>
        <generation-condition>
         <condition type="exclude" startsWith="QUARTZ"></condition>
        </generation-condition>
        <business-package default="tranxy">
         <condition type="package" startsWith="trans" result="translation"></condition>
        </business-package>
        <enrichment>
         <conventions>
          <!-- manipulate the structure and entities BEFORE manipulating the 
           entities -->
          <column-naming-convention type="apply-strip-column-name-suffix"
           pattern-to-strip="ID" />
          <reference-naming-convention
           type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
         </conventions>
              <entity name="language_x_translator">
                  <field name="language_id" linkReferenceAlias="translating_language" />
                  <field name="user_id" linkReferenceAlias="translator" />
              </entity>
              <entity name="LANGUAGE_X_SPEAKER">
                  <field name="LANGUAGE_ID" linkToTargetEntity="LANGUAGE"
                      linkToTargetField="IDLANGUAGE" linkReferenceAlias="spoken_language" />
                  <field name="user_id" linkReferenceAlias="speaker" />
              </entity>
              <entity name="APPLICATION">
                  <field name="TYPE">
                      <property tag="checkconstraint" alias="application_type">
                          <property name="OPENSOURCE"/>
                          <property name="COPYRIGHT" />
                      </property>
                  </field>
              </entity>
        </enrichment>
       </business-model>
          <statement-model>
             <queries>
                 <query name="get translation info">
                     <query-body><value>
    <![CDATA[select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email from translation t, language l, user tl, translation_key k where t.language_id = l.idlanguage and tl.idUser = t.translator_id and k.id = t.key_id and k.key_name like ? and l.code like ? order by key_name limit ?]]>
                        </value></query-body>
                     <query-params>
                         <query-param name="key" is-mandatory="false" type="STRING" sample="'test'"></query-param>
                         <query-param name="code" is-mandatory="false" type="STRING" sample="'FR'"></query-param>
                         <query-param name="max" is-mandatory="false" type="INT" sample="10"></query-param>
                     </query-params>
                 </query>
              </queries>
          </statement-model>
      </model>
      <targets>
                
          <target refname="REST-CXF-BSLA" 
             name="default" 
             fileName="mp-template-config-REST-CXF-Spring.xml" 
             outputdir-root="../../DEV/latvianjug/tranxy/rest"
             templatedir-root="../../template/framework/cxf">
          </target>
    
          <target refname="BackendOnBsla" 
             name="default" 
             fileName="mp-template-config-JPA2-bsla.xml" 
             outputdir-root="../../DEV/latvianjug/tranxy/bsla"
             templatedir-root="../../template/framework/bsla">
              <property name="add-cache-implementation" value="ehcache"></property>
          </target> 
          
       <target refname="JPA2" fileName="mp-template-config-JPA2.xml"
        outputdir-root="../../DEV/latvianjug/tranxy/jpa" 
            templatedir-root="../../template/framework/jpa">
        <property name="add-querydsl" value="2.1.2"></property>
        <property name="add-jpa2-implementation" value="hibernate"></property>
            <property name="add-cache-implementation" value="ehcache"></property>
            <property name="add-domain-specific-method" value="true"></property>
            <property name="add-xmlbinding" value="true"></property> 
            <property name="add-xml-format" value="lowercase-hyphen"></property> 
       </target>
       
          <target refname="SDD-beans" 
              outputdir-root="../../DEV/latvianjug/tranxy/jpa"
             fileName="mp-template-config-SDD-beans.xml" 
             templatedir-root="../../template/framework/bean">
             <property name="add-xmlbinding" value="true"></property> 
             <property name="add-xml-format" value="lowercase-hyphen"></property>
          </target>
    
          <target refname="COMMON-LIB" 
             fileName="mp-template-config-COMMON-LIB.xml" 
             templatedir-root="../../template/framework/common">
          </target>   
                            
          <target refname="MavenMaster" 
             name="maven" 
             fileName="mp-template-config-maven.xml" 
             outputdir-root="../../DEV/latvianjug/tranxy"
             templatedir-root="../../template/framework/maven">
          </target>
    
          <target refname="CACHE-LIB" fileName="mp-template-config-CACHE-LIB.xml"
              templatedir-root="../../template/framework/cache">
          </target>
          
       <target refname="LIB" fileName="mp-template-config-bsla-LIB-features.xml"
        templatedir-root="../../template/framework/bsla">
       </target>
    
          <target refname="REST-LIB" 
             fileName="mp-template-config-REST-LIB.xml" 
             templatedir-root="../../template/framework/rest">
          </target>
          <target refname="SPRING-LIB" 
             fileName="mp-template-config-SPRING-LIB.xml" 
             templatedir-root="../../template/framework/spring">
          </target>
    
      </targets>
     </configuration>
    </generator-config>
    
    Aside of the 'standard' CXF CRUD generation (see on demo). Here a set of new artifacts are generated to enable the enriched 'get translation infos' function.
    The part of the configuration may seem a bit long, I agree. It describes what templates and metadata file to use.
    I plan to have simplify version à la minuteproject console where you just reference a track name belonging to a catalog and pass some properties.
    The set of target currently displayed can be copied from the sample provided in /demo/config.

    Generation

    Drop TRANXY-JPA2-Spring-REST-CXF-SDD.xml in /mywork/config and run: model-generation.cmd/sh TRANXY-JPA2-Spring-REST-CXF-SDD.xml
    Generated artifacts go to /DEV/latvianjug/tranxy.

    Generated artifacts

    Here are presented on the new artifacts of the CXF-JPA2 track.
    The artifacts are presented from a top down perspective (starting from REST-Frontend down to Persistence layer).
    The name of the artifacts are deeply linked to the query name 'get translation info'.

    REST resources

    2 resources file are generated, one for json rendering the other for xml.

    GetTranslationInfoJsonResource.java

    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : CXFSpringSDDJsonResource
     * - file name : CXFSpringSDDResource.vm
    */
    package net.sf.mp.demo.tranxy.rest.statement;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    import java.util.Date;
    import java.util.List;
    import java.util.ArrayList;
    import java.io.*;
    import java.sql.*;
    
    import javax.servlet.http.*;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.FormParam;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.POST;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.GET;
    import javax.ws.rs.PUT;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.Context;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Request;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.core.UriInfo;
    import javax.xml.bind.JAXBElement;
    
    import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
    import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
    import net.sf.mp.demo.tranxy.dao.sdd.face.statement.GetTranslationInfoDaoFace;
    /**
     *
     * <p>Title: GetTranslationInfoJsonResource</p>
     *
     * <p>Description: remote interface for GetTranslationInfoJsonResource service </p>
     *
     */
    @Produces ({MediaType.APPLICATION_JSON})
    @Consumes ({MediaType.APPLICATION_JSON})
    @Service ("getTranslationInfoJsonResource")
    @Transactional
    @Path ("/rest/json/gettranslationinfos")
    public class GetTranslationInfoJsonResource {
    
        @Autowired
        @Qualifier("getTranslationInfoDaoFace")
        GetTranslationInfoDaoFace getTranslationInfoDaoFace;
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
        @GET
        @Produces (MediaType.APPLICATION_JSON) 
    
        public GetTranslationInfoOutList executeAndFormatXml (
            @QueryParam ("key") String key ,
            @QueryParam ("code") String code ,
            @QueryParam ("max") Integer max ) {
            return execute(
               key ,
               code ,
               max   
      );
        }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
        public GetTranslationInfoOutList execute (
            String key ,
            String code ,
            Integer max ) {
      GetTranslationInfoIn getTranslationInfoIn = new GetTranslationInfoIn ();
      getTranslationInfoIn.setKey (key);
      getTranslationInfoIn.setCode (code);
      getTranslationInfoIn.setMax (max);
            return getTranslationInfoDaoFace.execute(getTranslationInfoIn);
        }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    
    }

    GetTranslationInfoXmlResource.java

    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : CXFSpringSDDXmlResource
     * - file name : CXFSpringSDDResource.vm
    */
    package net.sf.mp.demo.tranxy.rest.statement;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    import java.util.Date;
    import java.util.List;
    import java.util.ArrayList;
    import java.io.*;
    import java.sql.*;
    
    import javax.servlet.http.*;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.FormParam;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.POST;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.GET;
    import javax.ws.rs.PUT;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.Context;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Request;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.core.UriInfo;
    import javax.xml.bind.JAXBElement;
    
    import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
    import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
    import net.sf.mp.demo.tranxy.dao.sdd.face.statement.GetTranslationInfoDaoFace;
    /**
     *
     * <p>Title: GetTranslationInfoXmlResource</p>
     *
     * <p>Description: remote interface for GetTranslationInfoXmlResource service </p>
     *
     */
    @Produces ({MediaType.APPLICATION_XML})
    @Consumes ({MediaType.APPLICATION_XML})
    @Service ("getTranslationInfoXmlResource")
    @Transactional
    @Path ("/rest/xml/gettranslationinfos")
    public class GetTranslationInfoXmlResource {
    
        @Autowired
        @Qualifier("getTranslationInfoDaoFace")
        GetTranslationInfoDaoFace getTranslationInfoDaoFace;
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
        @GET
        @Produces (MediaType.APPLICATION_XML) 
    
        public GetTranslationInfoOutList executeAndFormatXml (
            @QueryParam ("key") String key ,
            @QueryParam ("code") String code ,
            @QueryParam ("max") Integer max ) {
            return execute(
               key ,
               code ,
               max   
      );
        }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
        public GetTranslationInfoOutList execute (
            String key ,
            String code ,
            Integer max ) {
      GetTranslationInfoIn getTranslationInfoIn = new GetTranslationInfoIn ();
      getTranslationInfoIn.setKey (key);
      getTranslationInfoIn.setCode (code);
      getTranslationInfoIn.setMax (max);
            return getTranslationInfoDaoFace.execute(getTranslationInfoIn);
        }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    
    }
    

    Spring configuration

    application-context.xml

    It is adapted to reflect the new beans
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xmlns:jaxrs="http://cxf.apache.org/jaxrs"
     xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/context 
      http://www.springframework.org/schema/context/spring-context-3.0.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://cxf.apache.org/jaxrs
      http://cxf.apache.org/schemas/jaxrs.xsd">
    
        <import resource="classpath:META-INF/cxf/cxf.xml" />
        <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
        <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
        
        <context:component-scan base-package="net.sf.mp.demo.tranxy.rest"/>
        <context:component-scan base-package="net.sf.mp.demo.tranxy.dao.sdd.impl"/>
    
        <import resource="classpath:net/sf/mp/demo/tranxy/factory/spring/spring-config-Tranxy-BE-main.xml"/>    
     
        <jaxrs:server id="restContainer" address="/">
            <jaxrs:serviceBeans>
       <!-- tranxy --> 
       <ref bean="applicationResource"/>
       <ref bean="languageResource"/>
       <ref bean="userResource"/>
       <!-- translation --> 
       <ref bean="translationResource"/>
       <ref bean="translationKeyResource"/>
       <ref bean="translationRequestResource"/>
     
       <!-- statements -->
       <ref bean="getTranslationInfoXmlResource"/>
       <ref bean="getTranslationInfoJsonResource"/>
            </jaxrs:serviceBeans>
        </jaxrs:server> 
    
    </beans> 
    

    DAO layer

    Interface GetTranslationInfoDaoFace.java

    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : SDDDaoInterface
     * - file name : SDDDaoInterface.vm
    */
    package net.sf.mp.demo.tranxy.dao.sdd.face.statement;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    
    import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
    import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
    
    /**
     *
     * <p>Title: GetTranslationInfoDaoFace</p>
     *
     * <p>Description: remote interface for GetTranslationInfoDaoFace service </p>
     *
     */
    public interface GetTranslationInfoDaoFace {
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
        public GetTranslationInfoOutList execute (GetTranslationInfoIn getTranslationInfoIn) ;
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    
    
    } 
    

    Implementation GetTranslationInfoRepository.java

    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : SDDSpringJPADao
     * - file name : SDDSpringJPADao.vm
    */
    package net.sf.mp.demo.tranxy.dao.sdd.impl.statement;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.persistence.Query;
    
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Repository;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOutList;
    import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOut;
    import net.sf.mp.demo.tranxy.sdd.in.statement.GetTranslationInfoIn;
    import net.sf.mp.demo.tranxy.dao.sdd.face.statement.GetTranslationInfoDaoFace;
    
    /**
     *
     * <p>Title: GetTranslationInfoRepository</p>
     *
     * <p>Description: SDD DAO Spring JPA implementation </p>
     *
     */
    @Repository ("getTranslationInfoDaoFace")
    @Transactional(propagation = Propagation.REQUIRED) 
    public class GetTranslationInfoRepository implements GetTranslationInfoDaoFace{
    
     public static final String QUERY_NATIVE = "select k.key_name, t.translation, t.date_finalization, l.code, l.description, tl.first_name, tl.last_name, tl.email from translation t, language l, user tl, translation_key k where t.language_id = l.idlanguage and tl.idUser = t.translator_id and k.id = t.key_id and k.key_name like ? and l.code like ? order by key_name limit ?";
    
     @PersistenceContext(unitName = "tranxy")  
        EntityManager entityManager;  
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_GET-get translation info@
        public GetTranslationInfoOutList execute (GetTranslationInfoIn getTranslationInfoIn) {
      GetTranslationInfoOutList getTranslationInfoOutList = new GetTranslationInfoOutList();
      List<GetTranslationInfoOut> list = executeJDBC (getTranslationInfoIn);
      getTranslationInfoOutList.setGetTranslationInfoOuts (list);
            return getTranslationInfoOutList;
        }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @SDD_EXECUTE_JDBC-get translation info@
     public List<GetTranslationInfoOut> executeJDBC(GetTranslationInfoIn getTranslationInfoIn) {
      List<GetTranslationInfoOut> list = new ArrayList<GetTranslationInfoOut>();
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      Connection conn = null;
      try {
       conn = getConnection();
       pstmt = conn.prepareStatement(QUERY_NATIVE);
       pstmt.setString(1, getTranslationInfoIn.getKey()); 
       pstmt.setString(2, getTranslationInfoIn.getCode()); 
       pstmt.setInt(3, getTranslationInfoIn.getMax()); 
       rs = pstmt.executeQuery();
       while (rs.next()) {
        GetTranslationInfoOut getTranslationInfoOut = new GetTranslationInfoOut();
        getTranslationInfoOut.setKeyName(rs.getString(1)); 
        getTranslationInfoOut.setTranslation(rs.getString(2)); 
        getTranslationInfoOut.setDateFinalization(rs.getString(3)); 
        getTranslationInfoOut.setCode(rs.getString(4)); 
        getTranslationInfoOut.setDescription(rs.getString(5)); 
        getTranslationInfoOut.setFirstName(rs.getString(6)); 
        getTranslationInfoOut.setLastName(rs.getString(7)); 
        getTranslationInfoOut.setEmail(rs.getString(8)); 
        list.add(getTranslationInfoOut);
             }
      } catch (Exception e) {
            e.printStackTrace();
         } finally {
           try {
             rs.close();
             pstmt.close();
             conn.close();
           } catch (Exception e) {
             e.printStackTrace();
           }
         }
      return list;
     }
    //MP-MANAGED-UPDATABLE-ENDING
    
    //if JPA2 implementation is hibernate
     @SuppressWarnings("deprecation")   
        public Connection getConnection() throws HibernateException {  
      Session session = getSession();  
      Connection connection = session.connection();  
      return connection;  
        } 
     
        private Session getSession() {  
         Session session = (Session) entityManager.getDelegate();  
         return session;  
        }
     
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    
    }
    

    Beans!!

    Welcome back DTO! You do not need to re-wrap your sql against ORM!
    Go straight: your I/O = your DTOs

    Input bean

    GetTranslationInfoIn.java


    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : SDDInputBean
     * - file name : JavaBean.vm
    */
    package net.sf.mp.demo.tranxy.sdd.in.statement;
    
    import java.util.List;
    import java.util.ArrayList;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    /**
     *
     * <p>Title: GetTranslationInfoIn</p>
     *
     * <p>Description: Java Bean containing a collection of GetTranslationInfo </p>
     *
     */
    public class GetTranslationInfoIn {
    
        private String key;
        private String code;
        private Integer max;
    
        /**
        * Default constructor
        */
        public GetTranslationInfoIn() {
        }
     
        public String getKey() {
            return key;
        }
     
        public void setKey (String key) {
            this.key =  key;
        }
    
        public String getCode() {
            return code;
        }
     
        public void setCode (String code) {
            this.code =  code;
        }
    
        public Integer getMax() {
            return max;
        }
     
        public void setMax (Integer max) {
            this.max =  max;
        }
    
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    }
    

    Output beans

    GetTranslationInfoOut


    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : SDDOutputBean
     * - file name : JavaBean.vm
    */
    package net.sf.mp.demo.tranxy.sdd.out.statement;
    
    import java.util.List;
    import java.util.ArrayList;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    /**
     *
     * <p>Title: GetTranslationInfoOut</p>
     *
     * <p>Description: Java Bean containing a collection of GetTranslationInfo </p>
     *
     */
    public class GetTranslationInfoOut {
    
        private String keyName;
        private String translation;
        private String dateFinalization;
        private String code;
        private String description;
        private String firstName;
        private String lastName;
        private String email;
    
        /**
        * Default constructor
        */
        public GetTranslationInfoOut() {
        }
     
        public String getKeyName() {
            return keyName;
        }
     
        public void setKeyName (String keyName) {
            this.keyName =  keyName;
        }
    
        public String getTranslation() {
            return translation;
        }
     
        public void setTranslation (String translation) {
            this.translation =  translation;
        }
    
        public String getDateFinalization() {
            return dateFinalization;
        }
     
        public void setDateFinalization (String dateFinalization) {
            this.dateFinalization =  dateFinalization;
        }
    
        public String getCode() {
            return code;
        }
     
        public void setCode (String code) {
            this.code =  code;
        }
    
        public String getDescription() {
            return description;
        }
     
        public void setDescription (String description) {
            this.description =  description;
        }
    
        public String getFirstName() {
            return firstName;
        }
     
        public void setFirstName (String firstName) {
            this.firstName =  firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
     
        public void setLastName (String lastName) {
            this.lastName =  lastName;
        }
    
        public String getEmail() {
            return email;
        }
     
        public void setEmail (String email) {
            this.email =  email;
        }
    
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    }
    
    

    GetTranslationInfoOutList


    /**
     * Copyright (c) minuteproject, minuteproject@gmail.com
     * All rights reserved.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     * 
     * More information on minuteproject:
     * twitter @minuteproject
     * wiki http://minuteproject.wikispaces.com 
     * blog http://minuteproject.blogspot.net
     * 
    */
    /**
     * template reference : 
     * - name : SDDOutputBeanCollection
     * - file name : JavaBeanCollection.vm
    */
    package net.sf.mp.demo.tranxy.sdd.out.statement;
    
    import java.util.List;
    import java.util.ArrayList;
    
    import javax.xml.bind.annotation.*;
    
    import net.sf.mp.demo.tranxy.sdd.out.statement.GetTranslationInfoOut;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    /**
     *
     * <p>Title: GetTranslationInfoOutList</p>
     *
     * <p>Description: Java Bean GetTranslationInfoOutList </p>
     *
     */
    @XmlRootElement (name="GetTranslationInfoOutList")
    public class GetTranslationInfoOutList {
    
        @XmlElement (name="GetTranslationInfoOut") //(name="gettranslationinfoouts")
        private List<GetTranslationInfoOut> getTranslationInfoOuts;
    
        /**
        * Default constructor
        */
        public GetTranslationInfoOutList() {
        }
     
        public void setGetTranslationInfoOuts (List<GetTranslationInfoOut> getTranslationInfoOuts) {
            this.getTranslationInfoOuts = getTranslationInfoOuts;
        }
    
        @XmlTransient
        public List<GetTranslationInfoOut> getGetTranslationInfoOuts () {
            if (getTranslationInfoOuts==null)
                getTranslationInfoOuts = new ArrayList<GetTranslationInfoOut>();
            return getTranslationInfoOuts;
        }
    
        public void addGetTranslationInfoOut (GetTranslationInfoOut getTranslationInfoOut) {
            getGetTranslationInfoOuts ().add(getTranslationInfoOut);
        }
    
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
    //MP-MANAGED-ADDED-AREA-ENDING @implementation@
    }
    
    

    Improvements

    SDD concept is brand new inside minuteproject, a number of improvement are foreseen:
    • error handling
    • validation (type, mandatory)
    • default value

    Build deploy and test

    Build

    Run >mvn clean package from the root application

    Deploy

    Drop the resulting artefact tranxyRestCxfApp.war in tomcat/webapps
    Start tomcat

    Test

    2 URLs - one for json format the other for xml format are available

    REST URL with parameters returning json format
    http://localhost:8080/tranxyRestCxfApp/rest/json/gettranslationinfos?key=%25&code=%25&max=2
    Search for all key : % and all code : % and max number of return value is 2
    REST URL with parameters returning xml format
    Search for all key : % and all code : % and max number of return value is 1

    Now you can enjoy and make cool front-end (ajax, jquery friendly and other js).

    Conclusion

    If you want to go fast, and when working with ORM abstraction takes to long, then have a look at Statement Driven Development.

    The war between pro Domain Object vs. DTO is irrelevant for Minuteproject.
    On this project both live aside.
    Use the approach that best suits your needs.


    Wednesday, May 16, 2012

    Statement driven development WYSIWYG for REST

    In my previous article, I mentioned that minuteproject will have statement driven development features.
    This page I disclose the potential of SDD while developing REST application.
    Regarding the technologies I take:
    • REST with CXF
    • Spring Bean for DAO
    • JPA2 as persistence layer
    Original WYSIWYG is revisited into What You STATE Is What You Get.

    As fundamentals you need a Statement with is surrounding I/O.
    The model against which the statement is applied is important but secundary.

    As example for SQL, I take 3 queries:
    • select * from address where latitude between ? and ? and longitude between ? and ? and lcase(city) like ?
    • select * from address where addressid between ? and ?
    • select * from address where lcase(city) like ?
    Those queries must be given 
    • a name
    • an input (replace question marks by sample value and provide name)
    • an output (deduce from the metadata recieved after executing the query)
    And basically that's it.

    Saturday, February 25, 2012

    Productivity by example: Liferay with JOOQ

    This article shows how to use jOOQ for the Liferay data model.
    Minuteproject generates jOOQ artifacts for Liferay.
    The main interest of using minuteproject to generate jOOQ artefacts are 
    • Liferay database does not have any foreign key, but minuteproject is able to detect relationship based on patterns.
    •  Some DB naming convention might need to be adapted at Java level
      • DB tables ending with '_' will have underscore stripped
      • Column name ending with '_id' corresponding to PK or FK will have this particule stripped when not coliding with other variable
    • Code is updatable, meaning that you can change part of it or entirely and consecutive generation will keep your alterations
    For more information about the enrichment made on top of Liferay by minuteproject check another post targeting Liferay with JPA2.
    Here the reverse-engineering targets jOOQ framework and the configuration mentions jOOQ track reference

    Configuration
    mp-config-LIFERAY-JOOQ.xml
    <!DOCTYPE root>
    <generator-config>
     <configuration>
      <conventions>
       <target-convention type="enable-updatable-code-feature" />
      </conventions>
      <model name="liferay" version="1.0" package-root="net.sf.mp.demo">
       <data-model>
        <driver name="mysql" version="5.1.16" groupId="mysql"
         artifactId="mysql-connector-java"></driver>
        <dataSource>
         <driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
         <url>jdbc:mysql://127.0.0.1:3306/lportal</url>
         <username>root</username>
         <password>mysql</password>
        </dataSource>
        <schema>lportal</schema>
        <primaryKeyPolicy oneGlobal="true">
         <primaryKeyPolicyPattern name="none"></primaryKeyPolicyPattern>
        </primaryKeyPolicy>
       </data-model>
       <business-model>
        <generation-condition>
         <condition type="exclude" startsWith="QUARTZ"></condition>
        </generation-condition>
        <enrichment>
         <conventions>
          <entity-naming-convention type="apply-strip-table-name-suffix"
           pattern-to-strip="_" />
          <!-- manipulate the structure and entities BEFORE manipulating the 
           entities -->
          <foreign-key-convention
           type="autodetect-foreign-key-based-on-similarity-and-map"
           column-ending="id" column-starting="" />
          <column-naming-convention type="apply-strip-column-name-suffix"
           pattern-to-strip="ID" />
          <reference-naming-convention
           type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
         </conventions>
        </enrichment>
       </business-model>
      </model>
      <targets>
       <target refname="JOOQ" name="JOOQ" fileName="mp-template-config-JOOQ.xml"
        outputdir-root="../../dev/JOOQ/liferay" templatedir-root="../../template/framework/jooq">
        <property name="jooq-version" value="2.0.4"></property>
       </target>
       <target refname="LIB" fileName="mp-template-config-bsla-LIB-features.xml"
        templatedir-root="../../template/framework/bsla">
       </target>
      </targets>
     </configuration>
    </generator-config>
    Execution
    Install Liferay mysql 6.0.6 running
    Download minuteproject
    Drop this config in /mywork/config
    And execute model-generation.(sh/cmd) mp-config-LIFERAY-JOOQ.xml

    Result 
    Artifacts
    The resulting artefacts are
    • A maven project with following dependencies
      • jOOQ
      • jUnit
      •  mysql drive
    • jOOQ artifacts
      • factory, keys, tables, records...
    • unit test
    The artifacts are generated in /dev/JOOQ/liferay
    If you build there will be a compilation error in jOOQ artifacts:
    Table 'expandotable' has a field call 'table' that collides with a Jooq method. 
    But just by altering the code to remove the compilation error and flaging to take into account this modification:
    In ExpandotableRecord 
    • Change getTable into getTable_ 
    • Exclude code from been overwritten: line 24 //MP-MANAGED-UPDATABLE-BEGINNING-ENABLE
     //MP-MANAGED-UPDATABLE-BEGINNING-ENABLE @jooq-record-pk-liferay@
     /**
      * An uncommented item
      * 
      * PRIMARY KEY
      */
        public void setTable(java.lang.Long value) {
            setValue(net.sf.mp.demo.liferay.tables.Expandotable.__EXPANDOTABLE.TABLE, value);
        }
     /**
      * An uncommented item
      * 
      * PRIMARY KEY
      */
        public java.lang.Long getTable_() {
            return getValue(net.sf.mp.demo.liferay.tables.Expandotable.__EXPANDOTABLE.TABLE);
        }
     //MP-MANAGED-UPDATABLE-ENDING
    
    
    Remark: Future version will handle this issue but it is interesting to see how the code can be customized without losing the productivity of consecutive generations.

    Eventually run: mvn clean package
    • A set of unittest are executed:
      • The default unit test consist to retrieve the first row of each entity.
    • A JOOQ package is released
    Code
    The code can be downloaded from minuteproject googlecode here.

    Sample
    Active record:
    Providing the Foreign key gives the ability to provide jOOQ records with some methods
    One to many
    //todo
    Testing

    /**
     * This class is generated by minuteproject 4 jOOQ
     */
    package net.sf.mp.demo.liferay.tables;
    
    import net.sf.mp.demo.liferay.tables.records.UserRecord;
    import net.sf.mp.demo.liferay.Liferay;
    import net.sf.mp.demo.liferay.Keys;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    import net.sf.mp.demo.liferay.LiferayFactory;
    import static net.sf.mp.demo.liferay.tables.User.__USER;
    
    import org.jooq.Record;
    import org.jooq.Result;
    import org.junit.Test;
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @import@
    import static net.sf.mp.demo.liferay.tables.Company.__COMPANY;
    import static net.sf.mp.demo.liferay.tables.Account.__ACCOUNT;
    //MP-MANAGED-ADDED-AREA-ENDING @import@
    
    //MP-MANAGED-ADDED-AREA-BEGINNING @class-annotation@
    //MP-MANAGED-ADDED-AREA-ENDING @class-annotation@
    @javax.annotation.Generated(value = { "http://www.jooq.org", "2.0.4" }, comments = "This class is generated by minuteproject 4 jOOQ")
    public class TestUser {
    
     @Test
     public void testUser() {
      Connection conn = null;
      String userName = "root";
      String password = "mysql";
      String url = "jdbc:mysql://127.0.0.1:3306/lportal";
    
      try {
       Class.forName("org.gjt.mm.mysql.Driver").newInstance();
       conn = DriverManager.getConnection(url, userName, password);
       LiferayFactory create = new LiferayFactory(conn);
    
       // MP-MANAGED-UPDATABLE-BEGINNING-ENABLE
       // @jooq-unittest-testUser-liferay@
       // write your own tests, just set DISABLE to ENABLE in the comment
       // above
       // future generation will not erase your code ;)
       Result<Record> result = create
         .select(__USER.FIRSTNAME, __USER.LASTNAME, __COMPANY.WEB, __ACCOUNT.NAME)
         .from(__USER).join(__COMPANY)
         .on(__USER.COMPANY.equal(__COMPANY.COMPANY))
         .join(__ACCOUNT)
         .on(__COMPANY.ACCOUNT.equal(__ACCOUNT.ACCOUNT))
         .where(__COMPANY.WEB.like("%ray.com"))
         .orderBy(__USER.LASTNAME.asc().nullsFirst()).limit(10)
         .fetch();
       for (Record r : result) {
        String firstname = r.getValue(__USER.FIRSTNAME);
        String lastname = r.getValue(__USER.LASTNAME);
        String web = r.getValue(__COMPANY.WEB);
        String accountname = r.getValue(__ACCOUNT.NAME);
    
        System.out.println(" firstname : " + firstname + " lastname : "
          + lastname + " web : " + web + " accountname : "
          + accountname);
       }
       // MP-MANAGED-UPDATABLE-ENDING
    
      } catch (Exception e) {
       e.printStackTrace();
      } finally {
       if (conn != null) {
        try {
         conn.close();
        } catch (SQLException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
        }
       }
      }
     }
    
     // MP-MANAGED-ADDED-AREA-BEGINNING @implementation@
     // MP-MANAGED-ADDED-AREA-ENDING @implementation@
    
    }
    
    Giving at execution
     firstname :  lastname :  web : liferay.com accountname : Liferay
     firstname : Joe lastname : Bloggs web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 1 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 10 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 2 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 3 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 4 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 5 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 6 web : liferay.com accountname : Liferay
     firstname : Test lastname : DLC 7 web : liferay.com accountname : Liferay
    
    JOOQ quick review
    //todo
    • Typesafe query speeds up the development process
    • No ORM 
    • Focus on CRUD and complexe query 
    • No configuration loading meaning fast to test