Spring Boot解説第12回(開発環境編：DB接続とMybatis)

みなさん、こんにちは！
情創 技術開発局の@TEBASAKIです。
今回を含めましてあと6回で開発環境編も終わりとなります。

今回はDB接続とMyBatisについて解説していきます。

# フォルダ構成の再掲
第8回にプロジェクトのフォルダ構成を載せましたが、
今回新たにMyBatisを使用するので、改めて掲載します。
また、少々修正した部分があるので、
第8回のほうも修正しておきました。

```text:フォルダ構成
src
  ┗ main
        ┣ java
        ┃    ┗ com
        ┃         ┗ qiita
        ┃                ┗ demo
        ┃                      ┣ dao(DB処理層)
        ┃                      ┃
        ┃                      ┣ domain(ドメインモデルを定義する層)
        ┃                      ┃      ┗ tbl(DB Bean)
        ┃                      ┃
        ┃                      ┣ config(Springの色々な設定をJavaコード上で行うファイル群)
        ┃                      ┃
        ┃                      ┣ service(トランザクション処理を管理する層)
        ┃                      ┃
        ┃                      ┣ web(WEB層-コントローラーを記載)
        ┃                      ┃   ┣ controller
        ┃                      ┃   ┃
        ┃                      ┃   ┗ form(画面 Bean)
        ┃                      ┃
        ┃                      ┗Application.java(エントリポイント)
        ┃
        ┗ resources
                   ┣ dao(MyBatisのxml格納)
                   ┃
                   ┣ META-INF
                   ┃       ┗ resources
                   ┃
                   ┣ public(共通のjsやcss)
                   ┃
                   ┣ static(静的ファイル)
                   ┃                  ┣ images(画像)
                   ┃                  ┃
                   ┃                  ┣ scripts(js)
                   ┃                  ┃
                   ┃                  ┣ styles(css)
                   ┃                  ┃
                   ┃                  ┗ (静的なHTMLファイル群)
                   ┃
                   ┗ templates(thymeleaf 拡張子html)
```

MyBatisのxmlファイルは上記の通り、
`src/main/resources/dao`に配置します。

ここに配置する理由ですが、warファイル作成時にwarファイルに含まれないためです。
ただし、後述するDaoConfig.java の factory.setMapperLocationsを変更するのであれば、
配置するパスを変更しても構いません。

# MyBatis
>MyBatis はカスタム SQL、ストアドプロシージャ、高度なマッピング処理に対応した
優れた永続化フレームワークです。 
MyBatisを使うことで、直接 JDBC を扱うコードを書いたり、
クエリ引数やクエリ結果を手動で設定する必要がほとんどなくなります。 
MyBatis の設定やデータベースレコードと Java オブジェクトの関連付けは、
XML またはアノテーションを使って行うことができます。

引用元：[MyBatis &#x2013; MyBatis 3 | イントロダクション](http://www.mybatis.org/mybatis-3/ja/index.html "MyBatis - MyBatis 3 | イントロダクション")

上記の通り、MyBatisとはJavaオブジェクトとSQLを紐付けるフレームワークです。
加えてSpring Bootとの連携を強くするMyBatis-Springというライブラリも使用します。
MyBatis-Springについてはこちらのページを参照してください。

参考：[mybatis-spring &#x2013; MyBatis-Spring | イントロダクション](http://www.mybatis.org/spring/ja/ "mybatis-spring - MyBatis-Spring | イントロダクション")

## build.gradleの変更
MyBatisおよびMyBatis-Springを使用するために、
build.gradleのdependenciesに以下の記述を追加します。

```groovy:build.gradle
dependencies {
    ...
    compile("org.mybatis:mybatis:3.3.0")
    compile("org.mybatis:mybatis-spring:1.2.3")
    ...
}
```

## mybatis-config.xml
先ほどMyBatisのxmlファイルは`src/main/resources/dao`に配置すると言いましたが、
MyBatisの設定ファイルであるmybatis-config.xmlは**`src/main/resources`**に配置します。

```xml:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="lazyLoadingEnabled" value="true" />
		<setting name="useColumnLabel" value="true" />
		<setting name="mapUnderscoreToCamelCase" value="false" />
	</settings>
</configuration>
```

ここで設定している項目の内容について説明します。
項目名の後ろには設定可能な値を記してあります。

- **lazyLoadingEnabled** ( true | false )

>Lazy Loading（遅延読み込み）の有効／無効を切り替えるグローバルな設定です。 
無効にした場合、association として指定されているデータは直ちに読み込まれます。
association 要素で fetchType 属性が指定されている場合はそちらの指定が優先されます。

- **useColumnLabel** ( true | false )

>列名の代わりに列ラベルを使用します。
ドライバによって動作が異なります。
ドライバのドキュメントを参照するか、両方のモードを試して動作を確認してください。

デフォルト値は true です。

- **mapUnderscoreToCamelCase** ( true | false )

>データベースにある A_COLUMN のようなアンダースコアを含む列を 
Camel Case の Java プロパティ aColumn に自動的にマッピングする機能の
有効／無効を切り替えます。

デフォルト値は false です。

設定可能な他の項目については下記のリンクを参照してください。

引用元：[MyBatis &#x2013; MyBatis 3 | 設定](http://www.mybatis.org/mybatis-3/ja/configuration.html "MyBatis - MyBatis 3 | 設定")

# DaoConfig.java
データベースに必要な設定情報をapplication.propertiesから読み込んで保存するクラスです。
`src/main/java/com/qiita/demo/config`に配置します。

```java:DaoConfig.java
package com.qiita.demo.config;

import java.io.IOException;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;

@Configuration
@MapperScan("com.qiita.demo.dao")
public class DataConfig {
  /**
   * SqlSessionFactoryBean格納クラス。
   *
   * <PRE>
  * DataSourceをSqlSessionFactoryBeanにセットします。
  * mybatisの設定情報をSqlSessionFactoryBeanにセットします。
   * </PRE>
   *
   * @return SqlSessionFactoryBean。
   */
  @Bean
  public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    ResourcePatternResolver resolver =
        ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader());
    // MyBatis のコンフィグレーションファイル
    factory.setConfigLocation(resolver.getResource("classpath:mybatis-config.xml"));
    // MyBatis で使用する SQL ファイル群(daoフォルダ内のサブフォルダ内も含んだ全てのxml)
    factory.setMapperLocations(resolver.getResources("classpath:dao/**/*.xml"));

    return factory;
  }
}
```

上記のコード内で設定している内容について説明していきます。

## Configクラス
Springではすべての設定をapplicationContext.xmlというファイルに記述していましたが、
Spring Bootでは設定をコード上で行うのが一般的なので、
Configクラスを作成してそこに記述します。
Configクラスの作成には下記の2つのアノテーションが使用できます。

- **`@Configuration`**

Bean設定クラスに付与します。
このクラス内のメソッドに`@Bean`を付与することで、
そのメソッドをBean生成メソッドとします。

- **`@Component`**

クラスに付与することで、そのクラスをBeanとして認識させます。

今回は前者の`@Configuration`を使用しています。

## ワイルドカードについて
ワイルドカードについてはご存じの方が多いかと思いますが、
`"classpath:dao/**/*.xml"`の「`*`」は任意の文字列にマッチします。
しかし、「`**`」については知らない方もいるのではないでしょうか？
(私も知りませんでした。)
これは**「深さ0のフォルダを含めたあらゆる深さのフォルダ」**
にマッチします。
つまり`"classpath:dao/**/*.xml"`は、
「classpath:daoフォルダ以下のサブフォルダ内を含めたすべてのxmlファイル」
にマッチします。

## MapperScanアノテーション
`@MapperScan`を使用するとメソッドとSQLの紐付の際に、
指定された階層を自動で検出してくれるようになります。
今回は`com.qiita.demo.dao`を指定しているので、
この階層については特別な操作をすることなく、メソッドとSQLが紐付けられます。

# 紐付けの実装例
MyBatisを用いたDB接続の流れとしては、
リクエストを受けたControllerがServiceとmapperを経由してDBにアクセスし、
返ってきた値を使用してレスポンスを返す、というイメージです。

DBから情報を取得するmapperから順に、
その内容について説明していきます。

## mapperを記述するxmlについて
`src/main/resources/dao`に置かれたxmlファイルにはmapperを記述します。
以下に簡単なサンプルを掲載します。

```xml:TestDao.xml
<!--?xml version="1.0" encoding="UTF-8" ?-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.qiita.demo.dao.TestDao">
    <select id="selectAll" resultType="com.qiita.demo.domain.tbl.TestBean">
        select * from demo
    </select>
</mapper>
```

このmapperと紐付くインターフェースは以下のように定義します。
ファイルは`src/main/java/com/qiita/demo/dao`に配置します。

```java:TestDao.java
/*
 * TestDao.java
 *
 */

package com.qiita.demo.dao;

import java.util.List;

import com.qiita.demo.domain.tbl.TestBean;

public interface TestDao {


  List<TestBean> selectAll();

}
```

取得した結果が格納されるTestBeanは以下のように定義します。
ファイルは`src/main/java/com/qiita/demo/domain/tbl`に配置します。

```java:TestBean.java
package com.qiita.demo.domain.tbl;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TestBean implements java.io.Serializable {

  private int id;
  private String name;

}
```

TestBeanクラスはlombokを用いてgetterおよびsetterを省略しています。
lombokについては今回のテーマからは外れてしまうので、
以下のサイトを参照してください。

参考：[Lombok - Qiita](http://qiita.com/yyoshikaw/items/32a96332cc12854ca7a3 "Lombok - Qiita")

上記の例ではテーブルdemoの中身をすべて取得するselect文と、
それと紐付くメソッド`selectAll`を定義しています。
`<mapper>`の`namespace`要素に紐付け先のインターフェースを指定します。
`<select>`は文字通りselect文の定義に使用されます。
`id`要素に紐付けるメソッド名を、`resultType`要素に返り値の型をそれぞれ指定します。
引数を指定する`parameterType`要素もありますが、
引数および返り値の型は紐付け先のメソッドと一致させる必要があるので、注意してください。

その他にも使用できる要素がありますが、詳細は以下のサイトを参照してください。

参考：[MyBatis &#x2013; MyBatis 3 | Mapper XML ファイル](http://www.mybatis.org/mybatis-3/ja/sqlmap-xml.html "MyBatis - MyBatis 3 | Mapper XML ファイル")

## Serviceの作成
インターフェースを定義したら、このメソッドを呼び出すServiceを作成します。

```java:TestService.java
package com.qiita.demo.service.test;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.qiita.demo.dao.TestDao;
import com.qiita.demo.domain.tbl.TestBean;


@Service
public class TestService {
  @Autowired
  private TestDao testDao;

  public List<TestBean> selectAll() {
    return testDao.selectAll();
  }



}
```

Serviceはmapper経由で取得した結果を返すだけで、
ほかには何もしていません。

## Controllerの作成
続いてServiceを呼び出すControllerを作成します。

```java:TestController.java
package com.qiita.demo.web.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.qiita.demo.domain.tbl.TestBean;
import com.qiita.demo.service.test.TestService;


@Controller
public class TestController {
   @Autowired
   private TestService testService;

  @RequestMapping("/test")
  @ResponseBody
  public String home() {
	  String returnStr = "";
	  List<TestBean> list = testService.selectAll();
	  for (TestBean testBean : list) {
		  returnStr += "id  :" + testBean.getId() + " ";
		  returnStr += "name:" + testBean.getName() + "<br>";
	  }
	  return returnStr;
  }



}

```
ControllerもService経由で取得したデータを画面に表示するだけで、
ほかに特別なことは一切やっていません。

以上のプログラムの流れについては、以下のサイトの説明が参考になります。
また、上記以外のデータ取得方法についても説明されています。

参考：[Spring4勉強会 第四回 - Qiita](http://qiita.com/yu_eguchi/items/5bdda6f6ee7baf441870 "Spring4勉強会 第四回 - Qiita")

# 実践
それでは実践して動作を確認しましょう。
今回使用するテーブルは第10回で使用したものと同じです。

| id | name |
|:-:|:-:|
| 1  | aaa  |
| 2  | bbb  |
| 3  | ccc  |


設定を終えたら、プロジェクトを右クリック→Gradleからすべてリフレッシュを行い、
bootRunで実行します。

実行後、http://localhost:8080/test/ にアクセスしてみましょう。
無事テーブルの全件取得が成功したことが確認できます。
![image](https://qiita-image-store.s3.amazonaws.com/0/116031/ce10ad5a-f91b-d768-fcde-d638ecb78684.png)



今回の解説はここまでです。

それではまたお会いしましょう！