つかびーの技術日記

(情報)工学修士, 元SIer SE, 現Web系 SEの技術blogです。Scala, Java, JS, TS, Python, Ruby, AWS, GCPあたりが好きです。

MyBatis Generatorで生成するクラス内のDateをCalendarに変更する

      2014/01/05

訂正 2014/1/5

コメントにて、tomoさんからよりスマートな方法を教えて頂きました。以下のようにgeneratorConfig.xmlを編集すれば、以降のCalendarPluginは不要です。

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration >
  (中略)
  <context id="context1">
    (中略)
    <table tableName="%">
      <columnOverride column="DATETIME" javaType="java.util.Calendar" />
    </table>
  </context>
</generatorConfiguration>

columnOverrideについてはこちらを参照。tableName=”%”となっているので、DATETIMEという名前の列を持っていないテーブルが存在する場合、警告が出てしまいます。ちゃんとやる場合はtableNameを正しく定義してあげる必要があるかと思います。この設定で自動生成を行うとBean内の型がDateからCalendarに変わりました。実際に使用(Insert, Select)して問題なさそうでした。

tomoさん、どうもありがとうございました。

本文

前回の記事でMyBatisでDateではなくCalendarを利用する方法を説明しました。独自のTypeHandlerを用意し、MyBatisにCalendar型の扱い方を示しました。

MyBatis用のクラスや設定ファイルを自力で作成する場合は、何も問題はありません。しかし、MyBatis Generatorを利用する場合は別です。MyBatis Generatorを利用する場合、DBのテーブルの列が日付型である場合、Java側ではDateになってしまいます。折角Generatorで自動生成しているのに、手動でDateからCalendarへ変更するという処理は行いたくありません・・・。

今回はこれを何とかして、MyBatis GeneratorでCalendarを利用できるようにします。

例によってPluginで対応

MyBatis Generatorの挙動をカスタマイズしたいときはPluginを利用します。

MyBatis Generator + Lombokを試したときと同様にPluginを作成します。

CalendarPlugin.java

package com.tsukaby.mybatisdate.mybatis.plugins;

import java.util.List;

import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;

public class CalendarPlugin extends PluginAdapter {

  public CalendarPlugin() {
    super();
  }

  @Override
  public boolean validate(List<String> warnings) {
    return true;
  }

  @Override
  public void initialized(IntrospectedTable introspectedTable) {
  }

  @Override
  public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
    changeDateToCalendar(topLevelClass);
    return true;
  }

  protected void changeDateToCalendar(TopLevelClass topLevelClass) {
    FullyQualifiedJavaType typeDate = new FullyQualifiedJavaType("java.util.Date");
    FullyQualifiedJavaType typeCalendar = new FullyQualifiedJavaType("java.util.Calendar");
    boolean hasCalendarClass = false;

    // Date型のfieldをCalendar型へ変更
    for (Field field : topLevelClass.getFields()) {
      if (typeDate.equals(field.getType())) {
        field.setType(typeCalendar);
        hasCalendarClass = true;
      }
    }

    // メソッドの戻り値型、引数型をCalendarへ
    for (Method method : topLevelClass.getMethods()) {
      // 戻り値
      if (typeDate.equals(method.getReturnType())) {
        method.setReturnType(typeCalendar);
        hasCalendarClass = true;
      }

      // 引数型
      List<Parameter> list = method.getParameters();
      for (int i = 0; i < list.size(); i++) {
        if (typeDate.equals(list.get(i).getType())) {
          // DateをCalendarに置換
          list.set(i, new Parameter(typeCalendar, list.get(i).getName()));
          hasCalendarClass = true;
        }
      }
    }

    // Calendar型を利用している場合はimport追加
    if (hasCalendarClass) {
      topLevelClass.addImportedType(typeCalendar);
    }

  }
}

generatorConfig.xml (contextタグ内に以下を追加)

<plugin type="com.tsukaby.mybatisdate.mybatis.plugins.CalendarPlugin" />

これで実行するとDateがCalendarになります。

問題点

実際に利用してみると、生成されたJavaBean(DTO)に警告が・・・・

インポートされた java.util.Date は 1 度も使用されていません

上記のクラス内にtopLevelClassという変数があります。これは内部にimportするクラスのセットを持っていますが、上記のchangeDateToCalendarメソッド内でそのセットを操作していません。正確には、そのセットにCalendarクラスは追加しましたが、Dateクラスは取り除いていません。

じゃあ取り除けばいいよね・・・と思ってもこれができない・・・!

セットは以下のメソッドで取得することができます。

topLevelClass.getImportedTypes();

しかし、残念ながらこのメソッドの戻り値はUnmodifiableなセットです。つまり変更できません。他にimportを取り除くためのメソッドなども無く、残念ながら上記の警告は回避できそうにありません。何かもっと良い方法などをご存知の方いらっしゃいましたら教えて頂きたいです。

そんな訳で、警告は出てしまいますが、一応使えるようにしてみました。以上です。

 - Java