MyBatis GeneratorでMapper&Entityを自動生成しよう!


このページについて

Java開発におけるDBのMapperやEntityの自動生成を試みたい。
⇒Mybatis Generatorを使ってみよう!というものです。


MyBatis Generatorって何?

JavaプログラムとDB間のデータを相互に変換授受してくれる通訳さんです。
(Object-Relational Mapper(O/RM)の一種)
で、今回やりたいのは、その"Generate"(=Java ObjectとMapperの自動生成)機能。


開発方針

SpringBoot&Gradleの上にMyBatisを加える方針とします。
環境構築からの方は以下を参考ください。
SpringBoot入門...いいから動確だ!

環境 サービス/バージョン
実行環境 Windows10
開発環境 eclipse Oxygen.2 Release (4.7.2) Java版
開発言語 Java 8
DB PostgreSQL 10
Framework SpringBoot 2.1.3
依存関係 PostgreSQL、Thymeleaf、Web

※Thymeleaf、Webは最後の動作確認で使います


eclipseにMybatis Generatorをインストールする

eclipse→「ヘルプ」にあるマーケットプレイスから取ってきましょう。


プロジェクトにMyBatisを迎え入れる

Gradleにmybatis-spring-boot-starterを追記します。
その後Graldeのリフレッシュを実行して依存関係を更新しましょう。

build.gralde
plugins {
    id 'org.springframework.boot' version '2.1.3.RELEASE'
    id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.lab.app.ketman'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    // 追記
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.0'
    //ここまで
    runtimeOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'org.postgresql:postgresql'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}


DB側にテーブルを用意する

お試し用のテーブルを適当に作る。

CREATE TABLE orange.songs
(
    title character varying(30) NOT NULL,
    release_date date,
    sales integer,
    winning_flag boolean,
    song_id character(4) NOT NULL,
    CONSTRAINT song_id PRIMARY KEY (song_id)
)
WITH (
    OIDS = FALSE
);

ALTER TABLE orange.songs
    OWNER to postgres;

実行構成ファイルを作成する

自動生成の対象とするテーブルや、生成物の格納先などを定義するxmlファイルを作成します。
src/main/resources配下のディレクトリに格納します。

generationConfig.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="PostgresTables" targetRuntime="MyBatis3">

    <!-- スキーマ情報を取得する DB への接続設定 -->
    <jdbcConnection driverClass="org.postgresql.Driver"
        connectionURL="jdbc:postgresql://localhost:5432/※スキーマ名※"
        userId="※ユーザID※"
        password="※パスワード※">
    </jdbcConnection>

    <!-- SELECT 結果等を格納するドメインモデルを生成する設定 -->
    <javaModelGenerator targetPackage="com.lab.app.ketman.mybatis.domain" targetProject="※プロジェクト名※/src/main/java">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <!-- SQL 設定が記述された XML を生成する設定 -->
    <sqlMapGenerator targetPackage="com/lab/app/ketman/mybatis" targetProject="※プロジェクト名※/src/main/resources">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
    <!-- マッパークラスを生成する設定 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.lab.app.ketman.mybatis.mapper" targetProject="※プロジェクト名※/src/main/java">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <!-- コードを生成するテーブルを指定 -->
    <table schema="orange" tableName="songs">
      <property name="useActualColumnNames" value="true" />
    </table>

  </context>
</generatorConfiguration>

実行する

「実行」→「実行構成」→「MyBatis Generator」
構成ファイルに先ほどのgeneratorConfig.xmlを選択して実行。
generationConfig.xmlで指定したディレクトリに以下のファイルが出来ているはず。

  • Songs.java
    • いわゆるDTO(Data Transfer Object)
    • テーブルのカラムを表した変数とgetter/setterを持っています。
  • SongsMapper.java
    • insertやdeleteなどCRUDを持っているクラスです。
  • SongsExample.java
    • SongMapperの引数として与えることで、where句の様に働きます。
  • SongsMapper.xml
    • 引用:MyBatisを使ってみよう
      • DBから取得した値をどのエンティティに格納するかのマッピング定義
      • Javaから渡された値をどのように解釈するかのマッピング定義
      • Mapperインターフェースに宣言されたメソッドに対応するSQL


画面に表示してみよう

適当にinsertしておく。

INSERT INTO orange.songs(
    title, release_date, sales, winning_flag, song_id)
    VALUES ('キリキリマイ', '2003-06-04', 20206, false, '0001');
COMMIT;

今回はSpringBoot上で動かすことを前提としているため、
生成されたMapperクラスに「@Mapper」アノテーションを追記します。

SongsMapper.java
@Mapper
public interface SongsMapper {

ControllerからMapperを呼び出します。

SampleController.java
package com.lab.app.ketman.controller;

// importは省略

@Controller
// Mapperクラスが格納されているパスを明記する必要がある
@MapperScan(basePackages = { "com.lab.app.ketman.mybatis.mapper.*"})
public class SampleController {
    @Autowired
    SongsMapper sm;

    @RequestMapping("/index")
    public String index(Model model) {
        Songs s = sm.selectByPrimaryKey("0001");    // 検索結果をSongsオブジェクトに格納
        model.addAttribute("title", s.getTitle());  // getterで値を取り出す
        return "index";
    }
}


addAttributeした値をHTML側から取得する。
(今回はSpringBoot上で動かすことを前提としているため、
推奨であるThymeleafを使用しています。)

index.html
<h1 th:text="${title}"></h1>

無事に出力することが出来ました。


補足①

最初、SongsMapper.xml と SongsMapper.javaの紐付けが上手くいかず、
Invalid bound statement (not found)エラーを吐いてしまいました。
今回はxmlとjava側のパスを合わせることで解消出来ました。

以下引用:Spring Boot + Mybatisのmapper-locations

Spring Boot + Mybatisでは、Mapper XMLとMapperクラスのパスが同じ場合、
上記の指定をしなくてもMybatisがMapper XMLを読み込んでくれます。
Mapper XMLとMapperクラスのパスが同じ」のイメージは以下のような形です。
・src/main/java/jp/co/arsware/example/mapper/CityMapper.java
・src/main/resources/jp/co/arsware/example/mapper/CityMapper.xml
※co以下が同じパスであれば読み込んでくれる

ただし、application.propertiesでmapper-locationsを定義しても解消されず…。
この点は不明。


補足②

Controllerなどパッケージの構成についても気を使う必要があります。
こちらの記事が読みやすかったです。
Spring BootでNo qualifying bean of typeが出た時の原因と対応

※①、②とも同じ投稿者さんでした…。多謝。


おわりに

本稿ではMybatis Generatorを用いた稼働の確認までを扱いました。
XMLやExample.javaをメンテナンスすることで結合や動的SQLなんかも対応できると思います。