Here's JPAEntity.vm, with a few extra niceties that you can remove if you like:
- our Torque schema uses a table-level Torque-style option called "data-type" to indicate the type of data contained therein, with "system" data being read-only,
- our tables don't have a version number field, so the template suppresses it,
- our columns use the Torque "javaName" attribute to store a more developer-friendly name for certain columns (the actual Torque ORM tool uses it for something totally different); you could use a Torque "option" tag for this instead,
- our CHAR(1) columns have a Torque-style "flag" option that indicates whether they store a Y/N boolean or a genuine CHAR(1) value, which affects whether the associated Java field is a boolean/Boolean or a char/Character,
- the template adds JSR-303 (Bean Validation) annotations to all String fields based on their length and nullability in the database.
Code:
## This Velocity template generates the JPA entity class.
## The Roo annotations below will ensure that getters, setters, CRUD methods,
## etc. will be generated and maintained by Roo when it is run.
##
## All database objects like $table and $column are from the Torque package
## org.apache.torque.engine.database.model
##
## A macro that lowercases the first letter of the given string
#macro (lowercaseFirst $input)
#set ($firstLetter = $input.substring(0, 1).toLowerCase())
#set ($rest = $input.substring(1))
$firstLetter$rest#end
package ${package};
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;
/**
* $!table.description.
*
* This class contains a combination of (a) code generated from the database
* schema XML and (b) hand-written
* business logic. If you regenerate this class from the schema (e.g. after
* modifying that file), make sure you preserve the hand-written business logic.
* By the same token, if you are adding business logic to a JPA class, make sure
* you write a unit test for it, no matter how trivial, to guard against the
* kind of accidental deletion described above. This approach ensures that the
* schema XML remains the authoritative definition of the database schema while
* still allowing us to have a rich domain model.
*/
@Entity
#if ("$!{table.getOption('data-type')}" == "system")
#set ($createSetters = "(settersByDefault = false)")
#set ($readOnly = ',
flushMethod = "", mergeMethod = "", persistMethod = "", removeMethod = ""')
#end
## Get this table's ID column, if any (not all tables have one)
#if ($table.getPrimaryKey().size() == 1)
@RooEntity(identifierColumn = "${table.getPrimaryKey().get(0).getName()}", versionField = ""$!readOnly)
#else
@RooEntity(versionField = "")
#end
## Disable setter methods for system data tables
@RooJavaBean$!createSetters
@RooToString
@Table(name = "${table.getName()}")
public class ${table.getName()} {
#foreach ($column in $table.Columns)
#if (!$column.isPrimaryKey() && !$column.getDescription().startsWith("Not used"))
## ------- Add a blank line plus any @NotNull and @Size annotations ----
#if ($column.description)
// $!column.description
#end
#if ($column.getJavaNative() == "String")
#set ($length = ", length = $column.getSize()")
#end
@Column(name = "$column.getName()"$!length)
## Generate the field name by lowercasing the first letter of the column
#set ($fieldName = "#lowercaseFirst($column.getName())")
#if (!$column.getJavaName().equalsIgnoreCase($column.getName()))
## This column has a more commonly used name
#set ($fieldName = "#lowercaseFirst($column.getJavaName())")
#end
#if ($column.isForeignKey())
## ------- FK column -----------------------------------------------
#if ("$!column.getForeignKey().getOption('multiplicity')" == "one-to-one")
@OneToOne
#else
@ManyToOne
#end
#set ($lastIndex = $fieldName.length() - 2)
private $column.getForeignKey().getForeignTableName() $fieldName.substring(0, $lastIndex);
#else
## ------- Normal (non-ID, non-FK) column --------------------------
## ------- Work out which Java type to use for this field
#set ($javaType = $column.getJavaNative())
#if ($javaType == "String" && $column.getSize() == 1)
## It's a one-character field; could be a flag or a character
#if ("$!column.getOption('flag')" == "false")
#if ($column.isNotNull())
#set ($javaType = "char")
#else
#set ($javaType = "Character")
#end
#else
#if ($column.isNotNull())
#set ($javaType = "boolean")
#else
#set ($javaType = "Boolean")
#end
#end
#end
#if ($javaType == "BigDecimal" && !$column.getScale())
#set ($tooBigForInt = $column.getPrecision().length() > 1)
#if ($column.isNotNull())
#if ($tooBigForInt)
#set ($javaType = "long")
#else
#set ($javaType = "int")
#end
#else
#if ($tooBigForInt)
#set ($javaType = "Long")
#else
#set ($javaType = "Integer")
#end
#end
#end
## ------- Work out the default value, if any ----------------------
#set ($defaultValue = "")
#if ($column.DefaultValue && !$column.DefaultValue.equalsIgnoreCase("NULL"))
#if ($javaType == "BigDecimal")
#set ($defaultValue = " = new BigDecimal($column.DefaultValue)")
#elseif ($javaType == "boolean")
#if ($column.DefaultValue == "Y")
#set ($defaultValue = " = true")
#elseif ($column.DefaultValue == "N")
#set ($defaultValue = " = false")
#end
#elseif ($javaType == "Boolean")
#if ($column.DefaultValue == "Y")
#set ($defaultValue = " = Boolean.TRUE")
#elseif ($column.DefaultValue == "N")
#set ($defaultValue = " = Boolean.FALSE")
#end
#elseif ($javaType == "Byte")
#set ($defaultValue = " = new Byte((byte) $column.DefaultValue)")
#elseif ($javaType == "Character" || $javaType == "char")
#set ($defaultValue = " = '$column.DefaultValue'")
#elseif ($javaType == "int" || $javaType == "Integer")
#set ($defaultValue = " = $column.DefaultValue")
#elseif ($javaType == "long" || $javaType == "Long")
#set ($defaultValue = " = $column.DefaultValue")
#elseif ($javaType == "Short")
#set ($defaultValue = ' = new Short("$column.DefaultValue")')
#elseif ($javaType == "String")
#set ($defaultValue = ' = "$column.DefaultValue"')
#elseif (!$column.isPrimitive() && $javaType != "String")
#set ($defaultValue = " = new ${javaType}($column.DefaultValue)")
#end
#end
## ------- Annotations (non-FK fields) -----------------------------
#if ($column.isNotNull() && $javaType != "boolean"
&& $javaType != "char" && $javaType != "int"
&& $javaType != "long" && $javaType != "short")
@NotNull
#end
#if ($javaType == "String")
#set ($min = "")
#if ($column.isNotNull())
#set($min = "min = 1, ")
#end
@Size(${min}max = $column.getSize())
#end
## ------- Declaration (non-FK fields) -----------------------------
private $javaType $fieldName$defaultValue;
#end
#end
#end
#foreach ($foreignKey in $bidirectionalFKs)
#if ($foreignKey.getForeignTableName() == $table.getName())
#set ($fieldName = "#lowercaseFirst($foreignKey.getTableName())")
// Inverse side of bidirectional FK ${foreignKey.getTableName()}.${foreignKey.getLocalColumnNames()}
#set ($mappedByColumn = ${foreignKey.getForeignColumns().get(0)})
#set ($lastIndex = $mappedByColumn.length() - 2)
#set ($mappedByUpper = $mappedByColumn.substring(0, $lastIndex))
#set ($mappedBy = "#lowercaseFirst($mappedByUpper)")
#if ("$!foreignKey.getOption('multiplicity')" == "one-to-one")
@OneToOne(cascade = CascadeType.ALL, mappedBy = "$mappedBy")
private ${foreignKey.getTableName()} ${fieldName};
#else
@OneToMany(cascade = CascadeType.ALL, mappedBy = "$mappedBy")
private Collection<${foreignKey.getTableName()}> ${fieldName}s;
#end
#end
#end
}
By all means tweak the template (or replace it entirely) according to what code you want generated.