Browse the JavaDoc for all the IJGenerator classes here.
2.1.1 Customizing names of generated artifacts
2.1.2 Relation with other objects
2.1.3 Many To Many relationships
3. TemplateGenerator abstract class
3.1 MultipleDefinitionsTemplateGenerator
3.2 SingleDefinitionTemplateGenerator
3.4 Writing custom TemplateGenerator
5.1. Via command line as stand-alone application
IJGenerator (stands for Intelligent Java Generator) is simple and in the same time powerful code generator written in Java programming language. Its main advantages are that it is independent from various modeling tools (reads model data directly from simple POJO annotated models or from plain XML); it uses famous Apache Velocity templates to generate code.
Generator work-flow is simple:
- Definition of models
- Definition of templates for code to be generated
- Choose from set of already implemented TemplateGenerators or create new one
- Run the IJGenerator
Model definitions are the source used by IJGenerator to generate artifacts. Model definition contains complete description of object. For example - package information, in which database table will be persisted, various information about fields: validation rules, relation with other objects, etc....
Let's consider the following example - an Item in store.
Each item has few properties - number, name, price.
If we have to define a model for this object item we must describe:
- the object itself: its package name, DB table, etc...
- object's fields - number, name, price: validation rules, DB column mappings, etc..
We can do this in 2 ways - with annotated POJO classes or with plain XML:
If we stick to the example with 'Item' above our model will look like this using annotated POJO classes approach:
package ipman.model.domain;
import ijgen.generator.annotations.Constraints;
import ijgen.generator.annotations.DBTable;
import ijgen.generator.annotations.PackageDesc;
import ijgen.generator.annotations.Persisted;
import ijgen.generator.annotations.Primary;
import ijgen.generator.annotations.UserExposed;
@PackageDesc(name="my.package")
@DBTable(name="ITEM")
public class Item {
@Primary
@Persisted
@Constraints(required=true)
@UserExposed
long id;
@Persisted(columnName = "ITEM_NAME")
@UserExposed
@Constraints(required=true, maxLength=100)
String name;
@Persisted
@Constraints(required=true)
@UserExposed
long price;
}
Simple, isn't it:) Now let's look in details in the code listed above.
As you may already noticed it is a standard Java bean consisting of three fields
and a lot of annotations.
Fields of this class are properties of the object that we want to describe in the model.
Annotations are responsible to instruct the generator how it should process these fields.
Let's go thorough annotations one by one:
@PackageDesc(name="my.package") - Instructs the generator that artifacts produced for this model have to be in package "my.package"
@DBTable(name="ITEM") - Instructs the generator that domain object described in this model have to be persisted in DB table "ITEM"
@Primary - Instructs the generator that this field will be primary key (id) for this object. Note that only one field per object can be marked as primary.
@Persisted(columnName = "ITEM_NAME") - Instructs the generator that this field have to be persisted and have to be stored in column ITEM_NAME. If columnName attribute is not specified then generator automatically generates its value for you. In the current case this value would be "NAME".
@Constraints(required=true, maxLength=100) - Instructs the generator about the validation rules that have to be generated for this field. This information is also useful for generation of DB create scripts to determine whether a column is nullable or not.
@UserExposed - Indicates that this field have to be visible for the user, and not to be present only in the domain object. A good example is DTO (Data Transfer Objects) - if a field is marked with this annotation then it will be exposed in the DTO objects and will be visible for the end user.
There is also a possibility to exclude some templates not to be processed for this model. This can be easily achieved by using the following annotation:
...
@GeneratedLayers(skipLayers="service")
public class Item {
....
@GeneratedLayers(skipLayers="service") - Instructs the generator that template with name "service" should not be processed for this model.
You can also specify how generated artifacts will be named. You can control this for each template and specify a suffix that will be appended after name of the model.
...
@GeneratedLayers(layersFileNameSuffixes="domain=,service=ServiceNameTest")
public class Item {
....
@GeneratedLayers(layersFileNameSuffixes="domain=,service=ServiceNameTest")
- Instructs the generator the following 2 things:
1. artifact produced by executing generator for the model on template "domain" should have no extension to the name. So generated artifact will have name "Item".
2. artifact produced by executing generator for the model on template "service" should have extension "ServiceNameTest" to the name. So generated artifact will have name "ItemServiceNameTest".
In a real world single objects without any relations are not applicable. Let's consider our example with item. Items usually are located in stores. So we will create a model for object Store and describe the relation between Item and Store.
...
public class Store {
@Primary
@Persisted
@Constraints(required=true)
@UserExposed
long id;
@UserExposed
@Relation(relationType = RelationType.ONE_TO_MANY, joinColumn="store_id")
List
}
Definition of Store model is simple - it has 2 fields - id and list of items. List of Item objects represents the relation between these 2 objects. Relation is described with annotation: @Relation(relationType = RelationType.ONE_TO_MANY, joinColumn="store_id") - Instructs the generator that this field is a relation with other table. In the concrete example relation is described as "One to Many" (One store contains Many items). Also a join column is specified - this is the column in Item that holds the relation to Store (to which store this item belongs).
Now we have to modify our Item model and to add this field:
.....
public class Item {
.....
@Persisted
@Constraints(required=true)
@UserExposed
long storeId;
.....
Now we have defined this field that will play as a foreign key. Note that we have not specified columnName explicitly in @Persisted annotation. In this case IJGenerator will automatically create name "store_id"
Imagine the case when one item could be present in more than one store. Then we have the relation called "Many to Many". Using IJGenerator annotations this can be expressed in the following way in the Store model:
.....
class Store {
.....
@Relation(relationType = RelationType.MANY_TO_MANY)
List
.....
Note that here we do not specify a join column. That's why Many To Many relations are
implemented with usage of linking table. In our case this would be something like this: STORE_ITEMS.
Table will have 2 columns: STORE_ID and ITEM_ID. You do not have to trouble about anything:
IJGenerator will automatically generate create scripts for this table and also all
the infrastructure code for reading/writing from/into it.
Of course a change must be done in Item model too. Because we are using linking table -
we don't need anymore field for foreign key. We have to delete the field storeId that we have
defined few minutes ago.
Coming soon...
TemplateGenerator is an abstract class and its implementations are the classes used by IJGenerator to initialize and prepare Velocity Context, bind model definitions to it, etc...
When you start IJGenerator you must pass a TemplateGenerator implementation as an argument.
TemplateGenerator uses Apache Velocity engine to process templates. There are few objects
that are bound to VelocityContext and can be used directly in the templates. This is the way how
model information is accessible in templates. Description of bound objects is described few paragraphs below separately for each TemplateGenerator.
TemplateGenerator contains one final method "generate()" that invokes set of templates methods
implemented in TemplateGenerator successors. This is the mechanism which allows definition of unique and flexible
TemplateGenerator that will fulfill various needs depending on the current case.
There are four abstract methods in TemplateGenerator that has to be implemented:
protected abstract void doGenerate(Map
List
throws Exception;
protected abstract String getFileFilter();
protected abstract String getCustomLine();
protected abstract String getGeneratedExtension();
doGenerate(.....) - The real generation happens here. Method takes as an arguments: list of templates; list of model definitions; settings passed to generator.
getFileFilter() -
Specifies extension for template files that have to be processed by this TemplateGenerator implementation.
Example: "jv" - only templates with extension "jv" will be processed; dao.vm.jv
getCustomLine() - Specifies the line indicating that a file is already customized and should not be overriden by the generator.
getGeneratedExtension() - Specifies the extension that generated artifacts will have. Example "java" will lead to ItemDomain.java.
Complete description of these four methods you can find in the JavaDoc documentation.
For most of the cases you won't write your own implementation of TemplateGenerator.
Instead you can use three predefined TemplateGenerator implementations:
MultipleDefinitionsTemplateGenerator is an implementation of TemplateGenerator
that processes all models and templates and for each model executes all the templates.
So if we have models Item and Store and templates domain, dto and service - we'll get the following artifacts produced by the generator:
ItemDomain, ItemDto, ItemService
StoreDomain, StoreDto, StoreService
A typical application of this implementation is generation of all the layers for all the domain objects in Enterprise Java applications.
Bound variables:
- GeneratorConstants.CLASS_DEFINITION_KEY: current model definition processed by the generator.
Instance of ClassDefinition class.
- GeneratorConstants.TEMPLATE_UTILS_KEY: Utility class containing various useful methods.
Instance of TemplateUtils class.
- GeneratorConstants.GLOBAL_SETTINGS_KEY: Instance of GlobalSettings class.
Contains settings passed to generator when started.
- GeneratorConstants.DB_FIELD_TYPE_MAPPER_KEY: Implementation of JavaToDBMapping interface.
Concrete implementation depends on the database type passed to generator as an argument.
For more information please look at the section - "Running Generator".
SingleDefinitionTemplateGenerator is an implementation of TemplateGenerator
that processes all models and templates and executes all the templates one by one for all the models at once.
So if we have models Item and Store and templates domain, dto and service - we'll get the following artifacts produced by the generator:
Domain
Dto
Sevice
A typical application of this implementation is generation of Spring contexts and various configuration files in which one file contains configuration for more than one object.
Bound variables:
- GeneratorConstants.CLASS_DEFINITION_KEY: current model definition processed by the generator.
Instance of ClassDefinition class.
- GeneratorConstants.TEMPLATE_UTILS_KEY: Utility class containing various useful methods.
Instance of TemplateUtils class.
- GeneratorConstants.GLOBAL_SETTINGS_KEY: Instance of GlobalSettings class.
Contains settings passed to generator when started.
- GeneratorConstants.DB_FIELD_TYPE_MAPPER_KEY: Implementation of JavaToDBMapping interface.
Concrete implementation depends on the database type passed to generator as an argument.
For more information please look at the section - "Running Generator".
DbCreateTemplateGenerator is an implementation of TemplateGenerator
that is responsible for generation of database create script for all the models that are defined.
Bound variables:
- GeneratorConstants.CLASS_DEFINITION_KEY: current model definition processed by the generator.
Instance of ClassDefinition class.
- GeneratorConstants.TEMPLATE_UTILS_KEY: Utility class containing various useful methods.
Instance of TemplateUtils class.
- GeneratorConstants.GLOBAL_SETTINGS_KEY: Instance of GlobalSettings class.
Contains settings passed to generator when started.
- GeneratorConstants.DB_FIELD_TYPE_MAPPER_KEY: Implementation of JavaToDBMapping interface.
Concrete implementation depends on the database type passed to generator as an argument.
For more information please look at the section - "Running Generator".
- GeneratorConstants.LINK_TABLES_KEY: Collection of LinkTableInfo objects.
Each of the objects represents information for link table used for Many To Many relationships.
There might be a case when these three TemplateGenerator implementations described in sections 3.1 - 3.3 are not enough and you are in some special case. Then you can write your own TemplateGenerator. All you have to do is to implement the four abstract methods that are described at the beginning of sections 3.
IJGenerator is based on Apache Velocity templates and engine.
Writing templates is simple and easy to understand. They are very similar to JSP.
Below is listed sample code for template that generates DTO (Data Transfer Object):
package ${classDefinition.packageName}.dto;
import java.util.*;
import ${classDefinition.packageName}.domain.*;
public class ${classDefinition.className}Dto {
#foreach($field in $classDefinition.fieldDefinitions)
#if($field.isUserExposed())
private $templateUtils.getClassWithoutPackage($field.fieldType) $field.fieldName;
#end
#end
#foreach($field in $classDefinition.fieldDefinitions)
#if($field.isUserExposed())
#set($fieldName = $field.getFieldName())
public $templateUtils.getClassWithoutPackage($field.fieldType)
$templateUtils.getCamelCaseGetterName(${fieldName})() {
return this.${fieldName};
}
public void $templateUtils.getCamelCaseSetterName(${fieldName})
($templateUtils.getClassWithoutPackage($field.fieldType) ${fieldName}) {
this.${fieldName} = ${fieldName};
}
#end
#end
}
Using model Item and MultipleDefinitionsTemplateGenerator this template will produce code like this:
public class Item {
private long id;
private String name;
private long price;
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public long getPrice() {
return this.price;
}
public void setPricelong price() {
this.price = price;
}
}
For more details about writing Apache Velocity templates you can read this article.
IJGenerator can be started in 2 ways: via command line as a stand-alone application or as an ANT task.
Starting is simple:
java -cp ijgenerator.jar:velocity-1.6.2-dep.jar:models ijgen.generator.app.IJGen
-mode java
-models models
-templates templates
-generator "class:ijgen.generator.engine.generators.MultipleDefinitionsTemplateGenerator;
filter:jv;extension:java;custom://custom;output:out"
-db "user:usr1;pass:pass1;driver:my.com.Drv;url:someUrl;type:mysql"
Note that in order to be started IJGenerator you have to pass in the -cp argument the correct path to IJGenerator and velocity jars, also to models directory (only in case if you are using annotated POJOs approach).
Description of options:
-mode: mode in which the generator will run. Possible values are "java" or "xml".
If you specify "java" generator will use annotated POJO classes for models.
If you put "xml" there then XML models will be loaded.
-models: Path to the directory where the model files are stored.
Note: If you are using annotated POJO's you have to compile them before running the generator.
This is why IJGenerator uses reflection to read the annotations.
-templates: Path to the directory where the template files are stored.
-generator: Specifies TemplateGenerator that have to be executed.
Each generator must be initialized with options. Options are listed in list having the following format:
"option1:value1;option2:value2;......optionN:valueN". Note that options list have to be surrounded by " " symbols.
Below is a list of possible options:
class: Specifies the implementation on TemplateGenerator that will be used by IJGenerator to produce artifacts
Example: class:ijgen.generator.engine.generators.MultipleDefinitionsTemplateGenerator;
filter: Specifies the extension of the template files that have to be processed by IJGenerator.
Example: filter:jv Means that all templates which name ends with .jv will be processed
extension: Specifies the extension that will be appended to the artifacts generated by IJGenerator.
Example: extension:java Means that generated artifacts will have extension .java
custom: Specifies the string that instructs the generator that a file is already customized and should not be overwritten by IJGenerator.
Example: custom://custom Means that files having first line that is equal to "//custom" will not be overwritten.
output: Specifies the output directory where IJGenerator will store the generated artifacts.
Example: out:/home/myacc/gen Means that generated code will go to: /home/myacc/gen directory.
Note: There could be more than one generator passed as an argument.
Just you have to add another -generator "option1:value1....." fragment to the parameters list.
-db: Specifies settings for the database - username, url, driver, etc.
This information is needed in the case when DB sql scripts will be created or any other infrastructure code used for database manipulation.
Possible options are:
driver: JDBC driver used to connect to database.
url: URL of the database.
user: Username with which the generated code/scripts will connect to the database.
pass: Password with which the generated code/scripts will connect to the database.
type: Type of the database: mysql, oracle, etc.
The other way to start IJGenerator is to integrate it in a ANT build script. The first step is to define ant task for IJGenerator:
<taskdef name="gen" classname="ijgen.generator.ant.GeneratorTask">
<classpath>
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
</taskdef>
"lib" folder is the one that you will find when you extract the contents of finary release of IJGenerator.
Not it is time to write the generate target that will do the real generation:
<target name="generate">
<gen sourceMode="xml">
<path
modelsDirectory="models"
templatesDirectory="templates"
/>
<database
driver = "${sql.driver}"
url = "${sql.url}${sql.user}"
username = "${sql.user}"
password = "${sql.pass}"
databaseType = "mysql"
/<
<generatorType
className="ijgen.generator.engine.generators.MultipleDefinitionsTemplateGenerator"
templateFileFilter = "jv"
generatedExtension = "java"
customLine = "//custom"
outputDirectory = "out"
/>
</gen>
</target>
"Gen" task accepts 3 possible elements as parameters:
path: Specifies path to the directories for model and template files.
Possible attributes of this element are:
modelsDirectory: Path to the directory where the model files are stored.
Note: If you are using annotated POJO's you have to compile them before running the generator.
This is why IJGenerator uses reflection to read the annotations.
templatesDirectory: Path to the directory where the template files are stored.
database: Specifies settings for the database - username, url, driver, etc.
This information is needed in the case when DB sql scripts will be created or any other infrastructure code used for database manipulation.
Possible attributes of this element are:
driver: JDBC driver used to connect to database.
url: URL of the database.
username: Username with which the generated code/scripts will connect to the database.
password: Password with which the generated code/scripts will connect to the database.
databaseType: Type of the database: mysql, oracle, etc.
database: Specifies settings for the database - username, url, driver, etc.
This information is needed in the case when DB sql scripts will be created or any other infrastructure code used for database manipulation.
Possible attributes of this element are:
driver: JDBC driver used to connect to database.
url: URL of the database.
username: Username with which the generated code/scripts will connect to the database.
password: Password with which the generated code/scripts will connect to the database.
databaseType: Type of the database: mysql, oracle, etc.
-generatorType: Specifies TemplateGenerator that have to be executed.
Below is a list of possible attributes:
className: Specifies the implementation on TemplateGenerator that will be used by IJGenerator to produce artifacts
Example: className="ijgen.generator.engine.generators.MultipleDefinitionsTemplateGenerator"
templateFileFilter: Specifies the extension of the template files that have to be processed by IJGenerator.
Example: templateFileFilter="jv" Means that all templates which name ends with .jv will be processed
generatedExtension: Specifies the extension that will be appended to the artifacts generated by IJGenerator.
Example: generatedExtension="java" Means that generated artifacts will have extension .java
customLine: Specifies the string that instructs the generator that a file is already customized and should not be overwritten by IJGenerator.
Example: customLine="//custom" Means that files having first line that is equal to "//custom" will not be overwritten.
outputDirectory: Specifies the output directory where IJGenerator will store the generated artifacts.
Example: outputDirectory="/home/myacc/gen" Means that generated code will go to: /home/myacc/gen directory.
Note: There could be more than one generatorType element inside the Gen target.
This chapter will contain some detailed information abou specific issues and cases.
It will also contain a lot of samples for various scenarios.