FreeGenタスク
FreeGenタスクとは?
なんでも自動生成DBFluteタスクです。
例えば、Solrの schema.xml を読み込んで対応するクラスを自動生成したり、JSONの定義を読み込んで対応するクラスを自動生成したり、様々な用途で利用されます。
DBFluteのメインの自動生成とは切り離された機能です。 DBFluteの自動生成エンジンを間借りして好きなものを読み込んで好きなものを自動生成するためのものと考えてよいでしょう。
主な実行タイミング
- 自動生成したいとき
FreeGenのインフラマップ
実行コマンド
DBFluteクライアント配下の manage.(bat|sh) の 12 (freegen) を選択して実行します。
処理概要
FreeGenタスクを実行すると...
- [DBFluteクライアント]/dfprop/freeGenMap.dfprop が読み込まれる
- [DBFluteクライアント]/freegen/ControlFreeGen.vm が実行される
FreeGenの使い方
ひとつのリソースでひとつのクラス #one-to-one class
例えば、message.properties から MessageDef というクラスを生成するなら...
e.g. message definition @message.properties
sea.land = good morning
piari.bonvo = good afternoon
...
- テーブル
- message.properties自体
- カラム
- propertiesのそれぞれのプロパティ
freeGenMap.dfprop
dfprop は以下のようになります。
e.g. dfprop for one-to-one @freeGenMap.dfprop
map:{
; MessageGen = map:{
; resourceMap = map:{
; resourceType = PROP
; resourceFile = ../src/main/resources/message.properties
}
; outputMap = map:{
; outputDirectory = ../src/main/java
; package = org.docksidestage.freegen.message
; templateFile = MessageDef.vm
; className = MessageDef
}
}
...
}
templateFileは、DBFluteクライアント の freegen ディレクトリからのパスとなります。
ControlFreeGen.vm
ControlFreeGen.vm では、以下のようなメソッドやプロパティを使って制御します。
- request.isOnlyOneTable()
- 単一のテーブルか否か? (ここでは true の方で実装)
- request.table
- Loadされたテーブル情報 (テンプレートファイル内で利用)
- request.templatePath
- テンプレートファイルのパス (指定された templateFile 含む)
- request.generateFilePath
- 自動生成ファイルのパス (指定された package, className 含む)
e.g. control for one-to-one as Normal Gen @ControlFreeGen.vm
$manager.info("requestList: ${requestList.size()}")
#foreach ($request in $requestList)
#set ($optionMap = $request.optionMap)
$request.enableOutputDirectory()
$manager.makeDirectory($request.generateDirPath)
##
## <<< Normal Gen >>>
##
#if ($request.isOnlyOneTable())
#set ($table = $request.table)
$manager.info("parse('${request.generateFilePath}')")
$generator.parse($request.templatePath, $request.generateFilePath, "", "")
#else
#foreach ($table in $request.tableList)
#set ($path = "${request.generateDirPath}/${table.camelizedName}.java")
$manager.makeDirectory($path)
$manager.info("parse('${path}')")
$generator.parse($request.templatePath, $path, "", "")
#end
#end
#end
上記の例を、特にカスタマイズせずにこのまま利用しても良いでしょう。
Velocityテンプレート
指定された Velocity のテンプレートの中では、以下のプロパティを使ってテンプレートを実装します。
- table
- Loadされたテーブル情報
- request.className
- 指定されたクラス名
- table.columnList
- Loadされたカラム情報のリスト
e.g. control for one-to-one as Normal Gen @ControlFreeGen.vm
/**
* @author FreeGen
*/
public class ${request.className} {
#foreach ($column in $table.columnList)
/** ${column.comment}, name=${column.capCamelName}, variable=${column.variableCount} */
public static final String ${column.defName} = "${column.propertyKey}";
...
#end
columnで利用可能なプロパティは、DfFreeGenTableLoader を implements した TableLoader によって変わりますので、ソースコードをご覧ください。
ひとつのリソースで複数のクラス #one-to-many classes
例えば、schema.json から、それぞれのテーブルに対応するクラスを生成するなら...
e.g. table definition as JSON @schema.json
MEMBER: {
$comment : "member table"
, $type : "table"
, MEMBER_ID : { type: "numeric", comment: "identity" }
, MEMBER_NAME : { type: "varchar" }
, MEMBER_STATUS_CODE : { type: "varchar" }
, MEMBER_STATUS : { type: "ref" }
},
PURCHASE: {
$comment : "purchase table"
, $type : "table"
, PURCHASE_ID : { type: "numeric" }
, MEMBER_ID : { type: "numeric" }
, MEMBER : { type: "ref" }
},
...
- テーブル
- jsonの中の一つ一つのテーブル定義
- カラム
- テーブルの中のカラム定義
freeGenMap.dfprop
dfpropは以下のようになります。(className は使わないので unused)
e.g. dfprop for one-to-many @freeGenMap.dfprop
map:{
; TableBeanGen = map:{
; resourceMap = map:{
; resourceType = JSON_SCHEMA
; resourceFile = ../src/main/resources/json/schema.json
}
; outputMap = map:{
; outputDirectory = ../src/main/java
; package = org.docksidestage.freegen.bean
; templateFile = TableBean.vm
; className = unused
}
; optionMap = map:{
; tablePath = map
; mappingMap = map:{
; type = map:{ numeric = Integer ; varchar = String }
}
}
}
...
}
optionMap は、テーブルに関するオプション情報です(昔は tableMap でした)。 任意にプロパティを指定してテンプレート内で利用することもできますし、resourceTypeによって指定が必須のプロパティや自動的に設定されるプロパティなどもあります。
resourceTypeによって変わるプロパティは、DfFreeGenTableLoader を implements した TableLoader によって変わりますので、ソースコードをご覧ください。
ControlFreeGen.vm
ControlFreeGen.vm では、以下のようなメソッドやプロパティを使って制御します。
- request.isOnlyOneTable()
- 単一のテーブルか否か? (ここでは false の方で実装)
- request.tableList
- Loadされたテーブル情報のリスト
- request.templatePath
- テンプレートファイルのパス (指定された templateFile 含む)
- table.generateDirPath
- 自動生成の出力先ディレクトリのパス (指定された package 含む)
e.g. control for one-to-one as Normal Gen @ControlFreeGen.vm
$manager.info("requestList: ${requestList.size()}")
#foreach ($request in $requestList)
#set ($optionMap = $request.optionMap)
$request.enableOutputDirectory()
$manager.makeDirectory($request.generateDirPath)
#if ($request.isOnlyOneTable())
#set ($table = $request.table)
$manager.info("parse('${request.generateFilePath}')")
$generator.parse($request.templatePath, $request.generateFilePath, "", "")
#else
#foreach ($table in $request.tableList)
#set ($path = "${request.generateDirPath}/${table.camelizedName}.java")
$manager.makeDirectory($path)
$manager.info("parse('${path}')")
$generator.parse($request.templatePath, $path, "", "")
#end
#end
#end
上記の例を、特にカスタマイズせずにこのまま利用しても良いでしょう。
Velocityテンプレート
指定された Velocity のテンプレートの中では、以下のプロパティを使ってテンプレートを実装します。
- table
- tableListのループの現在のテーブル情報
- table.columnList
- そのテーブルのカラム情報のリスト
e.g. control for one-to-one as Normal Gen @ControlFreeGen.vm
/**
* @author FreeGen
*/
public class ${table.camelizedName} {
#foreach ($column in $table.columnList)
#if ($column.isNormalColumn)
#set ($javaNative = ${column.type})
#elseif ($column.isRefColumn)
#set ($javaNative = ${column.camelizedName})
#end
/** ${column.name} */
protected ${javaNative} ${column.uncapCamelName};
#end
...
#end
table や columnで利用可能なプロパティは、DfFreeGenTableLoader を implements した TableLoader によって変わりますので、ソースコードをご覧ください。
ひとつのリソースで自由に #one-to-free classes
一つのリソースで、自由に自動生成したいのであれば... (というか、このパターンが一番多いかなと)
templateFile も unused にします。
e.g. dfprop for one-to-free @freeGenMap.dfprop
{
map:{
; TableBeanGen = map:{
; resourceMap = map:{
; resourceType = JSON_SCHEMA
; resourceFile = ../src/main/resources/json/schema.json
}
; outputMap = map:{
; outputDirectory = ../src/main/java
; package = org.docksidestage.freegen.bean
; templateFile = unused
; className = unused
}
; optionMap = map:{
; tablePath = map
; mappingMap = map:{
; type = map:{ numeric = Integer ; varchar = String }
}
}
}
...
}
}
そして、ControlFreeGen.vm にて、requestName で分岐させて自由にやります。
e.g. control for one-to-one as Normal Gen @ControlFreeGen.vm
#if ($request.requestName == "TableBeanGen")
##
## <<< Table Bean Gen >>>
##
#foreach ($table in $request.tableList)
#set ($path = "${request.generateDirPath}/bean/bs/Bs${table.camelizedName}.java")
$manager.makeDirectory($path)
$request.info("parse('${path}')")
$generator.parse("./json/BsJsonBean.vm", $path, "", "")
#set ($path = "${request.generateDirPath}/bean/ex/${table.camelizedName}.java")
$manager.makeDirectory($path)
$request.info("parse('${path}')")
#if (!$files.file(${generator.outputPath},$path).exists())
$generator.parse("./json/ExJsonBean.vm", $path, "", "")
#end
#end
#end
コードを読もう
とはいえ、ソースコードを読まないと使い方はよくわからないと思います。