MyBatisGeneratorのMapper自動生成機能をカスタマイズして見た

MyBatisでSpringプロジェクトの環境構築をして、MyBatisGeneratorのDto,Dao,Mapperの自動生成機能を使って見たが、いくつ改善したいところがあります。
・Springのアノテーションを自動追加したい
・Lombokを対応したい
・DtoとDaoのファイル名を馴染みなXXXDto.javaとXXXDao.javaに改修したい
以上の課題を解決するため、MyBatisGeneratorのPluginAdapterによって自動生成機能をカスタマイズして見ました。

##環境構成
・eclipse
・Java
・MyBatis-Spring
・MyBatisGenerator
・Spring-boot
・Lombok 
・Maven

##依頼注入一覧

```pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>springBatchTest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springBatchTest</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-batch</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.4.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>
```

#PluginAdapterのサブクラスを作成する
ここでアノテーションを追加する。

```MyBatisPlugin.java
package com.example.demo.conf;

import java.util.List;

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;

public class MyBatisPlugin extends PluginAdapter {

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

	/*
	 * Daoクラスの自動生成をコントロールするメソッド
	 */
	@Override
	public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		
		// Lombok機能をimportする
		topLevelClass.addImportedType("lombok.Data");
		// アノテーション追加
		topLevelClass.addAnnotation("@Data");

		return true;
	}

	/*
	 * Dtoクラスの自動生成をコントロールするメソッド
	 */
	@Override
	public boolean clientGenerated(Interface interfaze, IntrospectedTable introspectedTable) {

		// Spring機能をimportする
		interfaze.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Repository"));
		interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Mapper"));
		// アノテーション追加
		interfaze.addAnnotation("@Mapper");
		interfaze.addAnnotation("@Repository");

		return true;
	}

	/*
	 * DtoクラスのSetterメソッドをコントロールするメソッド
	 */
	@Override
	public boolean modelSetterMethodGenerated(Method method, TopLevelClass topLevelClass,
			IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
		
		// DtoクラスのSetterメソッドを自動生成しないようにする
		return false;
	}

	/*
	 * DtoクラスのGetterメソッドをコントロールするメソッド
	 */
	@Override
	public boolean modelGetterMethodGenerated(Method method, TopLevelClass topLevelClass,
			IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
		
		// DtoクラスのGetterメソッドを自動生成しないようにする
		return false;
	}

}
```
##IntrospectedTableMyBatis3Implのサブクラスを作成する
ここでファイル名を改修する、やり方はシンプルで、IntrospectedTableにあるDto、Daoのファイル名編集に関するメソッドをそのままコピーして、改修したい部分だけ改修して、オーバーライドすればＯＫです。

```MyBatis3Ex.java

package com.example.demo.conf;

import static org.mybatis.generator.internal.util.StringUtility.*;

import org.mybatis.generator.codegen.mybatis3.IntrospectedTableMyBatis3Impl;

public class MyBatis3Ex extends IntrospectedTableMyBatis3Impl{

	/*
	 * IntrospectedTableのcalculateJavaClientAttributesメソッドの丸コピー
	 */
	@Override
    protected void calculateJavaClientAttributes() {
        if (context.getJavaClientGeneratorConfiguration() == null) {
            return;
        }

        StringBuilder sb = new StringBuilder();
        sb.append(calculateJavaClientInterfacePackage());
        sb.append('.');
        if (stringHasValue(tableConfiguration.getMapperName())) {
            sb.append(tableConfiguration.getMapperName());
        } else {
            if (stringHasValue(fullyQualifiedTable.getDomainObjectSubPackage())) {
                sb.append(fullyQualifiedTable.getDomainObjectSubPackage());
                sb.append('.');
            }
            sb.append(fullyQualifiedTable.getDomainObjectName());
            // ↓↓　元は"Mapper"と書いてる、"Dao"に改修します。
            sb.append("Dao"); //$NON-NLS-1$
        }
        setMyBatis3JavaMapperType(sb.toString());

        sb.setLength(0);
        sb.append(calculateJavaClientInterfacePackage());
        sb.append('.');
        if (stringHasValue(tableConfiguration.getSqlProviderName())) {
            sb.append(tableConfiguration.getSqlProviderName());
        } else {
            if (stringHasValue(fullyQualifiedTable.getDomainObjectSubPackage())) {
                sb.append(fullyQualifiedTable.getDomainObjectSubPackage());
                sb.append('.');
            }
            sb.append(fullyQualifiedTable.getDomainObjectName());
            sb.append("SqlProvider"); //$NON-NLS-1$
        }
        setMyBatis3SqlProviderType(sb.toString());
        
        sb.setLength(0);
        sb.append(calculateJavaClientInterfacePackage());
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("DynamicSqlSupport"); //$NON-NLS-1$
        setMyBatisDynamicSqlSupportType(sb.toString());
    }
	
	/*
	 * IntrospectedTableのcalculateModelAttributesメソッドの丸コピー
	 */
	@Override
    protected void calculateModelAttributes() {
        String pakkage = calculateJavaModelPackage();

        StringBuilder sb = new StringBuilder();
        sb.append(pakkage);
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("Key"); //$NON-NLS-1$
        setPrimaryKeyType(sb.toString());

        sb.setLength(0);
        sb.append(pakkage);
        sb.append('.');
		sb.append(fullyQualifiedTable.getDomainObjectName());
		// ↓↓ "Dto"を追加する
        sb.append("Dto");
        setBaseRecordType(sb.toString());

        sb.setLength(0);
        sb.append(pakkage);
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("Record"); //$NON-NLS-1$
        setKotlinRecordType(sb.toString());

        sb.setLength(0);
        sb.append(pakkage);
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("WithBLOBs"); //$NON-NLS-1$
        setRecordWithBLOBsType(sb.toString());

        String exampleTargetPackage = calculateJavaModelExamplePackage();
        sb.setLength(0);
        sb.append(exampleTargetPackage);
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("Example"); //$NON-NLS-1$
        setExampleType(sb.toString());
    }
}
```

##generatorConfigの設定
```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>
  <!-- targetRuntimeを上記のMyBatis3Exに設定する -->
  <context id="context1"  targetRuntime="com.example.demo.conf.MyBatis3Ex">
  
    <!-- pluginを追加する -->
	<plugin type="com.example.demo.conf.MyBatisPlugin" >
     <property name="custom" value="true"/>
    </plugin>
    
    <jdbcConnection connectionURL="jdbc:mysql://localhost/mysql" driverClass="com.mysql.jdbc.Driver" password="root" userId="root" />
    
    <javaModelGenerator targetPackage="com.example.demo.dto" targetProject="SpringBatchDemo/src/main/java" >
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>
    
    <sqlMapGenerator targetPackage="com.example.demo.mapper" targetProject="SpringBatchDemo/src/main/resources" >
     <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
    
    <javaClientGenerator targetPackage="com.example.demo.dao"  targetProject="SpringBatchDemo/src/main/java" type="XMLMAPPER">
   	 <property name="enableSubPackages" value="true" />
    </javaClientGenerator>
    
    <table schema="mysql" tableName="main" />
    
  </context>
</generatorConfiguration>
```

#実行結果

```MainDao.java
package com.example.demo.dao;

import com.example.demo.dto.MainDto;
import com.example.demo.dto.MainExample;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface MainDao {
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    long countByExample(MainExample example);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int deleteByExample(MainExample example);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int insert(MainDto record);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int insertSelective(MainDto record);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    List<MainDto> selectByExample(MainExample example);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    MainDto selectByPrimaryKey(Integer id);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int updateByExampleSelective(@Param("record") MainDto record, @Param("example") MainExample example);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int updateByExample(@Param("record") MainDto record, @Param("example") MainExample example);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int updateByPrimaryKeySelective(MainDto record);

    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table main
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    int updateByPrimaryKey(MainDto record);
}
```

```MainDto.java
package com.example.demo.dto;

import lombok.Data;

@Data
public class MainDto {
    /**
     *
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database column main.id
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    private Integer id;

    /**
     *
     * This field was generated by MyBatis Generator.
     * This field corresponds to the database column main.name
     *
     * @mbg.generated Mon Jan 17 23:38:28 JST 2022
     */
    private String name;
}
```