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

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

はじめに

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

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

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

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

開発環境

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

Spring 5.2.1.RELEASE
Hibernate 5.2.10.Final
MySQL 8.0.16
Thymeleaf 3.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フレームワークを使って開発を行いたい方の参考になればうれしいです。

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

広告

コメントを残す

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

CAPTCHA


ABOUT US
ぽむ
はじめまして! ぽむ と申します。 Java、Kotlin (Android)、VBAなどの開発経験があるITエンジニアです。 備忘録として始めたブログですがみなさまのお役に立てたら光栄です。 英語など IT 以外の話題にも触れていこうと思っています。 詳しい自己紹介についてはこちら! よろしくお願いします。