【MyBatis Generator 1.4.1】Spring BootでMyBatis Dynamic SQLを使用する
概要
MyBatis Generatorの公式ページWhat’s New in MyBatis Generatorにあるように、バージョン1.4.0以降ではデフォルトのランタイムがMyBatis Dynamic SQLになりました。以前このブログに書いたMyBatis Generator 1.3.7の検証記事Spring BootでMyBatis Generator のGradleプラグインを使用するにあるような従来のExampleが廃止され、ラムダを使ってSQLを組み立てる形式となりました。
この記事では以下の環境でMyBatis Dynamic SQLを使う場合の設定を紹介します。
- Spring Boot 2.6.7
- Java 17
- PostgreSQL 13.0
- MyBatis-Generator-Core 1.4.1
build.gradleへ存関係を追加
Spring Bootプロジェクトのbuild.gradleへMyBatis Generatorプラグインと、MyBatis Generator本体であるMyBatis Generator Coreへの依存関係を追加します。
また、Spring BootでMyBatisを使用するときにはMyBatis-Spring-Boot-Starterを使用すると設定が簡単になるのであわせてbuild.gradleへ追加します。
build.gradle中のgeneratorConfig.xmlがMyBatist Generatorの設定ファイルです。Spring Bootプロジェクト内に追加する必要があります。
plugins {
id 'org.springframework.boot' version '2.6.7'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
// MyBatis Generatorプラグイン
id "com.thinkimi.gradle.MybatisGenerator" version "2.4"
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
mybatisGenerator
}
// MyBatis Generatorの設定
mybatisGenerator {
verbose = true
// 設定ファイルのパス
configFile = "src/main/resources/generatorConfig.xml"
dependencies {
mybatisGenerator 'org.mybatis.generator:mybatis-generator-core:1.4.1'
mybatisGenerator 'org.postgresql:postgresql'
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
implementation 'org.mybatis.generator:mybatis-generator-core:1.4.1'
implementation 'org.mybatis.dynamic-sql:mybatis-dynamic-sql:1.4.0'
implementation 'org.mybatis:mybatis-typehandlers-jsr310:1.0.2'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.2.2'
}
tasks.named('test') {
useJUnitPlatform()
}
MyBatis Generatorの設定ファイルを作成
generatorConfig.xmlを作成します。このファイルにはMyBatis Generator自体の設定と接続先DB、MyBatis Generatorの実行対象テーブルを記述します。
<?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" >
<!-- 生成される Java ファイルのコメントに日付を付与しない -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="org.postgresql.Driver" connectionURL="jdbc:postgresql://localhost:5432/TestDB"
userId="xxxxx" password="xxxxx"/>
<!-- PostgreSQLのTIMESTAMP型をJavaのLocalDateTimeへマッピングする -->
<javaTypeResolver>
<property name="useJSR310Types" value="true"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="com.example" targetProject="src/main/java"/>
<table tableName="book"></table>
</context>
</generatorConfiguration>
動作確認
下記のDDLで作成したbookテーブルに対して上記のgeneratorConfig.xmlの設定でMyBatis Generatorを実行してみます。
create table book (
id integer not null,
book_title text not null,
category_id integer not null,
updated_at timestamp with time zone not null
);
生成されるファイルは以下の3つです。
- Book.java(エンティティクラス)
- BookDynamicSqlSupport.java (テーブル、カラム情報)
- BookMapper.java (マッパークラス)
Book.javaは以下のようになります。
package com.example;
import javax.annotation.Generated;
import java.time.LocalDateTime;
public class Book {
@Generated("org.mybatis.generator.api.MyBatisGenerator")
private Integer id;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
private String bookTitle;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
private Integer categoryId;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
private LocalDateTime updatedAt;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public Integer getId() {
return id;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public void setId(Integer id) {
this.id = id;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public String getBookTitle() {
return bookTitle;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public void setBookTitle(String bookTitle) {
this.bookTitle = bookTitle;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public Integer getCategoryId() {
return categoryId;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public void setCategoryId(Integer categoryId) {
this.categoryId = categoryId;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
}
BookDynamicSqlSupport.javaは以下のようになります。
package com.example;
import org.mybatis.dynamic.sql.AliasableSqlTable;
import org.mybatis.dynamic.sql.SqlColumn;
import javax.annotation.Generated;
import java.sql.JDBCType;
import java.time.LocalDateTime;
public final class BookDynamicSqlSupport {
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final Book book = new Book();
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final SqlColumn<Integer> id = book.id;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final SqlColumn<String> bookTitle = book.bookTitle;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final SqlColumn<Integer> categoryId = book.categoryId;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final SqlColumn<LocalDateTime> updatedAt = book.updatedAt;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final class Book extends AliasableSqlTable<Book> {
public final SqlColumn<Integer> id = column("id", JDBCType.INTEGER);
public final SqlColumn<String> bookTitle = column("book_title", JDBCType.VARCHAR);
public final SqlColumn<Integer> categoryId = column("category_id", JDBCType.INTEGER);
public final SqlColumn<LocalDateTime> updatedAt = column("updated_at", JDBCType.TIMESTAMP);
public Book() {
super("book", Book::new);
}
}
}
BookMapper.javaは以下のようになります。
package com.example;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.dynamic.sql.BasicColumn;
import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter;
import org.mybatis.dynamic.sql.select.CountDSLCompleter;
import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.update.UpdateDSL;
import org.mybatis.dynamic.sql.update.UpdateDSLCompleter;
import org.mybatis.dynamic.sql.update.UpdateModel;
import org.mybatis.dynamic.sql.util.SqlProviderAdapter;
import org.mybatis.dynamic.sql.util.mybatis3.*;
import javax.annotation.Generated;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import static com.example.BookDynamicSqlSupport.*;
import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
@Mapper
public interface BookMapper extends CommonCountMapper, CommonDeleteMapper, CommonInsertMapper<Book>, CommonUpdateMapper {
@Generated("org.mybatis.generator.api.MyBatisGenerator")
BasicColumn[] selectList = BasicColumn.columnList(id, bookTitle, categoryId, updatedAt);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@SelectProvider(type=SqlProviderAdapter.class, method="select")
@Results(id="BookResult", value = {
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="book_title", property="bookTitle", jdbcType=JdbcType.VARCHAR),
@Result(column="category_id", property="categoryId", jdbcType=JdbcType.INTEGER),
@Result(column="updated_at", property="updatedAt", jdbcType=JdbcType.TIMESTAMP)
})
List<Book> selectMany(SelectStatementProvider selectStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@SelectProvider(type=SqlProviderAdapter.class, method="select")
@ResultMap("BookResult")
Optional<Book> selectOne(SelectStatementProvider selectStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default long count(CountDSLCompleter completer) {
return MyBatis3Utils.countFrom(this::count, book, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int delete(DeleteDSLCompleter completer) {
return MyBatis3Utils.deleteFrom(this::delete, book, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int deleteByPrimaryKey(Integer id_) {
return delete(c ->
c.where(id, isEqualTo(id_))
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int insert(Book row) {
return MyBatis3Utils.insert(this::insert, row, book, c ->
c.map(id).toProperty("id")
.map(bookTitle).toProperty("bookTitle")
.map(categoryId).toProperty("categoryId")
.map(updatedAt).toProperty("updatedAt")
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int insertMultiple(Collection<Book> records) {
return MyBatis3Utils.insertMultiple(this::insertMultiple, records, book, c ->
c.map(id).toProperty("id")
.map(bookTitle).toProperty("bookTitle")
.map(categoryId).toProperty("categoryId")
.map(updatedAt).toProperty("updatedAt")
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int insertSelective(Book row) {
return MyBatis3Utils.insert(this::insert, row, book, c ->
c.map(id).toPropertyWhenPresent("id", row::getId)
.map(bookTitle).toPropertyWhenPresent("bookTitle", row::getBookTitle)
.map(categoryId).toPropertyWhenPresent("categoryId", row::getCategoryId)
.map(updatedAt).toPropertyWhenPresent("updatedAt", row::getUpdatedAt)
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default Optional<Book> selectOne(SelectDSLCompleter completer) {
return MyBatis3Utils.selectOne(this::selectOne, selectList, book, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default List<Book> select(SelectDSLCompleter completer) {
return MyBatis3Utils.selectList(this::selectMany, selectList, book, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default List<Book> selectDistinct(SelectDSLCompleter completer) {
return MyBatis3Utils.selectDistinct(this::selectMany, selectList, book, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default Optional<Book> selectByPrimaryKey(Integer id_) {
return selectOne(c ->
c.where(id, isEqualTo(id_))
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int update(UpdateDSLCompleter completer) {
return MyBatis3Utils.update(this::update, book, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
static UpdateDSL<UpdateModel> updateAllColumns(Book row, UpdateDSL<UpdateModel> dsl) {
return dsl.set(id).equalTo(row::getId)
.set(bookTitle).equalTo(row::getBookTitle)
.set(categoryId).equalTo(row::getCategoryId)
.set(updatedAt).equalTo(row::getUpdatedAt);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
static UpdateDSL<UpdateModel> updateSelectiveColumns(Book row, UpdateDSL<UpdateModel> dsl) {
return dsl.set(id).equalToWhenPresent(row::getId)
.set(bookTitle).equalToWhenPresent(row::getBookTitle)
.set(categoryId).equalToWhenPresent(row::getCategoryId)
.set(updatedAt).equalToWhenPresent(row::getUpdatedAt);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int updateByPrimaryKey(Book row) {
return update(c ->
c.set(bookTitle).equalTo(row::getBookTitle)
.set(categoryId).equalTo(row::getCategoryId)
.set(updatedAt).equalTo(row::getUpdatedAt)
.where(id, isEqualTo(row::getId))
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int updateByPrimaryKeySelective(Book row) {
return update(c ->
c.set(bookTitle).equalToWhenPresent(row::getBookTitle)
.set(categoryId).equalToWhenPresent(row::getCategoryId)
.set(updatedAt).equalToWhenPresent(row::getUpdatedAt)
.where(id, isEqualTo(row::getId))
);
}
}