【Java・SpringBoot】Spring JDBC /  RowMapper（SpringBootアプリケーション実践編15）

ホーム画面からユーザー一覧画面に遷移し、ユーザーの詳細を表示するアプリケーションを作成して、Spring JDBCの使い方について学びます⭐️
前回までで、Jdbc Templateを使った基本のCRUD操作について学びました
今回は応用編で、RowMapperを学びます^^
構成は前回の記事を参考にしてください

⭐️前回の記事
[【Java・SpringBoot】Spring JDBC でユーザー削除処理（SpringBootアプリケーション実践編14）](https://qiita.com/suema0331/items/fe54bbf8d089be8eb1fe)

#RowMapper
* RowMapperとは、**データベースのレコード（行）と、Javaオブジェクトのマッピングを行う**ためのクラス
* これまでは、Userテーブルのデータを１件取得するには、<br>select文で取得した結果をMapから取り出して、Userクラスのインスタンスにセットしていた
    * 似たようなselect文を沢山作ると、コードの可読性が下がる。。
* →そんな時に、**RowMapper！**

#UserRowMapper作成
* まずは、UserRowMapperを作成

##RowMapperの使いかた
* **RowMapper<?>インターフェースを継承（implements）**
    * `?`=**マッピングに使うJavaオブジェクトのクラス**を指定
    * RowMapperを継承して、**mapRowメソッドをOverride**
    * `public class UserRowMapper implements RowMapper<User> {`
    * 引数の**ResultSetにはSelect結果**が入る
        * 以下ではResultSetの値をUserクラスにセット
        * 最後に**Userクラスのインスタンスをreturn**
* →RowMapperでは**Select結果とUserクラスを、あらかじめマッピングできる**


```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;
    }
}
   
```

#UserDaoJdbcImpl継承クラス作成
* **select文を実行する箇所だけRowMapper**に変えて、**insert、update文はUserDaoJdbcImplと同じ**動きをする
* 以下の例では、JdbcTemplateのメソッドの引数に、RowMapperをセット
* →**select結果が入ったUserクラスや`List<User>`の結果**が返る

```UserDaoJdbcImpl2.java
package com.example.demo.login.domain.repository.jdbc;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import com.example.demo.login.domain.model.User;

@Repository("UserDaoJdbcImpl2")
public class UserDaoJdbcImpl2 extends UserDaoJdbcImpl {

    @Autowired
    private JdbcTemplate jdbc;

    //ユーザー１件取得
    @Override
    public User selectOne(String userId) {
        //１件取得用SQL
        String sql = "SELECT * FROM m_user WHERE user_id = ?";
        //RowMapperの生成
        UserRowMapper rowMapper = new UserRowMapper();
        //SQL実行
        return jdbc.queryForObject(sql, rowMapper, userId);
    }

    //ユーザー全件取得
    @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);
    }
}

```

##@Repositoryアノテーション引数を追加
* **UserDaoJdbcImpl.java**の@Repositoryアノテーションに引数を追加し、**Bean名をセット**
* →Bean名をセットすることで、**@Autowiredする際に、どのクラスを使用するか指定**できる！(次に**サービスクラスを修正**してBean指定する)
* コード全文は下記参考

```UserDaoJdbcImpl.java
//中略
@Repository("UserDaoJdbcImpl")
public class UserDaoJdbcImpl implements UserDao {
//中略
```

##サービスクラスを修正
###@Qualifier
* @Autowiredと**@Qualifier**アノテーションを使用すると、どのBeanを使用するか指定できる
    * UserDaoインターフェースを継承したクラスが１つの場合は、**Springが自動でBeanを探してくれる**
    * インターフェースを**継承したクラスが２つ**（UserDaoJdbcImpl/UserDaoJdbcImpl2）の場合は、**@Qualifierが必要**
    * Springではインターフェースを作り、それを継承したクラスを作るというのが一般的な使い方
* コード全文は下記参考

```UserService.java
//中略
@Service
public class UserService {
    @Autowired
    @Qualifier("UserDaoJdbcImpl2")
    UserDao dao;
//中略
```

#SpringBootを起動してホーム画面確認！
* http://localhost:8080/home
* ユーザー一覧、ユーザー詳細画面に移ると、コンソールの表示が`UserDaoJdbcImpl2`に変わっています
* これでRowMapperを使うことができました〜^o^

```
//コンソール画面
メソッド開始： String com.example.demo.login.controller.HomeController.getUserDetail(SignupForm,Model,String)
userId = nekomofu@xxx.co.jp
メソッド開始： User com.example.demo.login.domain.repository.jdbc.UserDaoJdbcImpl2.selectOne(String)
メソッド終了： User com.example.demo.login.domain.repository.jdbc.UserDaoJdbcImpl2.selectOne(String)
メソッド終了： String com.example.demo.login.controller.HomeController.getUserDetail(SignupForm,Model,String)
```


#(参考)コード全文

```UserDaoJdbcImpl.java
package com.example.demo.login.domain.repository.jdbc;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import com.example.demo.login.domain.model.User;
import com.example.demo.login.domain.repository.UserDao;

@Repository("UserDaoJdbcImpl")
public class UserDaoJdbcImpl implements UserDao {

    @Autowired
    JdbcTemplate jdbc;

    // Userテーブルの件数を取得.
    @Override
    public int count() throws DataAccessException {

        //全件取得してカウント
        int count = jdbc.queryForObject("SELECT COUNT(*) FROM m_user", Integer.class);

        return count;
    }

    // Userテーブルにデータを1件insert.
    @Override
    public int insertOne(User user) throws DataAccessException {

        //１件登録
        int rowNumber = jdbc.update("INSERT INTO m_user(user_id,"
                + " password,"
                + " user_name,"
                + " birthday,"
                + " age,"
                + " marriage,"
                + " role)"
                + " VALUES(?, ?, ?, ?, ?, ?, ?)",
                user.getUserId(),
                user.getPassword(),
                user.getUserName(),
                user.getBirthday(),
                user.getAge(),
                user.isMarriage(),
                user.getRole());

        return rowNumber;
    }

    // Userテーブルのデータを１件取得
    @Override
    public User selectOne(String userId) throws DataAccessException {

        // １件取得
        Map<String, Object> map = jdbc.queryForMap("SELECT * FROM m_user"
                + " WHERE user_id = ?", userId);

        // 結果返却用の変数
        User user = new 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")); //ロール

        return user;
    }

    // Userテーブルの全データを取得.
    @Override
    public List<User> selectMany() throws DataAccessException {

        // M_USERテーブルのデータを全件取得
        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;
    }

    // Userテーブルを１件更新.
    @Override
    public int updateOne(User user) throws DataAccessException {

        //１件更新
        int rowNumber = jdbc.update("UPDATE M_USER"
                + " SET"
                + " password = ?,"
                + " user_name = ?,"
                + " birthday = ?,"
                + " age = ?,"
                + " marriage = ?"
                + " WHERE user_id = ?",
                user.getPassword(),
                user.getUserName(),
                user.getBirthday(),
                user.getAge(),
                user.isMarriage(),
                user.getUserId());
    }

    // Userテーブルを１件削除.
    @Override
    public int deleteOne(String userId) throws DataAccessException {

        //１件削除
        int rowNumber = jdbc.update("DELETE FROM m_user WHERE user_id = ?", userId);

        return rowNumber;

    }

    @Override
    public void userCsvOut() throws DataAccessException {

    }
}

```

```UserService.java
package com.example.demo.login.domain.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.login.domain.model.User;
import com.example.demo.login.domain.repository.UserDao;

@Service
public class UserService {
    @Autowired
    @Qualifier("UserDaoJdbcImpl2")
    UserDao dao;

    public boolean insert(User user) {
        // insert実行
        int rowNumber = dao.insertOne(user);
        // 判定用変数
        boolean result = false;

        if (rowNumber > 0) {
            // insert成功
            result = true;
        }
        return result;
    }

    public int count() {
        return dao.count();
    }

    public List<User> selectMany() {
        // 全件取得
        return dao.selectMany();
    }

    public User selectOne(String userId) {
        // selectOne実行
        return dao.selectOne(userId);
    }

    public boolean updateOne(User user) {

        // 判定用変数
        boolean result = false;

        // １件更新
        int rowNumber = dao.updateOne(user);

        if (rowNumber > 0) {
            // update成功
            result = true;
        }

        return result;
    }

    public boolean deleteOne(String userId) {

        // １件削除
        int rowNumber = dao.deleteOne(userId);

        // 判定用変数
        boolean result = false;

        if (rowNumber > 0) {
            // delete成功
            result = true;
        }
        return result;
    }
}


```