Doma-Genで生成するentityをカスタマイズする。 (entityの内容を変更。)

doma-genで生成されるentityの内容をカスタマイズした時に調べた内容をまとめました。自分の場合は、型の変更とアノテーションの要素を追加をしましたが、基本的にはfreemarkerの変更で完結するので、他の場合でも応用が利くと思います。

まず簡単に言ってしまうと、以下をすれば編集は完了します。
①genのパラメータにtemplatePrimaryDir="xxx"を追加。
②entity.ftl を変更。

## 「①genのパラメータにtemplatePrimaryDir="xxx"を追加。」

templatePrimaryDirとは公式ドキュメントを参照すると「テンプレートファイルを検索する際の優先ディレクトリです。独自テンプレートファイルを使用する場合に指定します。」と書かれています。
指定は適当に決めてください。動作検証時に自分は、仮の設定としてアプリケーションルート「.」と指定しました。
例) src/main/resources/doma/template
※http://doma.seasar.org/extension/doma_gen_gen_task.html

## 「②entity.ftl」

entity.ftlとはentityを生成するtemplateファイルです。このファイルを編集すればentityの生成内容を変更できます。

**(1)entity.ftlを入手する。**

entity.ftlはdoma-gen-2.13.0.jarの中にあります。解凍してファイルを取得してください。
※同バージョンのソースを入手できれば、そちらでも良いと思います。
▼ファイルの場所
`org/seasar/doma/doma-gen/2.13.0/org/seasar/doma/extension/gen/template/entity.ftl`

**(2)entity.ftlを「templatePrimaryDir」に指定したディレクトリに配置する。**

**(3)entity.ftlを編集する。**

※編集後のファイル

```text:entity.ftl
<#-- このテンプレートに対応するデータモデルのクラスは org.seasar.doma.extension.gen.EntityDesc です -->

<#-- (1)カスタマイズfunction定義 -->
<#function convertDataType dataType>
    <#local result = dataType?replace("Byte", "Integer")>
    <#local result = result?replace("int", "Integer")>
    <#local result = result?replace("Short", "Integer")>
    <#return result>
</#function>
<#function addElement columnName>
    <#if columnName == "create_time">
    <#return ", insertable=false">
    <#elseif columnName == "updated_datetime">
    <#return ", insertable=false, updatable=false">
    </#if>
    <#return "">
</#function>
<#-- (1)カスタマイズfunction定義 -->

<#import "/lib.ftl" as lib>
<#if lib.copyright??>
${lib.copyright}
</#if>
<#if packageName??>
package ${packageName};
</#if>

<#list importNames as importName>
import ${importName};
</#list>

/**
<#if showDbComment && comment??>
 * ${comment}
</#if>
<#if lib.author??>
 * @author ${lib.author}
</#if>
 */
@Entity<#if useListener || namingType != "NONE">(</#if><#if useListener>listener = ${listenerClassSimpleName}.class</#if><#if namingType != "NONE"><#if useListener>, </#if>naming = ${namingType.referenceName}</#if><#if useListener || namingType != "NONE">)</#if>
<#if showCatalogName && catalogName?? || showSchemaName && schemaName?? || showTableName && tableName??>
@Table(<#if showCatalogName && catalogName??>catalog = "${catalogName}"</#if><#if showSchemaName && schemaName??><#if showCatalogName && catalogName??>, </#if>schema = "${schemaName}"</#if><#if showTableName><#if showCatalogName && catalogName?? || showSchemaName && schemaName??>, </#if>name = "${tableName}"</#if>)
</#if>
public class <#if entityPrefix??>${entityPrefix}</#if>${simpleName}<#if entitySuffix??>${entitySuffix}</#if><#if superclassSimpleName??> extends ${superclassSimpleName}</#if> {
<#list ownEntityPropertyDescs as property>

  <#if showDbComment && property.comment??>
    /** ${property.comment} */
  <#else>
    /** */
  </#if>
  <#if property.id>
    @Id
    <#if property.generationType??>
    @GeneratedValue(strategy = ${property.generationType.referenceName})
      <#if property.generationType == "SEQUENCE">
    @SequenceGenerator(sequence = "${tableName}_${property.columnName}"<#if property.initialValue??>, initialValue = ${property.initialValue}</#if><#if property.allocationSize??>, allocationSize = ${property.allocationSize}</#if>)
      <#elseif property.generationType == "TABLE">
    @TableGenerator(pkColumnValue = "${tableName}_${property.columnName}"<#if property.initialValue??>, initialValue = ${property.initialValue}</#if><#if property.allocationSize??>, allocationSize = ${property.allocationSize}</#if>)
      </#if>
    </#if>
  </#if>
  <#if property.version>
    @Version
  </#if>
  <#if property.showColumnName && property.columnName??>
    <#-- (2)アノテーションに要素追加 -->
    @Column(name = "${property.columnName}"${addElement(property.columnName)})
  </#if>
    <#-- (3)変数の型変換 -->
    <#if !useAccessor>public </#if>${convertDataType(property.propertyClassSimpleName)} ${property.name};
</#list>
<#if originalStatesPropertyName??>

    /** */
    @OriginalStates
    <#if entityPrefix??>${entityPrefix}</#if>${simpleName}<#if entitySuffix??>${entitySuffix}</#if> ${originalStatesPropertyName};
</#if>
<#if useAccessor>
  <#list ownEntityPropertyDescs as property>

    /**
     * Returns the ${property.name}.
     *
     * @return the ${property.name}
     */
    <#-- (4)戻り値の型変換 -->
    public ${convertDataType(property.propertyClassSimpleName)} get${property.name?cap_first}() {
        return ${property.name};
    }

    /**
     * Sets the ${property.name}.
     *
     * @param ${property.name} the ${property.name}
     */
    <#-- (5)引数の型変換 -->
    public void set${property.name?cap_first}(${convertDataType(property.propertyClassSimpleName)} ${property.name}) {
        this.${property.name} = ${property.name};
    }
  </#list>
</#if>
}
```

編集内容の説明をします。

**コメント：(1)カスタマイズfunction定義 の箇所**
とコメントを打っている箇所は
A)引数で受けた型文字列を変換するconvertDataTypeメソッドです。
※変換仕様
Byte→Integer
int→Integer
Short→Integer

B)引数で受けたカラム名文字列がcreate_time or update_timeの場合にアノテーションに追加する文字列を返すaddElementメソッドです。
※仕様
create_timeの場合、", insertable=false" を返す。
update_timeの場合、", insertable=false, updatable=false" を返す。
それ以外は""を返す。

**コメント：(2)アノテーションに要素追加 の箇所**
@Columnの記載箇所でaddElementメソッドに呼び、アノテーションの要素をカスタマイズしています。

**コメント：(3)変数の型変換 の箇所**
とコメントを打っている箇所は
フィールド変数の記載箇所でconvertDataTypeを呼び、型をカスタマイズしています。

**コメント：(4)戻り値の型変換 の箇所**
とコメントを打っている箇所は
getterメソッドの記載箇所でconvertDataTypeを呼び、戻り値の型をカスタマイズしています。

**コメント：(5)引数の型変換 の箇所**
とコメントを打っている箇所は
setterメソッドの記載箇所でconvertDataTypeを呼び、引数の型をカスタマイズしています。

以上です。
freemarkerの構文さえ分かれば、entity生成をカスタマイズできるので
ぜひ試していただければと思います。