今回は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フレームワークを使って開発を行いたい方の参考になればうれしいです。
最後までお読み頂きありがとうございました。
コメントを残す