SpringBootで、いろんなO/Rマッパー。

SpringBootでJDBCを使ってDBとのやり取りを行う際、「O/Rマッパー」という機能使うことでコードの可読性が上がるみたいなことを学んだのでその備忘録。

## O/Rマッパーを使わないコード。
・以下はUser情報を全件取得する場合を想定したコード。

```java:UserDAO.java

// JdbcTemplateのインスタンス生成。SpringBootのJDBCを使う場合はこれを使う。
@Autowired
JdbcTemplate jdbc;

public List<User> selectMany() throws DataAccessException {

        // M_USERテーブルのデータを全件取得（queryForList）
    	// map→keyとvalueの組み合わせ。それをListで受け取る。
        List<Map<String, Object>> getList = jdbc.queryForList("SELECT * FROM m_user");

        // 結果返却用の変数
        List<User> userList = new ArrayList<>();

        // 取得したデータを結果返却用のListに格納していく
        for (Map<String, Object> map : getList) {
        	        	
            //Userインスタンスの生成
            User user = new User();

            // Userインスタンスに取得したデータをセットする
            user.setUserId((String) map.get("user_id")); //ユーザーID
            user.setPassword((String) map.get("password")); //パスワード
            user.setUserName((String) map.get("user_name")); //ユーザー名
            user.setBirthday((Date) map.get("birthday")); //誕生日
            user.setAge((Integer) map.get("age")); //年齢
            user.setMarriage((Boolean) map.get("marriage")); //結婚ステータス
            user.setRole((String) map.get("role")); //ロール

            //結果返却用のListに追加
            userList.add(user);
        }
        return userList;
    }

```

こんな感じです。簡単に説明すると、queryForListメソッドでSQLを発行しレコードを取得する。DBから取得した際、Map型（こんな形→｛USER_ID=test1 ...｝）で受け取るが、実際にユーザはたくさんいるのでそれを更に**List型で受け取る**。
そして取得したデータを拡張for文で１ユーザづつ取得してUserインスタンスを生成。
最後に作成したインスタンスをListに代入すればユーザ一覧の完成。

## O/Rマッパーを使う。
・そもそもO/Rマッパーとは何なのか？
　以下のURLに詳しく書いてあります。
　https://thinkit.co.jp/free/article/0606/13/1
　ざっくり言うと、DAOに直でSQL発行～インスタンスの生成までを書くのではなく、別のファイルに書いておく事で後々変更があった場合とかに柔軟に対応できるみたいな認識でいいと思います。

・では本題。何通りか使い方がある見たいのでそれぞれを見ていきます。

### 1,RowMapperを使う。
RowMapperをインポートしてUserインスタンスを生成する処理自体を分離させる。Mapperクラスは自分で作る。

・まずUserDAOを以下の様に変更。

```java:UserDAO.java

import org.springframework.jdbc.core.RowMapper;

　@Autowired
　private JdbcTemplate jdbc;

    //ユーザー全件取得
    @Override
    public List<User> selectMany() {

        //M_USERテーブルのデータを全件取得するSQL
        String sql = "SELECT * FROM m_user";

        //RowMapperの生成
        RowMapper<User> rowMapper = new UserRowMapper();

        //SQL実行
        return jdbc.query(sql, rowMapper);
    }

```
この時点でDAOはかなりスッキリした。やった事しては以下になる。
・RowMapperをインポート。
・SQLを変数に代入しておく。
・UserRowMapperのインスタンスを生成（ファイルはこの後作成する）
・JdbcTemplateのqueryメソッドを使ってDBから情報を取得。

こんな感じです。ではMapperクラスを作成していきます。作成場所はDAOを同じ階層で問題ないかと思います。

```java:UserRowMapper.java

package com.example.demo.login.domain.repository.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import com.example.demo.login.domain.model.User;

public class UserRowMapper implements RowMapper<User> {

    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
    	
        //戻り値用のUserインスタンスを生成
        User user = new User();
        
        //ResultSetの取得結果をUserインスタンスにセット
        user.setUserId(rs.getString("user_id"));
        user.setPassword(rs.getString("password"));
        user.setUserName(rs.getString("user_name"));
        user.setBirthday(rs.getDate("birthday"));
        user.setAge(rs.getInt("age"));
        user.setMarriage(rs.getBoolean("marriage"));
        user.setRole(rs.getString("role"));

        return user;
    }
}

```

こんな感じです。Mapperクラスのコード自体は元々DAOにあったものとほとんど変わりはありませんが、拡張for文でListを作らなくても良いという点が違います。
詳しくは分かりませんが、mapRow(ResultSet rs, int rowNum)で行単位に処理を行って、結果を都度返しているからだと思います。
rs.getString("user_id")からわかるようにResultSetに１つのレコードが代入されているものと思います。

### 2,BeanPropertyRowMapperを使う。
こちらもUserインスタンスを生成する処理自体を分離させます。通常のRowMapperと違う点はMapperクラスを作らなくてもいい点です。コードを見ていきましょう。

```java:UserDAO.java
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;

public List<User> selectMany() {

        //M_USERテーブルのデータを全件取得するSQL
        String sql = "SELECT * FROM m_user";

        //RowMapperの生成。UserRowMapperを使わなくてもBeanPropertyRowMapperにUser.classを指定すれば同じ事ができる。
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<User>(User.class);
        
        //SQL実行
        return jdbc.query(sql, rowMapper);
    }

```

要点を以下にまとめます。
・Mapperクラスは作成不要。
・BeanPropertyRowMapperインスタンスを生成。この時、型と生成するインスタンスのクラスを指定する。（UserならUser.classみたいに）
・JdbcTemplateのqueryメソッドを使ってDBから情報を取得。
こんな感じです。

ここまで来るとコードがだいぶスッキリしますね。というよりもこれでUserのインスタンスが生成されるのが不思議に思えてくる。

### 3,ResultSetExtractorを使う。
ResultSetExtractorを使います。使い方自体は1のRowMapperみたいな感じで、ResultSetExtractorをimportしたMapperクラスを作ってそいつをnewしてJDBCのqueryメソッドを使用します。

```java:UserDAO.java

public List<User> selectMany() {

        //M_USERテーブルのデータを全件取得するSQL
        String sql = "SELECT * FROM m_user";

        //ResultSetExtractorの生成
        UserResultSetExtractor extractor = new UserResultSetExtractor();

        //SQL実行
        return jdbc.query(sql, extractor);
    }

```

```java:UserResultSetExtractor.java

import org.springframework.jdbc.core.ResultSetExtractor;

// ResultSetExtractor<List<T>>をimplementsする。
public class UserResultSetExtractor implements ResultSetExtractor<List<User>> {

    @Override // extractDataメソッドをオーバーライドする。
    public List<User> extractData(ResultSet rs) throws SQLException, DataAccessException {

        //User格納用List
        List<User> userList = new ArrayList<>();

        //取得件数分のloop
        while(rs.next()) {

            //Listに追加するインスタンスを生成する
            User user = new User();

            //取得したレコードをUserインスタンスにセット
            user.setUserId(rs.getString("user_id"));
            user.setPassword(rs.getString("password"));
            user.setUserName(rs.getString("user_name"));
            user.setBirthday(rs.getDate("birthday"));
            user.setAge(rs.getInt("age"));
            user.setMarriage(rs.getBoolean("marriage"));
            user.setRole(rs.getString("role"));

            //ListにUserを追加
            userList.add(user);
        }
        return userList;
    }
}

```

要点をまとめると。
・ResultSetExtractorをimportする。
・ResultSetExtractor<List<T>>をimplementsする。
・extractDataメソッドをオーバーライドする。
・ResultSetをloopしてレコード毎に処理を行う。
こんな感じです。

## まとめ
以上、３つほどO/Rマッパーのコードを紹介しました。
まだ本番の開発をしたことがないので何とも言えませんが、それぞれに特徴や使うタイミングなどありそうです。
特にBeanPropertyRowMapperなんかはかなり簡単にSQL発行～インスタンス生成までが出来てしまうので有効活用できそうです。