User's Guide

Contents:

Prerequisites

This guide builds on the information already covered in the Quick Start and Basic Configuration pages.

Foundation Code

Much of RevGen's capabilities comes from the underlying JavaGen Agile framework. JavaGen Agile defines the model architectures, naming pattern mechanisms and code generation framework. To understand the underlying classes please see the JavaGen Agile User's Guide.

RevGen itself is just a collection of templates, a configuration file and a few classes specific to converting database schemas to Java. This guide focuses on RevGen-specific configuration.

Configuration Files

RevGen configuration is largely handled by two files:

revgen.properties
Overrides the default properties. Use revgen.properties for simple configuration.
RevGenSpringConfig.xml
Configures every detail of RevGen including default properties. Use RevGenSpringConfig.xml for more advanced configuration such as adding templates or altering the code generation pipeline.

Both files are located in src/revgen and depending on your needs you can you use any combination of the two. By default revgen.properties is loaded from the src/revgen folder and RevGenSpringConfig.xml is loaded from the classpath (i.e. the RevGen jar file).

The configuration to load both files from the src/revgen folder looks like this in Maven 2:

<plugin>
        <groupId>org.javagen.revgen</groupId>
        <artifactId>maven-revgen-plugin</artifactId>
        <version>1.0</version>
        <executions>
                <execution>
                        <phase>generate-sources</phase>
                        <goals><goal>revgen</goal></goals>
                        <configuration>
                                <propertyFile>file:src/revgen/revgen.properties</propertyFile>
                                <configFile>file:src/revgen/RevGenSpringConfig.xml</configFile>
                        </configuration>
                </execution>
        </executions>
</plugin>

And this in Ant:

<target name="revgen" depends="" unless="skipgen" description="RevGen code generator">
    <java classname="org.javagen.light.revgen.SpringMain" fork="true" failonerror="yes" classpathref="lib.path">
        <arg value="file:src/revgen/RevGenSpringConfig.xml"/>
        <arg value="file:src/revgen/revgen.properties"/>
    </java>
</target>

RevGenSpringConfig.xml

Any type of configuration can be achieved using src/revgen/RevGenSpringConfig.xml including:

  • Pipeline components and order of execution
  • Template names, locations and types.
  • Class, package and variable naming patterns.
  • Per-template overwrite predicates.
  • Binding templates to model instances.
  • Root structure of model tree including global properties and parent containers for generated model instances.
  • Reading, writing and merging of metadata.
  • Database table selection.
  • Code naming and conversion implementations

Much of the configuration in this document is done using this file. Becoming familiar with its contents will give you complete control over RevGen.

Database Setup

Database properties are set using:

dbDriverClassName = org.hsqldb.jdbcDriver
dbUsername = sa
dbPassword = 
dbUrl = jdbc:hsqldb:hsql://localhost/contact
# this is just needed by Hibernate at runtime:
hibernateDialect = org.hibernate.dialect.HSQLDialect

You can also substitute RevGen's DriverManagerDataSource for your own DataSource. For example this works:

<bean id="dataSource" class="org.hsqldb.jdbc.jdbcDataSource">
        <property name="database" value="${dbUrl}"/>
        <property name="user" value="${dbUsername}"/>
        <property name="password" value="${dbPassword}"/>
</bean>

Tables to process can can be limited using:

includeTables = ADRRESS, CITY, STATE, COUNTRY
excludeTables = INVENTORY_STAGING, SALES_STAGING

If your tables have plural names, you should set pluralTableNames to true.

If the names are not converted to singular nouns correctly, you can list them explicitly using the pluralisation bean:

<bean name="pluralisation" class="org.javagen.agile.oo.naming.RegularPlurals"/>
        <property name="singularToPlural">
                <map>
                        <entry key="Child" value="Children""/>
                </map>
        </property>
</bean>

Finally table names will be converted to uppercase if tableNamesToUpperCase is true.

Code Generator Options

The code generator can be configured to generate several types of output. Here are properties and their settings:

# Valid DAO implementation types (daoImplType): hibernate, spring, jpa
daoImplType = jpa
# annotations: true, false
annotations = true
# junitVersion: 3, 4
junitVersion = 4
# emit Hibernate validation annotations: true, false
hibernateValidation = true
# overrides one-time generation checks (overwriteAll): true, false
overwriteAll = true

Most of these properties are self documenting. If you set annotations to false, you will generate XML database mapping files.

annotations = false

Round Tripping

Round Tripping refers to the ability to freely mix hand written and generated code throughout the development process. Although RevGen does not currently support, merging both within a single file, several alternatives techniques are supported that achieve the same goals. These techniques can be combined as needed to achieve the desired level of round trip configuration.

One Time Generation

Many code generation artifacts are only emitted if they don't already exist. You can safely edit these files and they won't be overwritten (see Conditional Template Emission).

Extension-By-Inheritance

By default RevGen generates a single model class for each entity, but it can be configured to generate two classes:

extensionByInheritance = true
genEntityNameTemplate = #{modelName}Gen

This will result in a empty class being emitted that looks something like this:

public class Person extends PersonGen { ... }

Person can be extended with hand-written code without interfering with the generated class, PersonGen.

Dual Source Directories

The RevGen plugin can be configured to separate generated from hand written code, for example:

<plugin>
        <groupId>org.javagen.revgen</groupId>
        <artifactId>maven-revgen-plugin</artifactId>
        <configuration>
                <propertyFile>file:src/revgen/revgen.properties</propertyFile>
                <sourceDirectory>${pom.build.directory}/generated-sources/revgen-main</sourceDirectory>
                <testSourceDirectory>${pom.build.directory}/generated-sources/revgen-test</testSourceDirectory>
                <resourceDirectory>${project.build.directory}/generated-sources/revgen-resources</resourceDirectory>
        </configuration>
</plugin>

As another option, the RevGen plugin can remove duplicate files, allowing any generated file to be modified simply by copying it from the generated directory to the hand-coded directory. Just set removeDuplicates to true:

<plugin>
        <groupId>org.javagen.revgen</groupId>
        <artifactId>maven-revgen-plugin</artifactId>
        <configuration>
                <propertyFile>file:src/revgen/revgen.properties</propertyFile>
                <sourceDirectory>${pom.build.directory}/generated-sources/revgen</sourceDirectory>
                <testSourceDirectory>${pom.build.directory}/generated-sources/revgen-test</testSourceDirectory>
                <removeDuplicates>true</removeDuplicates>
        </configuration>
</plugin>

Property Propagation & Contexts

Default properties are defined on the RevGenPropertyPlaceholderConfigurer bean within the RevGenSpringConfig.xml file and can be overridden by using the revgen.properties file.

The resulting properties are injected into the root model (defaultsModel) which makes them available to the entire model tree as well as the templates.

Contexts (i.e. maps of key-value pairs) can be attached to model instances and even template emitters. Because models are arranged into a tree structure, global attributes can be shared from the root node or overridden by local nodes. The context visible at template invocation is the combination of the current context (leaf) down through the parent chain to the root with child values taking precedence over parent values. Thus by using grouping strategies, you can control the processing environment of whole collections of nodes.

Pipeline Usage

The overall behavior of RevGen is controlled by the GeneratorPipeline which implements a code generation pipeline or assembly line. Having a pipelined architecture makes it easy to customize code generation behavior by adding and removing task-specific modules called Generators.

To give you a sense of how to manipulate the pipeline to suite your needs, here are a few examples using predefined components in the RevGenSpringConfig.xml file:

  • Minimal (and fast) configuration needed for database to Java code generation
    <bean id="pipeline" class="org.javagen.agile.core.GeneratorPipeline">
            <property name="pipeline">
                    <list>
                            <ref bean="dbLoader"/>
                            <ref bean="db2ooPass1"/>
                            <ref bean="db2ooPass2"/>
                            <bean class="org.javagen.revgen.visitor.PostProcessVisitor"/>
                            <bean class="org.javagen.agile.core.visitor.DefaultValueVisitor"/>
                            <ref bean="emitterVisitor"/>
                    </list>
            </property>
    </bean>
  • Load the database schema and write the model as XML
    <bean id="pipeline" class="org.javagen.agile.core.GeneratorPipeline">
            <property name="pipeline">
                    <list>
                            <ref bean="dbLoader"/>
                            <bean class="org.javagen.revgen.visitor.IdAssignerDb2OO"/>
                            <bean class="org.javagen.agile.core.visitor.IndexBuilderVisitor"/>
                            <ref bean="writeMetadata"/>
                    </list>
            </property>
    </bean>
  • Load the database as an XML file and generate Java code
    <bean id="pipeline" class="org.javagen.agile.core.GeneratorPipeline">
            <property name="pipeline">
                    <list>
                            <bean class="org.javagen.revgen.core.xml.JAXBXmlReader">
                                    <property name="xmlFile" value="src/revgen/database-model.xml"/>
                            </bean>
                            <ref bean="db2ooPass1"/>
                            <ref bean="db2ooPass2"/>
                            <bean class="org.javagen.revgen.visitor.PostProcessVisitor"/>
                            <bean class="org.javagen.agile.core.visitor.DefaultValueVisitor"/>
                            <ref bean="emitterVisitor"/>
                    </list>
            </property>
    </bean>
  • Merge class model with XML file and generate Java code
    <bean id="pipeline" class="org.javagen.agile.core.GeneratorPipeline">
            <property name="pipeline">
                    <list>
                            <ref bean="dbLoader"/>
                            <ref bean="db2ooPass1"/>
                            <bean class="org.javagen.revgen.visitor.IdAssignerDb2OO"/>
                            <bean class="org.javagen.agile.core.visitor.IndexBuilderVisitor"/>
                            <ref bean="readAndMergeClassMetadata"/>
                            <ref bean="db2ooPass2"/>
                            <bean class="org.javagen.revgen.visitor.PostProcessVisitor"/>
                            <bean class="org.javagen.agile.core.visitor.DefaultValueVisitor"/>
                            <ref bean="emitterVisitor"/>
                    </list>
            </property>
    </bean>

A few things to note are:

  • The IdAssigner and IndexBuilder visitors must be run anytime new models are added before serializing or merging models.
  • The database-to-class transformation is done in three phases to allow multiple customization points.
  • The db2ooPass1 creates tables and columns and db2ooPass2 creates foreign key constraints.
  • The DefaultValueVisitor assigns defaults to all remaining null values in the model, simplifying template coding.

Association Tables

Most persistence frameworks can transparently manage association tables, completely hiding the foreign key management from the Java developer. The trade-off is that the Java developer can't access the values in this association table. In an association table that only consists of two foreign keys, this is not normally an issue, but if extra columns exist (like Timestamps, etc.) then these values are not visible.

By default RevGen is configured to identify many-to-many association tables and hide them in the mapping details:

<bean class="org.javagen.agile.db.visitor.LinkTableFinderVisitor">
        <property name="relativeInputPath" value="DATABASE"/>
</bean>

To map association tables as entities, simply comment out the LinkTableFinderVisitor from the pipeline.

To individually assign association tables, set linkTable to true in the merge-metadata.xml file:

<table linkTable="true" id="db:PERSONS_BANKS"/>

and configure an instance of IdOverrideVisitor to run before the database-to-Java transformation in the pipeline.

Adding Templates

To modify or add templates, the first step is to copy the default templates from the RevGen jar file into a local directory. There is an ant task, getTemplates, to automate this task.

Then configure RevGen to use the local copies:

templateBasePath = file:src/revgen/freemarker/java

Both Freemarker FreemarkerCodeGenerator and Velocity VelocityCodeGenerator template engines are currently supported.

Individual templates are configured using a TemplateEmitter:

<bean id="pk" class="org.javagen.agile.core.emitter.TemplateEmitter">
        <property name="sourceDirectoryProperty" value="#{srcDirectory}"/>
        <property name="relativeFilePathProperty" value="#{entityPackageNameTemplate}/#{modelName}.java"/>
        <property name="templatePath" value="orm/PK.java.ftl"/>
        <property name="templateGenerator" ref="freemarkerGenerator"/>
</bean>

In addition, they must be bound to specific model types. In this case, to any model instance with a "pk" modelType:

<bean id="binder" class="org.javagen.agile.core.emitter.binding.ModelTypeBinder">
        <property name="emitterBindings">
                <map>
                        <entry key="pk">
                                <set>
                                        <ref bean="pk"/>
                                </set>
                        </entry>
                </map>
        </property>
</bean>

Conditional Template Emission

Generated artifact emission can be made conditional. The fileDoesNotExist condition is defined to only allow one-time generation of artifacts. It can be added to a TemplateEmitter with the following line:

<property name="emitCondition" ref="fileDoesNotExist"/>

You can also trigger emission to property settings. For example:

<property name="emitCondition">
        <bean class="org.javagen.agile.core.emitter.logic.Equals">
                <constructor-arg value="${mappingType}"/>
                <constructor-arg value="hbm"/>
        </bean>
</property>