【JAVA】SPRING BOOTでWEBアプリを作成してみる 3日目 一覧画面

2021/9/4



こんにちは、前回のデータベースの導入はうまくいったでしょうか。今回はデータベースからデータを取得し、画面に取得データを一覧表示するところまでをやりたいと思います。



前準備


まず、一覧画面を作成する上で必要なものをネットからMavenを使って取得しましょう。

画面は、Springでこれまで使用してきたJspでなく、Spring Boot推奨のthymeleafで作成することにします。
DBからのデータ取得は、ポスグレDBと接続するドライバ"postgresql"、SQLを記述できるORマッパー(JAVAのOBJECTでデータベースを操作できるマッパー)"mybatis"で取得します。
また、データの取得時に自動でgetter、setterを作ってくれる"lombok"も使用します。

今回はデフォルトのMavenでプロジェクトを作成してますので、pom.xmlに上記の設定を追加してみましょう。



それでは、eclipseに"ctrl + shift + r"を押してから検索窓に"pom.xml"を打鍵し、pom.xmlを開きます。(すぐ見つかるならそのまま開いちゃってください)

pom.xmlのdependenciesタグに以下を追加します。
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

pom.xmlの全体
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>demo</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>1.8</java.version>
    <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>
pom.xmlを保存したら自動でライブラリの取得が始まります。うまく動かせなかった時には、eclipseの"プロジェクト -> クリーン"を押してから、プロジェクトを右クリックして、Maven -> プロジェクトの更新を押してください。それでもだめなら、隠しフォルダの.m2/repository/内にある、関係するフォルダを削除してからMaven -> プロジェクトの更新でうまくいくでしょう。

次に、Spring Bootの設定ファイルであるapplication.propertiesにDB接続用の設定を記載します。
application.ymlではないです。ymlは新しい設定の書き方で、書式が変わりますのでお間違いのないよう

/demo/src/main/resources/application.properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/web?web_schema
spring.datasource.username=dev
spring.datasource.password=xxxx


一覧画面の作成


MVCデザインパターン


WEB系の画面システムを作る場合、必須といっていいパターンがあります。MVCデザインパターンと言って、システムの役割を、具体的なデータの処理を行うモデル(model)、処理されたデータをユーザに示すビュー(view)、リクエストのハンドリングを行うコントローラ(controller)の3つに分けます。このように分けることで、開発フェーズにて各層の作業に集中しやすい、バグの特定が容易になるといった利点があります。

今回のシナリオでは、ユーザがブラウザでurlを指定し、コントローラがリクエストをモデルへハンドリング、モデルはデータ取得の処理を行い、ビューは受け取ったデータを画面へ表示するように役割分担させます。
< 作成ソースの全体図 >


ビュー


/demo/src/main/resources/templates/users.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>ユーザ情報一覧画面</title>
</head>
<body>

  <h1>Spring Boot 勉強用 ビュー</h1>

  <table border="1">

    <tr>
      <th>ID</th>
      <th>名前</th>
      <th>メール</th>
      <th>住所</th>
      <th>性別</th>
      <th>備考</th>
    </tr>

    <th:block th:with="userInfo=${users}">
      <tr th:each="user : ${userInfo}">
        <td th:text="${user.id}"></td>
        <td th:text="${user.name}"></td>
        <td th:text="${user.email}"></td>
        <td th:text="${user.address}"></td>
        <td th:text="${user.sex} ? #{sex.male} : #{sex.female}"></td>
        <td th:text="${user.remark}"></td>
      </tr>
    </th:block>

  </table>

</body>
</html>
画面に表示する性別の男性:女性の固定値は管理しやすいようmessages.propertiesで管理します。今回は日本語を含みますので、messages_ja.propertiesに記載します。ただ、日本語のみの場合でもエラーになりますのでmessages.propertiesは空ファイルを作成します。

下記の空ファイルを作成します。
/demo/src/main/resources/messages.properties

日本語のメッセージを追加します。
/demo/src/main/resources/messages_ja.properties
# 性別・男
sex.male=男性
# 性別・女
sex.female=女性
thymeleafではmessages.propertiesの参照は#{...}でします。また、変数の展開は${...}、ブロック内で使用するローカル変数はth:with="変数名=値"で定義します。Jspはマルチスレッド環境で同時に多数のリクエストがあると、インスタンス変数を違うスレッドの内容で書き換えることがあります。thymeleafでもスレッドセーフを意識してローカル変数にしたほうが良いでしょうか。ネットで調べても良くわかりませんでした。下記のリンクを見ると、thymeleafは意識しなくてもスレッドセーフになっている?
Thymeleaf | Is TemplateEngine.process thread-safe operation?

th:eachではusersテーブルから取得したリストのデータ分、連続して表示させます。
性別を表示している部分 ${user.sex} ? #{sex.male} : #{sex.female} は 変数のuser.sexがtrueならmessages_ja.propertiesのsex.maleを表示、それ以外はsex.femaleを表示します。この『A?B:C』の構文を3項演算子といいます。使用を推奨しないところもありますが、if ~ else 文をより簡潔に記載できます。


コントローラ


/demo/src/main/java/com/example/demo/controller/UserController.java
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.service.UserService;

/**
 *
 * Spring Boot 勉強用 コントローラ
 *
 * @author    otoku-se
 * @version   1.0.0
 */

@Controller
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     *
     * usersテーブルからデータを取得する -> モデル
     * users.htmlに返す -> ビュー
     *
     * @param   model  users.htmlへデータを持っていく
     * @return  users.htmlへ遷移
     */
    @GetMapping
    public String findAll(Model model) {

        model.addAttribute("users", userService.findAll());
        return "users";

    }

}
今回は@RestControllerでなく、@Controllerを使っています。@RestControllerはJsonやXML、文字列を返す時に使用。戻り値に遷移先の画面を指定するときは、@Controllerを使用し、return "users"でusers.htmlへ遷移するようになります。また、遷移先の画面へ引き継ぐデータはmodel.addAttributeに格納すると、springがビューへデータを移行してくれます。(${users}で展開しています)

@Autowiredは、SpringのDIコンテナにbeanとして登録されたクラス(@Componentのついたクラス等)をSpringのほうでnewしてくれます(依存性の注入)。@Componentは@Controllerや@Serviceといったアノテーションの中にも埋め込まれてます。下記はUserController.javaから呼ばれるUserService.javaのクラスについた@Serviceの中身です。



Springの特徴の一つとして依存性の注入(DI)があります。あるオブジェクトの中から呼ばれる実装部分のオブジェクトをSpringのDIコンテナに登録し、生成をコンテナにまかせることで、コードが簡潔になる、テストがしやすくなるというメリットがあります。

DIを利用したプログラムを作成する場合、コンポーネント間の関係はインタフェースを用いて記述し、具体的なコンポーネントを指定しない。具体的にどのコンポーネントを利用するかは別のコンポーネントや外部ファイル等を利用することで、コンポーネント間の依存関係を薄くすることができる。

依存関係がプログラムから外部に取り除かれることで、以下のようなメリットが発生する。

・結合度の低下によるコンポーネント化の促進
単体テストの効率化
特定のフレームワークへの依存度低下


モデル


/demo/src/main/java/com/example/demo/service/UserService.java
package com.example.demo.service;

import java.util.List;

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

import com.example.demo.domain.User;
import com.example.demo.mapper.UserMapper;

/**
 *
 * Spring Boot 勉強用 モデル
 *
 * @author    otoku-se
 * @version   1.0.0
 */

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

   /**
    *
    * usersテーブルにある全データを取得する
    *
    * @return  usersテーブルの全データをListで返します
    */

    @Transactional(readOnly = true)
    public List<User> findAll() {
        return userMapper.findAll();
    }

}
サービスではDBよりデータの取得を行います。
@Transactionalはデータ取得処理でエラーが起きた時に通常データをロールバック(元に戻す)してくれます。
今回はSelect文ですので読み取り専用のreadOnly=trueを指定しています。

/demo/src/main/java/com/example/demo/mapper/UserMapper.java
package com.example.demo.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.example.demo.domain.User;

/**
*
* Spring Boot 勉強用 モデル
*
* @author    otoku-se
* @version   1.0.0
*/

@Mapper
public interface UserMapper {

   /**
    *
    * usersテーブルにある全データを取得する
    *
    * @return  usersテーブルの全データをListで返します
    */
    List<User> findAll();
}


/demo/src/main/java/com/example/demo/mapper/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.demo.mapper.UserMapper">

    <select id="findAll" resultType="com.example.demo.domain.User">
        SELECT id,
               name,
               email,
               address,
               CASE WHEN sex = '1' THEN true
                    ELSE false
               END sex,
               remark
          FROM web_schema.users
    </select>

</mapper>
UserMapper.xmlのセレクトタグに実行するSQLを記載します。mapperのnamespaceに上記UserMapper.javaのインターフェースを指定すると、XMLとインターフェースの紐づけが行われます。idにはインターフェースの対応するメソッド名を指定。resultTypeには、SQLの結果を格納する下記のUser.javaを指定します。これだけでテーブルからデータを取得し、JAVAのオブジェクトに格納してくれます。インターフェースの実装はmybatisの方でやってくれます。こちらでDAOを実装する必要はありません。

※SQL : FROM users -> FROM web_schema.usersへスキーマを追加修正しました。


/demo/src/main/java/com/example/demo/domain/User.java
package com.example.demo.domain;

import lombok.Data;

/**
*
* Spring Boot 勉強用 モデル
* UsersテーブルのデータをマッピングするためのEntity
* getter setterはLombokの@Dataが自動的に作成してくれる
*
* @author    otoku-se
* @version   1.0.0
*/

@Data
public class User {

    private String id;
    private String name;
    private String email;
    private String address;
    private boolean sex;
    private String remark;

}


ここまでできたらプロジェクトを右クリックして、実行 -> Spring Boot アプリケーションを選択。
urlにhttp://localhost:8080/usersを入力して確認します。




今回はここまでです。
気をつけてはいますが、間違いがあったらごめんなさい。またね。

参考
LombokをSpringで利用するには
Spring MVCのコントローラでの戻り値いろいろ
Spring Bootで簡単なWebアプリケーションを書いてみる
mybatis
5. 国際化対応
17. Spring Bean と依存性注入

お借りした素材
NYN姉貴.png
ちょっと髪型を変えたNYN姉貴.png
ハチっぽいアレ

JAVA おすすめの本
[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]
スッキリわかるJava入門第2版 [ 中山清喬 ]
価格:2860円(税込、送料無料) (2020/4/12時点)






カテゴリ

このブログを検索

自己紹介

自分の写真
作らなきゃ(使命感)

QooQ