Mybatis Generatorで特定カラムをデフォルト値で常に更新するようにする方法

# はじめに
更新日付等、常にシステム日時で更新したいカラムがある場合、Mybatis Generatorで生成されたSQLをそのまま使用する場合は都度エンティティーに値を設定しなければなりません。

そこで、ここでは、Mybatis Generatorにおいて、特定カラムをデフォルト値で常に更新するようなSQLを生成されるようにカスタマイズする方法を紹介します。

# やり方
特定カラムを特定の値で常に更新するSQLを生成するための設定はデフォルトだと存在しないため、それを実現するためには別途プラグインを自前で作成して対応する必要があります。

## 準備
この自作プラグインはソース生成時にのみ必要で、実際のリリース対象には必要ありません。ですので、自作プラグイン用のプロジェクトは、ソース生成先プロジェクトとは独立して作成することをお勧めします。

自作プラグイン用のプロジェクトを作成したら、以下の通りpom.xmlファイルにプラグイン作成に必要なライブラリを追加します。

```xml:pom.xml
...
<dependencies>
...
    <dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.2</version></dependency> <!-- バージョンは適宜修正 -->
    <scope>provided</scope>
    ...
</dependencies>
...
```

## プラグインの処理を記述
PluginAdapterクラスを継承して、処理を記述します。
以下のメソッドをオーバーライドします。

|メソッド名|説明|
|---|---|
|validate|プラグイン実行の前提条件を満たしているか検証するためのメソッド。必ず実装する必要があるため、検証が不要な場合はtrueを返却する必要がある。|
|sqlMapInsertElementGenerated|INSERT文を生成するためのメソッド|
|sqlMapInsertSelectiveElementGenerated|NULL要素以外を対象にINSERTを行うためSQLを生成するためのメソッド|
|sqlMapUpdateByPrimaryKeySelectiveElementGenerated|NULL要素以外を対象に主キーでUPDATEを行うためのSQLを生成するためのメソッド|
|sqlMapUpdateByPrimaryKeyWithBLOBsElementGenerated|全項目を対象に主キーでUPDATEを行うためSQLを生成するためのメソッド（項目中にBLOBデータあり）|
|sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated|全項目を対象に主キーでUPDATEを行うためSQLを生成するためのメソッド（項目中にBLOBデータなし）|SQLを生成するためのメソッド（項目中にBLOBデータなし）
|sqlMapDocumentGenerated|SqlMap documentが生成されたタイミングで呼ばれ、documentにXML要素を別途追加する場合に使用する。|

上記のメソッドにて、それぞれ出力されるSQL文の加工を行います。

実装するクラスは以下の通りです。

```java:実装するPluginAdapterのサブクラス

package com.example.test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.VisitableElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.mybatis3.ListUtilities;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;

/**
 * 常に特定カラムをデフォルト値で更新するためのプラグイン
 */
public class UpdateDefauleValuePlugin extends PluginAdapter {

	/** カラム名 */
	private List<String> columnNameList = new ArrayList<String>();

	/** カラムに設定する値 */
	private List<String> columnValueList = new ArrayList<String>();

	/** UPDATE文のみ有効かどうか */
	private List<Boolean> updateOnlyList = new ArrayList<Boolean>();

	/** 新たに生成するタグ */
	private final Map<FullyQualifiedTable, List<XmlElement>> elementsToAdd = new HashMap<>();

	@Override
	public boolean validate(List<String> warnings) {

        boolean result = true;
		for (int i = 0; i < 10; i++) {
			String columnName = properties.getProperty("columnName" + (i + 1));
			String columnValue = properties.getProperty("columnValue" + (i + 1));
			if (columnValue == null && columnName == null) {
				continue;
			}
			if (columnName == null) {
				warnings.add("Please specify columnName" + (i + 1));
                result = false;
				continue;
			}
			if (columnValue == null) {
				warnings.add("Please specify columnValue" + (i + 1));
                result = false;
				continue;
			}

			columnNameList.add(columnName);
			columnValueList.add(columnValue);
			updateOnlyList.add(properties.getProperty("updateOnly" + (i + 1)) == null ? false
					: Boolean.valueOf(properties.getProperty("updateOnly" + (i + 1))));
		}
		return result;
	}

	@Override
	public boolean sqlMapInsertElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {

		// 対象カラムへの設定値を置換
		replaceColumnValue(element, introspectedTable, false);
		return true;
	}

	@Override
	public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {

		for (int i = 0; i < columnNameList.size(); i++) {

			// UPDATE文のみ対象の場合
			if (updateOnlyList.get(i)) {
				continue;
			}
			IntrospectedColumn targetColumn = getTargetColumn(introspectedTable, columnNameList.get(i));
			if (targetColumn == null) {
				continue;
			}
			String columnValue = columnValueList.get(i);

			List<VisitableElement> elementList = element.getElements();
			int count = 0;
			for (int j = 0; j < elementList.size(); j++) {
				VisitableElement e = elementList.get(j);
				if (e instanceof XmlElement) {
					XmlElement trimElement = (XmlElement) e;
					if (!"trim".equalsIgnoreCase(trimElement.getName())) {
						continue;
					}
					if (count == 0) {
						// INSERT部のifタグの書き換え
						chengeIftag(trimElement, targetColumn,
								MyBatis3FormattingUtilities.getEscapedColumnName(targetColumn) + ",");
						count++;
					} else {
						// VALUES部のifタグの書き換え
						chengeIftag(trimElement, targetColumn, columnValue + ",");
					}
				}
			}
		}
		return true;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeyWithBLOBsElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		// 対象カラムへの設定値を置換
		replaceColumnValue(element, introspectedTable, true);
		return true;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {
		// 対象カラムへの設定値を置換
		replaceColumnValue(element, introspectedTable, true);
		return true;
	}

	@Override
	public boolean sqlMapUpdateByPrimaryKeySelectiveElementGenerated(XmlElement element,
			IntrospectedTable introspectedTable) {

		List<VisitableElement> elementList = element.getElements();
		for (int i = 0; i < elementList.size(); i++) {
			VisitableElement e = elementList.get(i);
			if (e instanceof XmlElement) {
				XmlElement setElement = (XmlElement) e;

				// setタグ以外は対象外
				if (!"set".equalsIgnoreCase(setElement.getName())) {
					continue;
				}

				// 対象カラムへの設定値を置換
				for (int j = 0; j < columnNameList.size(); j++) {
					IntrospectedColumn targetColumn = getTargetColumn(introspectedTable, columnNameList.get(j));
					if (targetColumn == null) {
						continue;
					}
					String columnValue = columnValueList.get(j);

					// setタグ中のifタグの書き換え
					chengeIftag(setElement, targetColumn,
							MyBatis3FormattingUtilities.getEscapedColumnName(targetColumn) + " = " + columnValue
									+ ",");
				}

				// setタグを外だしするための新しいタグを作成
				XmlElement newElement = new XmlElement("sql");
				newElement.addAttribute(new Attribute("id", "UpdateSelectiveColumns"));
				context.getCommentGenerator().addComment(newElement);
				// 新しいタグにsetタグを追加
				newElement.addElement(setElement);
				// 新しく生成したタグをローカル変数に保存。後ほどsqlMapDocumentGeneratedメソッドにてRootDocument配下に追加
				List<XmlElement> elements = elementsToAdd.computeIfAbsent(introspectedTable.getFullyQualifiedTable(),
						k -> new ArrayList<>());
				elements.add(newElement);

				// setタグをincludeタグに入れ替える（外だししたSET句を参照するようにする）
				XmlElement answer = new XmlElement("include");
				answer.addAttribute(new Attribute("refid", "UpdateSelectiveColumns"));
				elementList.set(i, answer);
				break;
			}
		}

		return true;

	}

	@Override
	public boolean sqlMapDocumentGenerated(Document document,
			IntrospectedTable introspectedTable) {
		List<XmlElement> elements = elementsToAdd.get(introspectedTable.getFullyQualifiedTable());
		if (elements != null) {
			for (XmlElement element : elements) {
				document.getRootElement().addElement(element);
			}
		}

		return true;
	}

	/**
	 * 対象カラムへの設定値を置換
	 * @param columnName XmlElement
	 * @param introspectedTable IntrospectedTable
	 * @param isUpdate UPDATEかどうか
	 */
	private void replaceColumnValue(XmlElement element, IntrospectedTable introspectedTable, boolean isUpdate) {

		for (int i = 0; i < columnNameList.size(); i++) {
			// INSERT用かつupdate時のみ置換の場合
			if (!isUpdate && updateOnlyList.get(i)) {
				continue;
			}
			IntrospectedColumn targetColumn = getTargetColumn(introspectedTable, columnNameList.get(i));
			if (targetColumn == null) {
				continue;
			}
			String columnValue = columnValueList.get(i);
			List<VisitableElement> elementList = element.getElements();
			for (int j = 0; j < elementList.size(); j++) {
				VisitableElement e = elementList.get(j);
				String line = ((TextElement) e).getContent();

				// リストの要素を置換文字列に入れ替える
				elementList.set(j, new TextElement(
						line.replace(MyBatis3FormattingUtilities.getParameterClause(targetColumn), columnValue)));
			}
		}
	}

	/**
	 * カラム名からIntrospectedColumnを取得する
	 * @param introspectedTable IntrospectedTable
	 * @param columnName 取得対象とあるカラム名
	 * @return 対象のIntrospectedColumn
	 */
	private IntrospectedColumn getTargetColumn(IntrospectedTable introspectedTable, String columnName) {

		for (IntrospectedColumn introspectedColumn : ListUtilities
				.removeIdentityAndGeneratedAlwaysColumns(introspectedTable.getAllColumns())) {
			if (columnName.equalsIgnoreCase(introspectedColumn.getActualColumnName())) {
				return introspectedColumn;
			}
		}
		return null;
	}

	/**
	 * targetColumnに対するifタグの中身をreplacementで示す文字列に変更
	 * @param xmlElement XmlElement
	 * @param targetColumn 対象カラム
	 * @param replecement 置換文字列
	 */
	private void chengeIftag(XmlElement xmlElement, IntrospectedColumn targetColumn, String replecement) {

		for (int i = 0; i < xmlElement.getElements().size(); i++) {
			VisitableElement ve = xmlElement.getElements().get(i);
			if (ve instanceof TextElement) {
				continue;
			}
			XmlElement xe = (XmlElement) ve;
			if ("if".equalsIgnoreCase(xe.getName())) {
				Attribute attr = getTestAttribute(xe);
				if (attr == null) {
					continue;
				}
				if (attr.getValue().startsWith(targetColumn.getJavaProperty() + " != null")) {
					// リストの要素を置換文字列に入れ替える
					xmlElement.getElements().set(i, new TextElement(replecement));
				}
			}
		}

	}

	/**
	 * test属性を取得
	 * @param ifElement
	 * @return test属性のAttribute
	 */
	private Attribute getTestAttribute(XmlElement ifElement) {

		for (Attribute attr : ifElement.getAttributes()) {
			if ("test".equalsIgnoreCase(attr.getName())) {
				return attr;
			}
		}
		return null;
	}

}
```

上記ソースにおいては、``validate()``メソッドをオーバーライドして、カラム名と更新値をgeneratorConfig.xmlの設定値から読み取り、妥当性の検証、および、ローカル変数に追加を行っています。
そのため、デフォルト値を設定するカラム名、設定値をgeneratorConfig.xmlに記述することとなります。

また、上記ソースでは、UPDATE文のSET句のタグを外だしすることで、他でも使用できるようにしました。（出力例と使用例は後述）

generatorConfig.xmlへのデフォルト値を設定するカラム名、設定値の記入例は以下の通りです。

```xml:generatorConfig.xml
...
<generatorConfiguration>
    <context ...>
        <plugin type="com.example.test.UpdateDefaultValuePlugin"> <!-- 作成したPluginAdapterのサブクラスを指定 -->
            <property name="columnName1" value="update_date" /> <!-- デフォルト値を設定するカラム -->
            <property name="columnValue1" value="CURRENT_TIMESTAMP" /> <!-- 設定するデフォルト値 -->
        </plugin>
...
```

# 実行方法
実行方法は、Eclipseプラグインにて実行する場合とmavenで実行する場合で異なります。

## EclipseのMybatis Generatorプラグインにて実行する場合
generatorConfig.xmlファイルを当プラグインプロジェクトの任意のフォルダに配置します。

generatorConfig.xmlに、生成するソースファイルの配置先を指定します。
```xml:generatorConfig.xml
    ...

    <!-- SELECT 結果等を格納するドメインモデルを生成する設定 -->
    <!-- targetProjectには、ソース生成先のプロジェクトフォルダを含めて設定 -->
    <javaModelGenerator targetPackage="com.example.test.model" targetProject="sample-project/src/main/java">
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <!-- SQL 設定が記述された XML を生成する設定 -->
    <sqlMapGenerator targetPackage="com.example.test.repository" targetProject="sample-project/src/main/resources">
    </sqlMapGenerator>
    <!-- マッパークラスを生成する設定 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.test.repository" targetProject="sample-project/src/main/java">
    </javaClientGenerator>

    ...
```

Eclipseプラグインを使用する場合は、generatorConfig.xmlを右クリックし、「実行」→「Run MyBatis Generator」で実行できます。
具体的な実行方法は[こちら](https://qiita.com/norippe3/items/e0cfb33e1ec52b433965#%E5%AE%9F%E8%A1%8C%E6%96%B9%E6%B3%95)の通りです。

## Mavenにて実行する場合
まず最初に、プラグインプロジェクトにて以下のコマンドを実行します。
```shell
mvn package
```

これで、自作プラグインのjarファイルがtargetディレクトリに作成されます。

generatorConfig.xmlファイルを当プラグインプロジェクトの任意のフォルダに配置します。


pom.xmlに以下の記述を追加します。

```xml:pom.xml
...
<build>
    <plugins>
        <!-- mybatis-generatorプラグイン定義 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.4.2</version></dependency> <!-- バージョンは適宜修正 -->
            <configuration>
                <!-- generatorConfig.xmlファイルはプロジェクトのルートに配置している。適宜修正 -->
                <configurationFile>${basedir}/generatorConfig.xml</configurationFile>
                <!-- mybatis生成時、既存クラスを上書きしない場合はfalse。上書きする場合はtrue-->
                <overwrite>false</overwrite>
            </configuration>
            <dependencies>
                <!-- プラグインで使用するJDBCドライバ(PostgreSQL) -->
                <dependency>
                    <groupId>org.postgresql</groupId>
                    <artifactId>postgresql</artifactId>
                    <version>42.7.5</version>
                </dependency>
                <!-- 作成したプラグインを依存関係に追加 -->
                <dependency>
                    <groupId>${project.groupId}</groupId>
                    <artifactId>${project.artifactId}</artifactId>
                    <version>${project.version}</version>
                    <scope>system</scope>
                    <systemPath>${project.build.directory}/${project.build.finalName}.jar</systemPath>
                </dependency>
            </dependencies>
        </plugin>
        ...
    </plugins>
    ...
</build>
...
```
generatorConfig.xmlに、生成するソースファイルの配置先を指定します。
```xml:generatorConfig.xml
    ...

    <!-- SELECT 結果等を格納するドメインモデルを生成する設定 -->
    <!-- targetProjectには、ソース生成先のパスを設定-->
    <!-- 　下記例は、当プラグインプロジェクトからの相対パスにて設定している -->
    <javaModelGenerator targetPackage="com.example.test.model" targetProject="../sample-project/src/main/java">
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <!-- SQL 設定が記述された XML を生成する設定 -->
    <sqlMapGenerator targetPackage="com.example.test.repository" targetProject="../sample-project/src/main/resources">
    </sqlMapGenerator>
    <!-- マッパークラスを生成する設定 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="../com.example.test.repository" targetProject="sample-project/src/main/java">
    </javaClientGenerator>

    ...
```
pom.xmlとgeneratorConfig.xmlの設定が完了したら、当プラグインプロジェクトにて以下のコマンドを実行することでソースが生成されます。
```shell
mvn mybatis-generator:generate
```
2回目以降実施時は、上記コマンドを再実行するのみでOKです。

# 生成されたMapparファイルのサンプル

以下のようなテーブル定義について出力するものとします。

テーブル名：user
| カラム名 | 型 |
|---|---|
|user_id|varchar|
|user_name|varchar|
|delete_flg|char|
|version|integer|
|update_date|timestamp|

上記カラムのうち、update_dateを常にCURRENT_TIMESTAMPで更新したい場合は、以下の通り、generatorConfig.xmlを設定して実行します。
```xml:generatorConfig.xml
...
        <plugin type="com.example.test.UpdateDefaultValuePlugin">
            <property name="columnName1" value="update_date" />
            <property name="columnValue1" value="CURRENT_TIMESTAMP" />
        </plugin>
...
```


すると、上記のテーブルに対して、以下のように出力されます。
```xml:userMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.test.repository.UserMapper">
  <resultMap id="BaseResultMap" type="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    <id column="user_id" jdbcType="VARCHAR" property="userId" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
    <result column="delete_flg" jdbcType="CHAR" property="deleteFlg" />
    <result column="version" jdbcType="INTEGER" property="version" />
    <result column="update_date" jdbcType="TIMESTAMP" property="updateDate" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    user_id, user_name, delete_flg, version, update_date
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    select 
    <include refid="Base_Column_List" />
    from public.user
    where user_id = #{userId,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    delete from public.user
    where user_id = #{userId,jdbcType=VARCHAR}
  </delete>
  <insert id="insert" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    insert into public.user (user_id, user_name, delete_flg, 
      version, update_date)
    values (#{userId,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, #{deleteFlg,jdbcType=CHAR}, 
      #{version,jdbcType=INTEGER}, CURRENT_TIMESTAMP)
  </insert>
  <insert id="insertSelective" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    insert into public.user
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        user_id,
      </if>
      <if test="userName != null">
        user_name,
      </if>
      <if test="deleteFlg != null">
        delete_flg,
      </if>
      <if test="version != null">
        version,
      </if>
      update_date,
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        #{userId,jdbcType=VARCHAR},
      </if>
      <if test="userName != null">
        #{userName,jdbcType=VARCHAR},
      </if>
      <if test="deleteFlg != null">
        #{deleteFlg,jdbcType=CHAR},
      </if>
      <if test="version != null">
        #{version,jdbcType=INTEGER},
      </if>
      CURRENT_TIMESTAMP,
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    update public.user
    <include refid="UpdateSelectiveColumns" />
    where user_id = #{userId,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    update public.user
    set user_name = #{userName,jdbcType=VARCHAR},
      delete_flg = #{deleteFlg,jdbcType=CHAR},
      version = #{version,jdbcType=INTEGER},
      update_date = CURRENT_TIMESTAMP
    where user_id = #{userId,jdbcType=VARCHAR}
  </update>
  <sql id="UpdateSelectiveColumns">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    <set>
      <if test="userName != null">
        user_name = #{userName,jdbcType=VARCHAR},
      </if>
      <if test="deleteFlg != null">
        delete_flg = #{deleteFlg,jdbcType=CHAR},
      </if>
      <if test="version != null">
          version = #{version,jdbcType=INTEGER},
      </if>
      update_date = CURRENT_TIMESTAMP,
    </set>
  </sql>
  
</mapper>
```

このように、generatorConfig.xmlファイルで設定したカラム名(ここではupdate_date)の値が常に当該ファイルで設定した値（ここではCURRENT_TIMESTAMP）に常に更新するSQLが生成されます。

また、updateSelectiveのSET句の部分を外だしするようにカスタマイズされているので、同じSET句を別の個所でも使用できるようにしました。
以下のような使い方ができます。

<b>外だししたSET句を他のSQLで使用する場合の例）</b>
```xml:userMapper.xml
...

    <!- 自前で作成したUPDATE文に自動生成されたSET句を使用したい場合 ->
    <update id="updateByXXX" parameterType="com.example.test.model.User">
    update public.user
    <include refid="UpdateSelectiveColumns" />
    where user_id = #{userId}
    and version = #{version}
  </update>
...
```


# 注意事項
上記プラグインでは、updateExample～関連のメソッドには手を加えていないので、updateメソッド、およびupdateSelectiveメソッドのみ対応で、updateExampleメソッドには対応していません。
（当方は、WHARE句は直接SQLを記述する主義なので当該メソッドは利用しないため）

# 応用編
上記例では更新日付を常にCURRENT_TIMESTAMPで更新していますが、それ以外の使い方もご紹介します。

## 楽観ロック対応
以下のような設定をgeneratorConfig.xmlに行うことで、楽観ロック対応もできます。

```xml:generatorConfig.xml
...
<generatorConfiguration>
    <context ...>
        ...
        <plugin type="com.example.test.UpdateDefaultValuePlugin">
            <!-- 更新日付カラムを常にシステム日時で更新する -->
            <property name="columnName1" value="update_date" />
            <property name="columnValue1" value="CURRENT_TIMESTAMP" />
            <!-- 楽観ロック対応 -->
            <property name="columnName2" value="version" /> <!-- バージョン管理用カラム -->
            <property name="columnValue2" value="version + 1" /> <!-- 設定値 -->
            <property name="updateOnly2" value="true" /> <!-- UPDATE文のみ設定を有効とする -->
        </plugin>
```

<b>生成されるMapper.xmlの例</b>

```xml:生成されたMapper.xml
...
  <insert id="insert" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    insert into public.user (user_id, user_name, delete_flg, 
      version, update_date)
    values (#{userId,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, #{deleteFlg,jdbcType=CHAR}, 
      #{version,jdbcType=INTEGER}, CURRENT_TIMESTAMP)
  </insert>
  <insert id="insertSelective" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    insert into public.user
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        user_id,
      </if>
      <if test="userName != null">
        user_name,
      </if>
      <if test="deleteFlg != null">
        delete_flg,
      </if>
      <if test="version != null">
        version,
      </if>
      update_date,
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="userId != null">
        #{userId,jdbcType=VARCHAR},
      </if>
      <if test="userName != null">
        #{userName,jdbcType=VARCHAR},
      </if>
      <if test="deleteFlg != null">
        #{deleteFlg,jdbcType=CHAR},
      </if>
      <if test="version != null">
        #{version,jdbcType=INTEGER},
      </if>
      CURRENT_TIMESTAMP,
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    update public.user
    <include refid="UpdateSelectiveColumns" />
    where user_id = #{userId,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.example.test.model.User">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    update public.user
    set user_name = #{userName,jdbcType=VARCHAR},
      delete_flg = #{deleteFlg,jdbcType=CHAR},
      version = version + 1,
      update_date = CURRENT_TIMESTAMP
    where user_id = #{userId,jdbcType=VARCHAR}
  </update>
  <sql id="UpdateSelectiveColumns">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon May 12 12:13:38 JST 2025.
    -->
    <set>
      <if test="userName != null">
        user_name = #{userName,jdbcType=VARCHAR},
      </if>
      <if test="deleteFlg != null">
        delete_flg = #{deleteFlg,jdbcType=CHAR},
      </if>
      version = version + 1,
      update_date = CURRENT_TIMESTAMP,
    </set>
  </sql>
...
```
上記のように、バージョン管理用カラムversionは、UPDATE文の場合のみ設定値で更新され、INSERT文の場合はデフォルト値の設定はされません。
一方、update_dateに関しては、INSERT文,UPDATE文いずれの場合もCURRENT_TIMESTAMPで更新されます。

# 参考サイト

https://www.nextdoorwith.info/se/system-dev-mybatis-generator/

https://www.nextdoorwith.info/se/mybatis-generator-custom-plugin/

https://github.com/mybatis/generator/blob/master/core/mybatis-generator-core/src/main/java/org/mybatis/generator/plugins/RowBoundsPlugin.java