FreeGenタスク

FreeGenタスクとは?

なんでも自動生成DBFluteタスクです。

例えば、Solrの schema.xml を読み込んで対応するクラスを自動生成したり、JSONの定義を読み込んで対応するクラスを自動生成したり、様々な用途で利用されます。

DBFluteのメインの自動生成とは切り離された機能です。 DBFluteの自動生成エンジンを間借りして好きなものを読み込んで好きなものを自動生成するためのものと考えてよいでしょう。

主な実行タイミング

  • 自動生成したいとき

FreeGenのインフラマップ

図 : FreeGenのインフラマップ FreeGenのインフラマップ

実行コマンド

DBFluteクライアント配下の manage.(bat|sh) の 12 (freegen) を選択して実行します。

処理概要

FreeGenタスクを実行すると...

  1. [DBFluteクライアント]/dfprop/freeGenMap.dfprop が読み込まれる
  2. [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.tableMap)
$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
        }
        ; tableMap = map:{
            ; tablePath = map
            ; mappingMap = map:{
                ; type = map:{ numeric = Integer ; varchar = String }
            }
        }
    }
    ...
}

tableMap は、テーブルに関するオプション情報です(将来的に、互換性を残しつつ optionMap という名前に変更予定)。 任意にプロパティを指定してテンプレート内で利用することもできますし、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.tableMap)
$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
            }
            ; tableMap = 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

コードを読もう

とはいえ、ソースコードを読まないと使い方はよくわからないと思います。