【Springフレームワーク】Spring5とHibernate5.2とThymeleaf3を使ってCRUD操作してみよう!

今回はSpring5とHibernate5.2を使った、CRUD操作をご紹介したいと思います。

はじめに

CRUDとは、データベース管理システムの主要な4機能、Create(作成)・Read(読み出し)・Update(更新)・Delete(削除)の頭文字を取った造語で、クラッドと読みます。

Createは作成となっていますが、テーブルの作成ではなく、データベース管理システムの主要機能の1つであるInsert(挿入)のことです。

Webシステムで例を上げると下記の通りとなります。

  • テーブルからデータを読み込んでデータをリストとして表示します。(Read)
  • データを新規登録してデータをテーブル内に保存します。(Create)
  • 登録されているデータをテーブルから読み込んで更新します。(Update)
  • 登録されているデータをテーブルから読み込んで削除します。(Delete)

開発環境

開発環境は下記の通りです。

Spring5.2.1.RELEASE
Hibernate5.2.10.Final
MySQL8.0.16
Thymeleaf3.0.11.RELEASE

コントローラ

  • アクションを統括する司令塔のコントローラ
package com.pom2019.controller;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.pom2019.entity.HouseholdAppliances;
import com.pom2019.model.AppliancesForm;
import com.pom2019.service.HouseholdAppliancesDao;

@Controller
@ComponentScan("com.pom2019.service")
@RequestMapping("/")
public class AppliancesController {
    //DB操作を行うサービスを注入する。
    @Autowired
    private HouseholdAppliancesDao service;

    //日付型のテキストボックスのフォーマットを"yyyy/MM/dd"で統一する。
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy/MM/dd"), true));
    }

    //**CRUD操作のReadにあたる**
    //リクエストパスが"/list"、メソッドがGETで呼ばれた場合に発動するメソッド。
    //家電リストを取得後、モデルに設定し、テンプレート"displayList.html"を返している。
    @GetMapping(value = "/list")
    public String index(Model model) {
        List<HouseholdAppliances> list = service.findAll();
        model.addAttribute("list", list);
        return "displayList";
    }

    //**CRUD操作のReadにあたる**
    //リクエストパスが"/newview"、メソッドがGETで呼ばれた場合に発動するメソッド。
    //テンプレート"displayNew.html"を返している。
    //空でも appliancesForm をモデルにセットしておかないと正しく表示されない。
    @GetMapping(value = "/newview")
    public String newview(@ModelAttribute("appliancesForm") HouseholdAppliances form, Model model) {
        return "displayNew";
    }

    //**CRUD操作のReadにあたる**
    //リクエストパスが"/editview"、メソッドがPOSTで呼ばれた場合に発動するメソッド。
    //パス変数のidから家電を検索後、モデルに設定し、テンプレート"displayEdit.html"を返している。
    @PostMapping("/editview/{id}")
    public String editdata(@PathVariable Integer id, Model model) {
        List<HouseholdAppliances> list = service.findById(id);
        model.addAttribute("appliancesForm", list.get(0));
        return "displayEdit";
    }

    //**CRUD操作のCreateにあたる**
    //リクエストパスが"/save"、メソッドがPOSTで呼ばれた場合に発動するメソッド。
    //モデル属性で受け取った家電を保存後、"/list"にリダイレクトしている。
    @PostMapping("/save")
    public String save(@ModelAttribute("appliancesForm") HouseholdAppliances entity) {
        service.save(entity);
        return "redirect:/list";
    }

    //**CRUD操作のUpdateにあたる**
    //リクエストパスが"/edit"、メソッドがPOSTで呼ばれた場合に発動するメソッド。
    //モデル属性で受け取った家電を更新後、"/list"にリダイレクトしている。
    @PostMapping("/edit")
    public String edit(@ModelAttribute("appliancesForm") HouseholdAppliances entity) {
        service.update(entity);
        return "redirect:/list";
    }

    //**CRUD操作のDeleteにあたる**
    //リクエストパスが"/delete"、メソッドがPOSTで呼ばれた場合に発動するメソッド。
    //パス変数のidから家電を検索後、該当家電を削除している。その後、"/list"にリダイレクトしている。
    @PostMapping("/delete/{id}")
    public String delete(@PathVariable Integer id, Model model) {
        List<HouseholdAppliances> list = service.findById(id);
        service.delete(list.get(0));
        return "redirect:/list";
    }
}

モデル

  • DB操作を行うサービスモデル
package com.pom2019.service;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Service;

import com.pom2019.entity.HouseholdAppliances;

@Service
@ComponentScan("com.pom2019.config")
public class HouseholdAppliancesDao {

    @Autowired
    private SessionFactory sf;

    public HouseholdAppliancesDao() {
        super();
    }

    @SuppressWarnings("unchecked")
    public List<HouseholdAppliances> findAll() {
        Session ss = sf.openSession();
        return ss.createQuery("from HouseholdAppliances").list();
    }

    @SuppressWarnings("unchecked")
    public List<HouseholdAppliances> findById(Integer id) {
        Session ss = sf.openSession();
        return ss.createQuery("from HouseholdAppliances "
                + "where id = " + id).list();
    }

    public boolean save(HouseholdAppliances h) {
        Session ss = sf.openSession();
        Transaction trans = null;
        try {
            trans = ss.beginTransaction();
            ss.save(h);
            trans.commit();
        } catch (Exception e) {
            trans.rollback();
            return false;
        } finally {
            ss.close();
        }
        return true;
    }

    public boolean update(HouseholdAppliances h) {
        Session ss = sf.openSession();
        Transaction trans = null;
        try {
            trans = ss.beginTransaction();
            ss.update(h);
            trans.commit();
        } catch (Exception e) {
            trans.rollback();
            return false;
        } finally {
            ss.close();
        }
        return true;
    }

    public boolean delete(HouseholdAppliances h) {
        Session ss = sf.openSession();
        Transaction trans = null;
        try {
            trans = ss.beginTransaction();
            ss.delete(h);
            trans.commit();
        } catch (Exception e) {
            trans.rollback();
            return false;
        } finally {
            ss.close();
        }
        return true;
    }
}
  • テーブルをオブジェクト化したモデル
package com.pom2019.entity;
// Generated 2019/12/14 21:57:14 by Hibernate Tools 4.3.5.Final

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

/**
 * HouseholdAppliances generated by hbm2java
 */
@Entity
@Table(name = "HouseholdAppliances", catalog = "TESTDB")
public class HouseholdAppliances implements java.io.Serializable {

    private static final long serialVersionUID = 3713670612166968592L;
    private Integer id;
    private String householdAppliancesName;
    private Date purchaseDate;

    public HouseholdAppliances() {
    }

    public HouseholdAppliances(String householdAppliancesName, Date purchaseDate) {
        this.householdAppliancesName = householdAppliancesName;
        this.purchaseDate = purchaseDate;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)

    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "HouseholdAppliancesName", length = 100)
    public String getHouseholdAppliancesName() {
        return this.householdAppliancesName;
    }

    public void setHouseholdAppliancesName(String householdAppliancesName) {
        this.householdAppliancesName = householdAppliancesName;
    }

    @Temporal(TemporalType.DATE)
    @Column(name = "PurchaseDate", length = 10)
    public Date getPurchaseDate() {
        return this.purchaseDate;
    }

    public void setPurchaseDate(Date purchaseDate) {
        this.purchaseDate = purchaseDate;
    }

}

ビュー

  • 家電情報をリスト表示する画面
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>家電リスト</title>
    <style>
    table {
        border-collapse: collapse;
    }
    td {
        border: 1px solid gray;
        padding: 3px 8px;
    }
    </style>
</head>
<body>
<h2>家電リスト</h2>

<!-- 家電新規登録画面を表示する -->
<form action="newview" method="get">
    <button id="btnNew">新規</button>
</form>

<p />

<table>
    <tr>
        <td>id</td>
        <td>家電名</td>
        <td>購入日時</td>
        <td colspan="2">アクション</td>
    </tr>
    <tr th:each="data : ${list}">
        <td class="id" th:text="${data.id}"></td>
        <td th:text="${data.householdAppliancesName}"></td>
        <!-- purchaseDateの型はDate型なので、日付フォーマットで表示する -->
        <td th:text="${#dates.format(data.purchaseDate, 'yyyy/MM/dd')}"></td>
        <!-- 家電編集画面を表示する -->
        <td><form th:action="@{/editview/{id}(id=*{data.id})}" th:method="post"><button class="btnEdit">編集</button></form></td>
        <!-- 家電をリストから削除する -->
        <td><form th:action="@{/delete/{id}(id=*{data.id})}" th:method="post"><button class="btnDelete">削除</button></form></td>
    </tr>
</table>
</body>
</html>
  • 家電情報の新規登録画面
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>家電情報の新規登録</title>
    <style>
    table {
        border-collapse: collapse;
    }
    td {
        border: 1px solid gray;
        padding: 3px 8px;
    }
    .grayStyle {
        background: lightgray;
    }
    </style>
    <!-- jQuery -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <!-- jQuery UI -->
    <!-- カレンダー表示するために使用 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <!-- jQueyr UI(日本語ライブラリ) -->
    <!-- カレンダーを日本語表示するために使用 -->
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/i18n/jquery.ui.datepicker-ja.min.js"></script>
</head>
<body>
<h2>家電情報の新規登録</h2>

<!-- 保存ボタンが押されると、appliancesFormをモデルとして /save アクションを呼び出す -->
<form th:action="@{/save}" method="post" th:object="${appliancesForm}">
    <p>
        <button id="btnBack">戻る</button>
        <button id="btnSave" type="submit">保存</button>
    </p>
    <table>
        <tr>
            <td>id</td>
            <td class="grayStyle">
                <!-- 新規登録の場合、idは自動採番されるのでその旨を表示 -->
                idは自動採番されます。
            </td>
        <tr>
            <td>家電名</td>
            <td><input id="householdAppliancesName" type="text" th:field="*{householdAppliancesName}"/></td>
        </tr>
        <tr>
            <td>購入日時</td>
            <td>
                <!-- 日付フォーマットが指定のものから崩れないようにカレンダーからのみ入力を許可する(←readonly) -->
                <input id="purchaseDate" type="text" th:field="*{purchaseDate}" readonly="readonly"/>
            </td>
        </tr>
    </table>
</form>

<script>
    $(function() {
        //日付ピッカーの設定
        $('#purchaseDate').datepicker({
            //日付フォーマットの指定
            dateFormat: 'yy/mm/dd',
            //年をリストから変更できるようにする
            changeYear: true,
            //月をリストから変更できるようにする
            changeMonth: true
        });

        //戻るボタン
        $('#btnBack').on('click', function() {
            history.back();
        });
    });
</script>
</body>
</html>
  • 家電情報の編集画面
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>家電情報の編集</title>
    <style>
    table {
        border-collapse: collapse;
    }
    td {
        border: 1px solid gray;
        padding: 3px 8px;
    }
    .grayStyle {
        background: lightgray;
    }
    </style>
    <!-- jQuery -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <!-- jQuery UI -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <!-- jQueyr UI(日本語ライブラリ) -->
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/i18n/jquery.ui.datepicker-ja.min.js"></script>
</head>
<body>
<h2>家電情報の編集</h2>

<!-- 保存ボタンが押されると、appliancesFormをモデルとして /edit アクションを呼び出す -->
<!-- fieldへは、appliancesFormモデルに設定された値が反映される -->
<form th:action="@{/edit}" method="post" th:object="${appliancesForm}">
    <p>
        <button id="btnBack">戻る</button>
        <button id="btnSave" type="submit">保存</button>
    </p>
    <table>
        <tr>
            <td>id</td>
            <td>
                <input id="id" type="text" th:field="*{id}" readonly="readonly"/>
            </td>
        <tr>
            <td>家電名</td>
            <td><input id="householdAppliancesName" type="text" th:field="*{householdAppliancesName}"/></td>
        </tr>
        <tr>
            <td>購入日時</td>
            <td><input id="purchaseDate" type="text" th:field="*{purchaseDate}" readonly="readonly"/></td>
        </tr>
    </table>
</form>

<script>
    $(function() {
        //日付ピッカーの設定
        $('#purchaseDate').datepicker({
            dateFormat: 'yy/mm/dd',
            changeYear: true,
            changeMonth: true
        });

        //戻るボタン
        $('#btnBack').on('click', function() {
            history.back();
        });
    });
</script>
</body>
</html>

まとめ

Spring5フレームワークを使って、データベース管理システムの4つの基本動作であるCRUDを行う処理を見てきましたが、いかがだったでしょうか?

MVC(モデル、ビュー、コントローラ)で役割ごとに分業ができ、それぞれの処理はほんの数行で実装できています。

これからSpringフレームワークを使って開発を行いたい方の参考になればうれしいです。

最後までお読み頂きありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です