前回の続きです。

今回は、MyBatis Generator を使って、POJO クラスや DAO インターフェースの作成を自動化してみましょう。

 

 

前回で、ひとまず 「初めての Spring Boot」 的な物ができました。今回は、データベースと MyBatis 関連の設定を詰めていきたいと思います。

 

データベースの作成と初期データの投入

Spring Boot は、/{プロジェクト名}/src/main/resources/ 直下に schema.sqldata.sql を用意しておくと、起動時に DBやテーブルの作成と、初期データの投入を自動的にしてくれる機能があります。

ここでは、schema.sql にDBの定義とテーブルの定義、data.sql に初期データ投入の DDL を記述します。

 

schema.sql の作成

パッケージエクスプローラーの src/main/resources を右クリックして New → Other から、General → File をクリックして [Next]

保存先が、firstbootapp/src/main/resources になっているのを確認して、ファイル名に schema.sql と入力して Finish

次にこのファイルを右クリックして、Open With → Text Editor で開いてください。

以下のような SQL を入力して、保存します。

CREATE DATABASE IF NOT EXISTS `mydb`;
USE `mydb`;

DROP TABLE IF EXISTS `phone_book`;
CREATE TABLE `phone_book` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(30) NOT NULL DEFAULT '',
    `phone_no` VARCHAR(30) NOT NULL DEFAULT '',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

SQL をみていただければわかるように、この SQL を実行すると、毎回データベースを新たに作り直し、テーブルも再定義するようになっています。このように書いておかないと、次回起動時にすでに存在しているテーブルを CREATE TABLE しようとして、起動に失敗します。

毎回起動するたびに初期化されることに不安を覚えるかもしれません。でも安心してください、実運用時にはこの初期化処理を無効に できます。

 

data.sql の作成

次に同様に、同じ場所に data.sql を作成し、次のような内容を入力して保存します。

USE `mydb`;

DELETE FROM `phone_book`;
INSERT INTO `phone_book` (`id`, `name`, `phone_no`) VALUES
    (1, '雑廉堂', '090-1234-5678'),
    (2, 'Studio Rough and Cheap', '06-1234-5678');

簡易な電話帳を模した phone_book というテーブルに、初期データを登録しています。

 

application.properties にプロパティを追加

application.properties に次の項目を追加することで、サービス起動時に、データベースの初期化を走らせるかどうかを、制御することができます。

spring.datasource.initialization-mode=always

 

逆にいえば、この項目がなければ、データベースは初期化されなくなります

 

Spring Boot を起動して、DDL を走らせる

ここで、一旦 Spring Boot を起動して、先の DDL が正常に実行できるか確認してみます。

特に何もなく前回確認したページが確認できればよいですが、起動時にエラーや例外が発生していると、ページの確認以前にサービスが起動しません。

エラーが発生した場合は、起動時のログを確認しつつ、

  • application.properties の DBと接続設定が間違えていないかどうか
  • SQL の入力ミスがないか

をよく確認してください。

正常にエラーもなく起動できたら、MySQL のサーバーにログインして、データベースが作成されているかどうか念のために確認します。

以下は、MariaDB での確認例です。

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydb               |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.004 sec)

MariaDB [(none)]> use mydb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mydb]> select * from phone_book;
+----+------------------------+---------------+
| id | name                   | phone_no      |
+----+------------------------+---------------+
|  1 | 雑廉堂                 | 090-1234-5678 |
|  2 | Studio Rough and Cheap | 06-1234-5678  |
+----+------------------------+---------------+
2 rows in set (0.001 sec)

このように、データベース mydb が作成されて、テーブル phone_book にレコードが2件追加されていれば成功です。

 

MyBatis + MyBatis Generator

では、次に MyBatis Generator を使って、Spring Boot 側からデータベースにアクセス出来るように準備していきます。

 

MyBatis Generator をインストール

MyBatis Generator を使用するには、

  • Maven で依存関係を追加する
  • Eclipse マーケットプレイスからプラグインをインストールする

の二通りの方法がありますが、今回はマーケットプレイスからプラグインをインストールしてみます。

メニューの Help → Eclipse Market Place.. をクリックして、マーケットプレイスを開きます。

検索欄に " mybatis " 等と入力して Enter キーで検索を開始します。

以下のように、MyBatis Generator 1.4.0 とでてきたら、[ Install ] ボタンをクリックしてインストールします。

マーケットプレイスで MyBatis Generator を検索

マーケットプレイスで MyBatis Generator を検索

 

generatorConfig.xml を作成

次に、パッケージエクスプローラーの src/main/resources を右クリックして New → Other から、MyBatis → MyBatis Generator Configuration File をクリックして [ Next ]

  • Location: /firstbootapp/src/main/resources
  • File name: generatorConfig.xml

として、[ Finish ] をクリックします。

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="mydb"  targetRuntime="MyBatis3">
        <property name="autoDelimitKeyWords" value="true" />
        <property name="beginningDelimiter" value="`" />
        <property name="endingDelimiter" value="`" />
        <jdbcConnection
            connectionURL="jdbc:mysql://192.168.10.147:3306/mydb"
            driverClass="com.mysql.cj.jdbc.Driver"
            password="actua1passw0rd"
            userId="someuser" />
        <javaModelGenerator
            targetPackage="org.ranc.firstbootapp.domain.model"
            targetProject="firstbootapp/src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <sqlMapGenerator
            targetPackage="org.ranc.firstbootapp.domain.repository"
            targetProject="firstbootapp/src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <javaClientGenerator
            targetPackage="org.ranc.firstbootapp.domain.repository"
            targetProject="firstbootapp/src/main/java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <table tableName="phone_book" domainObjectName="PhoneBookModel"
            mapperName="PhoneBookRepository"
            enableInsert="true"
            enableSelectByPrimaryKey="true"
            enableSelectByExample="true"
            enableUpdateByPrimaryKey="true"
            enableUpdateByExample="true"
            enableDeleteByPrimaryKey="true"
            enableDeleteByExample="true"
            enableCountByExample="true"
            selectByExampleQueryId="true"
            modelType="flat" >
            <columnRenamingRule searchString="^class$" replaceString="clazz"/>
            <columnOverride column="id" delimitedColumnName="true"/>
            <columnOverride column="name" delimitedColumnName="true"/>
        </table>
    </context>
</generatorConfiguration>

 

順は前後しますが、簡単に内容の説明を・・(急いでいる方はここを飛ばして、こちら へ)

< context >

<context id="mydb"  targetRuntime="MyBatis3">
    <property name="autoDelimitKeyWords" value="true" />
    <property name="beginningDelimiter" value="`" />
    <property name="endingDelimiter" value="`" />

context はデータベース単位で複数定義できます。id がその識別子になります。データベース内のテーブルに関する定義などを含め、諸々がこの<context></context> に入ります。
targetRuntime は、ここでは MyBatis3 に設定しています。これは、以前の MyBatis ではなかったと思うのですが、使用できる Runtime は以下の通りになります。

  • MyBatis3DynamicSql: 最新の MyBatis Generator イチオシのランタイムです。DynamicSql では、極力 SQL 文を遠ざけたランタイムみたいですね。
  • MyBatis3Kotlin: これは上記の Kotlin サポートです。
  • MyBatis3: 従来までの MyBatis Generator の動作を行います。DAO インターフェース、XML マッパーファイル、POJO クラスに対応した Example クラスを生成します。
  • MyBatis3Simple: これは MyBatis3 と同じですが、Example クラスを生成しません。
autoDelimitKeywords
true に設定すると、SQLキーワードがテーブル内の列名に使われている場合、区切り文字で列名を区切ります。デフォルトは false
beginningDelimiter
区切り文字の開始文字を指定します。MySQL であれば、` ですし、MS SQL Server であれば [ になります。
endingDelimiter
区切り文字の終了文字を指定します。MySQL であれば、` ですし、MS SQL Server であれば ] になります。

 

<jdbcConnection>

<jdbcConnection
    connectionURL="jdbc:mysql://192.168.10.147:3306/mydb"
    driverClass="com.mysql.cj.jdbc.Driver"
    password="actua1passw0rd"
    userId="someuser" />

これは MyBatis Generator に対して渡す接続情報です。ここは特に説明の必要はないですね。

 

<javaModelGenerator>

<javaModelGenerator
    targetPackage="org.ranc.firstbootapp.domain.model"
    targetProject="firstbootapp/src/main/java">
    <property name="enableSubPackages" value="true" />
    <property name="trimStrings" value="true" />
</javaModelGenerator>

テーブルに対応した POJO クラスや動的にクエリを発行するために使用する Example クラスを生成するために使われます。
targetPackage は POJO クラスの出力先のパッケージ名。
targetProject はパッケージの起点となるプロジェクトのパスです。

enableSubPackages
テーブルのスキーマごとにパッケージを作成するかどうかを指定します。Microsoft SQL Server の dbo などがそれに該当すると思われます。例えばテーブルが sample_db.dbo.sample_table であれば、org.ranc.firstbootapp.domain.model.dbo.SampleTable にオブジェクトが出力されます。
trimStrings
プロパティは true にすると、データベースから文字列の値を受け取るときに、trim() を必ず呼び出すように設定されます。

<sqlMapGenerator>

<sqlMapGenerator
    targetPackage="org.ranc.firstbootapp.domain.repository"
    targetProject="firstbootapp/src/main/resources">
    <property name="enableSubPackages" value="true" />
</sqlMapGenerator>

SQL Map Generator は、テーブルと POJO クラスのマッピング、DAO インターフェースに対してマップされる SQL を含んだ XML ファイルを生成します。

targetPackage と、targetProject は、先の javaModelGenerator と同じになります。ただ、出力されるのは XML ファイルを出力するので、出力されるパスは projectname/src/main/resources 配下にします。

 

<javaClientGenerator>

<javaClientGenerator
    targetPackage="org.ranc.firstbootapp.domain.repository"
    targetProject="firstbootapp/src/main/java" type="XMLMAPPER">
    <property name="enableSubPackages" value="true" />
</javaClientGenerator>

Java client Generator は、作成した POJO クラスと XML マッパーファイルを簡単に利用できる DAO インターフェースクラスを生成します。
targetPackage は POJO クラスの出力先のパッケージ名。
targetProject はパッケージの起点となるプロジェクトのパスです。
type は <context> の contextRuntime が MyBatis3 の場合は次のようになります。

  • ANNOTATEDMAPPER: DAO インターフェースクラスのアノテーションでSQLを定義できます。XML ファイルは生成されません。
  • MIXEDMAPPER: アノテーションの付与された DAO インターフェースと XML ファイルの両方を生成します。
  • XMLMAPPER: DAOインターフェースにアノテーションは付与されず、XML ファイルは生成されます。

アノテーションが付与される場合は、以下に示すようにメソッドのアノテーションの値として SQL が定義されますが、XMLMAPPER の場合は、XML ファイルに定義されます。

@Select({
    "select",
    "`id`, `name`, phone_no",
    "from phone_book",
    "where `id` = #{id,jdbcType=INTEGER}"
}) @ResultMap("org.ranc.bbspre.domain.repository.PhoneBookRepository.BaseResultMap")
PhoneBookModel selectByPrimaryKey(Integer id);

これについては、実際それぞれの type で生成させた上で判断してみるのが良いと思います。

例のように列の少ないテーブルであれば、アノテーションで運用したほうが便利そうですが、数多くの列を持つテーブルやビューの場合は、インターフェースの内容が見づらくなります。

 

< table >

<table tableName="phone_book" domainObjectName="PhoneBookModel"
    mapperName="PhoneBookRepository"
    enableInsert="true"
    enableSelectByPrimaryKey="true"
    enableSelectByExample="true"
    enableUpdateByPrimaryKey="true"
    enableUpdateByExample="true"
    enableDeleteByPrimaryKey="true"
    enableDeleteByExample="true"
    enableCountByExample="true"
    selectByExampleQueryId="true"
    modelType="flat" >
    <columnRenamingRule searchString="^class$" replaceString="clazz"/>
    <columnOverride column="id" delimitedColumnName="true"/>
    <columnOverride column="name" delimitedColumnName="true"/>
</table>

<table> 要素では、そのテーブルのオブジェクト名や、生成する SQL の指定を行います。
tableName はテーブル名を指定。
domainObjectNameは、生成する POJO クラス名を指定します。
mapperName は XML マッパーファイルと DAO インターフェースの名前を指定します。
enable.. で始まる属性はそれぞれ、どのタイプの SQL を作成するか、
modelType は生成する POJO クラスの形式で、以下のとおりです。

  • flat: 単一の POJO クラスを生成されます。
  • hierarchical: プライマリキーのクラスと、その他のフィールドを含んだクラスが生成されます。
  • conditional: は hierarchical に似ていますが、プライマリキーが単一フィールドの場合は、クラスが分割されません。

また、この他にも、
schemacatalog といった属性があります。これは、使用しているデータベースの仕様に依存します。

columnRenamingRule
テーブルの列名を、POJO クラスのフィールド名に置き換える際のネーミングルールを定義します。ここでは、class という列名があった場合は clazz に置き換えます。
columnOverride
autoDelimitKeywords で置き換えきれない列名をここで手動で指定します。ここでは、idname については、生成する SQL 内で区切り文字で区切るように指定しています。

 

MyBatis Generator を実行する

それでは、実際に MyBatis Generator を実行してみます。

パッケージエクスプローラーのパッケージ名を右クリックして、Run as → Run Configuration .. から、

  1. MyBatis Generator をクリック
  2. file をクリックして実行構成を新規作成し、
  3. Workspace... をクリックして先ほど設定した、generatorConfig.xml を選択した後、
  4. Run で実行します。
MyBatis Generator, Run Configuration

MyBatis Generator, Run Configuration

 

次のようなログが出力されるはずです。

MyBatis Generator, Generate log

MyBatis Generator, Generate log

 

この記事で書いている通りに実行すれば、エラーになることは無いと思いますが、もしもエラーで失敗した場合は、ログを参考に必要箇所を修正してください。

MyBatis Generator 実行後のプロジェクトの構成は次のようになります。

MyBatis Generator 実行後のプロジェクト構成

MyBatis Generator 実行後のプロジェクト構成

赤丸で囲った部分が、MyBatis Generator で作成したファイルです。

 

データベースの連携を確認する

 

DAO インターフェースにアノテーションを降る

生成されたままの DAO インターフェースでは、Spring Boot で DAO を紐付けられないので、@Mapper アノテーションを付与しておきます。

package org.ranc.firstbootapp.domain.repository;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.ranc.firstbootapp.domain.model.PhoneBookModel;
import org.ranc.firstbootapp.domain.model.PhoneBookModelExample;

@Mapper
public interface PhoneBookRepository {
    :
    :
}

 

生成された POJO クラスを修正する

ここで、生成された POJO クラス、PhotoBookModel.java を開いて、toString() メソッドをおバーライドします。

ソースを開いて、STS4 のメニューから、Source → Generate toString() でもいいですし、Lombok の @ToString アノテーションでもいいでしょう。

 

コントローラを修正する

次に、HelloController を次のように修正します。

package org.ranc.firstbootapp.app;

import org.ranc.firstbootapp.domain.model.PhoneBookModel;
import org.ranc.firstbootapp.domain.repository.PhoneBookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    /* (1) */
    @Autowired
    PhoneBookRepository phoneBookRepository;

    @GetMapping("/")
    public String hello(Model model) {
        /* (2) */
        PhoneBookModel phoneBookModel = phoneBookRepository.selectByPrimaryKey(1);
        model.addAttribute("phoneBook", phoneBookModel);
        model.addAttribute("greeting", "Hello, World!");
        return "hello";
    }
}

修正点は次のとおりです。

  1. @Autowired アノテーションで、PhoneBookRepository の実装クラスを DI しています。
  2. DAOインターフェースの実装クラスから、id = 1 のレコードを取得して、POJO オブジェクトに代入し、Model に渡しています。

 

ビューを修正する

最後に、ビューを修正します。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Welcome!</title>
</head>
<body>
  <h1>Welcome!</h1>
  <p>
    <span th:text="${greeting}">greeting</span>
  </p>
  <p>
    <!-- データベースから取得したレコードをここに表示します -->
    <span th:text="${phoneBook}">phoneBook がここに表示されます</span>
  </p>
</body>
</html>

 

Spring Boot を実行する

それでは最後にもう一度、Spring Boot を起動し、http://localhost:8080/ にアクセスしてください。

以下のように表示されれば、成功です。

20200208-122404-520

DB連携が成功した場合

 

まとめ

本来は一つの記事で出したかったのですが、思ったよりも長くなってしまったので、前後の2回に分けました。

以前は、自分自身で DataSource クラスを作ってデータベースとのコネクションを張ったり、DAO クラスにたくさんの SQL を String の文字列として書いていたことを考えると、これでめちゃくちゃ楽になりました。

MyBatis では他にも 「動的なSQL」 を実行出来るようにするために、POJO クラスと合わせて、Example クラスの生成もしてくれます。これも使うと割に便利なんですが、それはまたの機会にということで・・。

それでは、お疲れさまでした。


 
記事を共有する

zaturendo

中小企業社内SE。

6件のコメント

ddrhiro · 2020年5月5日 5:45 AM

はじめまして。Javaは殆ど初心者のものです。
とある課題に追われ、SpringBoot + Thymeleaf + Mybatisでの実装方法を探しておりました。
結果として、最後の出力まで無事にできました。ありがとうございました。
作業の流れはもとより、写真下部にある解説がとても丁寧に書かれているなと拝見させて頂きながら感動していました(当方、他言語での開発経験あるものの、Javaのフレームワークやライブラリについてはほぼ無知です)。
他のサイトを20~30程度見、色々真似てみたのですが、情報が断片的or操作しても何が悪いのかわからずで困っておりました。
また、機会があれば別のアプリなども記事を書いて頂けると助かります。
楽しみにしております。
※非常に参考になりましたので、コメントを残させて頂きました。

    zaturendo · 2020年5月7日 9:35 PM

    記事を読んでくださりありがとうございます。
    私もいくつものサイトを参考にしようと思ったのですが、結局うまく行かなかったのがこの記事を各きっかけになりました。
    今後もいろいろ記事書いていきますので、よろしくおねがいします。

biginner · 2020年5月19日 2:33 PM

Java初心者のものです。springbootとMybatisを使用した操作に手こずっておりましたが、
こちらの記事のおかげで進捗を得る事が出来ました。
大変にわかりやすい記事で、参考になりました。

    zaturendo · 2020年5月22日 7:26 PM

    参考にして頂きありがとうございます。
    今後ともよろしくお願いします。

K · 2020年6月26日 11:05 AM

本当にとても親切な手順で助かりました。

    zaturendo · 2020年6月30日 5:03 PM

    ありがとうございます。
    今後ともよろしくお願いします。

コメントを残す

メールアドレスが公開されることはありません。