0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MyBatisお試し #2 データ取得

Last updated at Posted at 2024-12-30

Prev

概要

  • 業務でMyBatisを使用することがあったので理解度向上のために自宅でも色々触ってみる
  • 下記のような操作が可能なアプリを作成する

image.png

ソースコード

ユーザ一覧取得

  • localhost:8080/allUsersへアクセスするとDBに登録しているデータを全件表示できるよう実装する
  • 処理の流れは下記のイメージ
    • ユーザがlocalhost:8080/allUsersへアクセス
    • コントローラ → モデル → マッパー → XMLに記載したSQL実行 → 取得したデータをエンティティへ格納 → エンティティからDTOへ変換 → コントローラ → ビューに取得したデータを表示

データ取得用のSQL用意

  • DBからデータを取得する際はSQLをアノテーションに記載する方法とXMLに記載する方法の2種類がある
  • 今回はXMLにSQLを記載する方法を選択
UserMapper.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.example.mybatis.mapper.UserMapper">

    <!-- 全ユーザを取得 -->
    <select id="findAll" resultType="com.example.mybatis.entity.UserEntity">
        SELECT
            *
        FROM
            users
    </select>
</mapper>
  • UserMapper.xmlのselectタグの値とUserMapper.javaのメソッド名を一致させることで、メソッドが呼ばれるとSQLを実行することが可能になる
  • resultType属性の値にはSQLを実行して取得したデータを格納するクラス(UserEntity.java)を指定
    • データ格納用のクラスにはDBのカラムに対応するフィールドを用意する
    • 今回はUSERSテーブルで定義している下記の4つのカラムに対応するフィールドを用意
      • id
      • username
      • age
      • address

マッパー(DAO)インタフェースの実装

  • 上記のXML内のSQLを呼び出すためにUserMapperインタフェースを用意する
UserMapper.java
package com.example.mybatis.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.example.mybatis.dto.UserDto;

@Mapper
public interface UserMapper {

    /**
     * 全データを取得
     */
    List<UserEntity> findAll();
}
  • @Mapperでマッパーということを明示
  • findAllメソッドによってUserMapper.xmlのselect文を呼び出す

モデル

  • データベースとのやり取りを実施するUserLogicクラスを作成
UserLogic.java
package com.example.mybatis.model;

import java.util.List;

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

import com.example.mybatis.dto.UserDto;
import com.example.mybatis.mapper.UserMapper;

@Component
public class UserLogic {

    @Autowired
    private UserMapper userMapper;

    /**
     * findAllメソッドを呼び出し
     * USERSテーブルの全てのデータを取得
     */
    public List<UserDto> findAll() {
    
        List<UserEntity> userEntities = userMapper.findAll();
        return toDto(userEntities);
    }

    /**
     * UserEntityからUserDtoに変換
     */
    public List<UserDto> toDto(List<UserEntity> userEntities) {

        List<UserDto> allUsers = new ArrayList<>();
        for (UserEntity entity: userEntities) {
            UserDto userDto = new UserDto();
            userDto.setId(entity.getId());
            userDto.setUsername(entity.getUsername());
            userDto.setAge(entity.getAge());
            userDto.setAddress(entity.getAddress());
            allUsers.add(userDto);
        }
        return allUsers;
    }
}
  • @AutowiredによってuserMapperに対してDIを実施
  • userMapperインタフェースのfindAllメソッドを呼び出し
  • returnする際はListをUserEntity型からUserDto型に変換する必要があるため、toDtoメソッドを呼び出して変換を実施

コントローラ

  • モデル層からSQL実行結果を格納したデータを受け取りビューへ渡す処理を記述
MybatisController
@Controller
public class MybatisController {

    @Autowired
    private UserLogic userLogic;

    // 中略
    
    @GetMapping("/users")
    public String allUsers(Model model) {
        List<UserDto> allUserList = userLogic.findAll();
        model.addAttribute("users", allUserList);
        return "users";
    }
  • localhost:8080/allUsersへアクセスした際にallUsersメソッドを実行
  • モデル(UserLoic.java)のfindAllメソッドを呼び出し、DBに登録されている全データをList型のallUserListに格納
  • 受け取った全データのリストをaddAttributeメソッドによりusersという名前でビューに渡す
  • usersをreturnすることでuser.htmlを表示

エンティティの作成

  • SQLを実行し取得したデータを格納するためのエンティティクラスを作成
UserEntity.java
package com.example.mybatis.entity;

import lombok.Data;

@Data
public class UserEntity {
    
    private Integer id;
    private String username;
    private Integer age;
    private String address;
}
  • USERSテーブルのカラムに対応するフィールドを宣言
  • @Dataによってgetterとsetterを自動生成

DTO作成

  • コントローラとモデル間のデータのやり取りはこのDTOクラスを介して行うことで効率的なデータ受け渡しを実現
UserDto.java
package com.example.mybatis.dto;

import lombok.Data;

@Data
public class UserDto {

    private Integer id;
    private String username;
    private Integer age;
    private String address;
}
  • 基本的にエンティティと同様にDBのカラムに対応するフィールドとgetter、setterを持つ

application.ymlの修正

  • application.ymlにもMyBatis関連の設定を追記
application.yml
mybatis:
  mapper-locations: classpath:mapper/*.xml
  • mapper-locationでマッパーのXMLを配置しているパスを指定
    • 今回はsrc/main/resources/mapperに配置

ビューの作成

  • 取得したデータを一覧で表示するビューを作成する
users.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>ユーザ一覧</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <div class="container mt-3">
        <h1>ユーザ一覧</h1>
        <table class="table table-striped table-bordered">
            <thead class="table-dark">
                <tr>
                    <th>ID</th>
                    <th>ユーザ名</th>
                    <th>年齢</th>
                    <th>住所</th>
                </tr>
            </thead>
            <tbody>
                <!-- users からデータを繰り返し表示 -->
                <tr th:each="user : ${users}">
                    <td th:text="${user.id}">ID</td>
                    <td th:text="${user.username}">ユーザ名</td>
                    <td th:text="${user.age}">年齢</td>
                    <td th:text="${user.address}">住所</td>
                </tr>
            </tbody>
        </table>

        <a th:href="@{/main}" class="btn btn-secondary mt-3">メイン画面へ戻る</a>
    </div>
</body>

</html>
  • th:eachの部分によってコントローラから受け取った全ユーザのリストusersuserとして扱い、テーブルとして表示
  • 表の見た目はBootstrapにより整えているが本記事の内容とはあまり関係ないため割愛
  • メイン画面へ戻るためのボタンも用意

ユーザ一覧取得

image.png

ユーザ検索

  • localhost:8080/userSearchへアクセスしIDを入力すると、該当するIDのユーザ情報を表示できるようにする
  • 上記で作成したソースに対してそれぞれ下記の通り追記する

UserMapper.xm

UseMapper.xml
    <select id="findUser" resultType="com.example.mybatis.entity.UserEntity">
        SELECT
            *
        FROM
            users
        WHERE
            id = #{id}
    </select>
  • 入力フォームから渡されたIDを受け取りSELECT文によりユーザ情報を抽出

UserMapper.java

UserMapper.java
    /**
     * 入力フォームで指定したIDのデータを取得
     */
    UserEntity findUser(int id) throws UserNotFoundExeption;
  • findUserメソッドの引数としてIDを渡す
  • 指定したIDに対応するユーザが存在しない場合はUserNotExeptionを投げる
    ※後ほど作成

UserLogic.java

UserLogic.java
    /**
     * findUserメソッドを呼び出し
     * URLで指定したIDのデータを取得
     */
    public UserDto findUser(UserDto userDto) throws UserNotFoundExeption {

        UserEntity userEntity = userMapper.findUser(userDto.getId());

        if (userEntity == null) {
            throw new UserNotFoundExeption("指定したユーザは存在しません。");
        }
        return toDto(userDto, userEntity);
    }

    /**
     * UserEntityからUserDtoに変換
     */
    public UserDto toDto(UserDto userDto, UserEntity userEntity) {

        userDto.setUsername(userEntity.getUsername());
        userDto.setAge(userEntity.getAge());
        userDto.setAddress(userEntity.getAddress());
        return userDto;
    }
  • findUserメソッドでは入力フォームで入力されたIDを受け取るために、引数としてuserDtoを指定
    • userDto.getId()でURLに入力したIDを取得可能
  • 全件取得の際と同様にUserEntity型からUserDto型へ変換しreturnする
  • 指定したIDに対応するユーザが存在しない場合は、エラーメッセージと共にUserNotExeptionをコントローラへ投げる

MybatisController.java

MybatisController.java
    UserDto userDto = new UserDto();

   /**
     * ユーザ検索画面表示
     */
    @GetMapping("/userSearch")
    public String userSearch() {

        return "search/userSearch";
    }

    /**
     * 検索結果表示
     */
    @GetMapping("/userInfo")
    public String userInfo(@RequestParam("id") int id, Model model) {
        
        try {
            userDto.setId(id);
            userDto = userLogic.findUser(userDto);
            model.addAttribute("user", userDto);
        } catch (UserNotFoundException e) {
            model.addAttribute("errorMessage", e.getMessage());
            return "search/userInfo";
        }
        return "search/userInfo";
    }
  • localhost:8080/userSearchにアクセスするとユーザ検索画面を表示
  • 検索ボタンを押下するとlocalhost:8080/userInfoへ遷移
    • @RequestParam("id")により入力されたIDを引数として受け取る
  • DTOのidに入力されたIDをセットしモデル層へ渡す
  • モデルから受け取ったSELECT文の実行結果(userDto)をuserという名前でビューへ渡す
  • モデルからUserNotFoundExeptionが返ってきている場合はエラーメッセージをビューへ渡す

userSearch.html

  • ユーザ検索画面
user.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>ユーザ検索</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <div class="container mt-3">
        <h1>ユーザ検索</h1>
        <form th:action="@{/userInfo}" method="get">
            <div class="mb-3">
                <label for="userId" class="form-label">ユーザID</label>
                <input type="number" class="form-control" id="userId" name="id" required>
            </div>
            <button type="submit" class="btn btn-primary">検索</button>
        </form>

        <a th:href="@{/main}" class="btn btn-secondary mt-3">メイン画面へ戻る</a>
    </div>
</body>

</html>
  • th:actionではボタンを押した際の遷移先を指定
  • 今回はlocalhost:8080/userInfoに遷移するためuserInfoを指定

userInfo.html

  • 検索結果表示画面
userInfo.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>ユーザ検索</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <div class="container mt-3">
        <h1>検索結果</h1>
        <!-- 指定したIDのユーザが存在しない場合 -->
        <div th:if="${errorMessage}">
            <p th:text="${errorMessage}">指定したユーザは存在しません。</p>
        </div>
        <!-- 指定したIDのユーザが存在する場合 -->
        <div th:if="${user}">
            <table class="table table-striped table-bordered">
                <thead class="table-dark">
                    <tr>
                        <th>ID</th>
                        <th>ユーザ名</th>
                        <th>年齢</th>
                        <th>住所</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- user のデータを表示 -->
                    <td th:text="${user.id}">ID</td>
                    <td th:text="${user.username}">ユーザ名</td>
                    <td th:text="${user.age}">年齢</td>
                    <td th:text="${user.address}">住所</td>
                </tbody>
            </table>
        </div>

        <a th:href="@{/main}" class="btn btn-secondary mt-3">メイン画面へ戻る</a>
    </div>
</body>

</html>
  • th:ifにより指定したIDに対応するユーザが存在する場合と存在しない場合の条件分岐を実施
  • 存在する場合はユーザ情報を、存在しない場合はエラーメッセージを表示

UserNotFoundExeption.java

  • exeptionパッケージを新たに作成しUserNotFoundExeptionクラスを作成
UserNotFoundExeption.java
package com.example.mybatis.exception;

/**
 * ユーザが見つからない場合の例外
 */
public class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(String message) {
        super(message);
    }
}
  • ObjectNotFoundExeptionを使用しても良かったが、エラーメッセージに余計な文章がついてしまうため独自クラスとして実装
  • UserLogicクラスで指定したメッセージをエラーメッセージとして設定

ユーザ検索

ユーザが存在する場合

  • ID:1

image.png

image.png

ユーザが存在しない場合

  • ID:7

image.png

image.png

Next

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?