MyBatisを利用してJavaでtodoリスト作ってみた。
メモです。
こんにちは、青のひとりごとです。
今回はMyBatisの便利さを試すために、MyBatisを用いてJavaのSpringBootでtodoリストを作ってみました。
結論としては、
とても便利でした!
特に、
場合分けしてSQL文を実行するのに有用だ
と感じました。
MyBatisを利用する際、マッパーファイル(Daoのようなもの)のアノテーションに直接SQL文を書き込む方法もあるようですが、今回はXMLファイルを用いた方法で記述しました。
今回、MyBatisを試すにあたって、こちらの記事を主に参考にさせていただきました。そのため、バージョンなどの設定はこちらの記事と同じになっています。
XMLファイルには以下のように記述を行いました。
PlaceMapper.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.TaskMapper">
<resultMap id="taskMap"
type="com.example.demo.entity.Task">
<result column="ID" jdbcType="INTEGER" property="taskId" />
<result column="TASK" jdbcType="VARCHAR" property="task" />
<result column="MANHOURS" jdbcType="INTEGER" property="manHours" />
<result column="ALREADY" jdbcType="BOOLEAN" property="already" />
</resultMap>
<!-- Todo一覧を取り出す -->
<select id="selectAll" resultMap="taskMap">
SELECT ID,TASK,MANHOURS,ALREADY FROM TASK
</select>
<!-- 特定のTodoの編集 -->
<update id="update">
UPDATE TASK
SET
TASK = #{task},
MANHOURS = #{manHours}
WHERE
ID = #{taskId}
</update>
<!-- 特定のTodoの済/未の変換 -->
<update id="updateAlready">
UPDATE TASK
SET
<choose>
<when test="already == 'done'">
ALREADY = 1
</when>
<when test="already != 'done'">
ALREADY = 0
</when>
</choose>
WHERE
ID = #{taskId}
</update>
<!-- 特定のTodoの削除 -->
<delete id="delete">
DELETE FROM TASK
WHERE
ID = #{taskId}
</delete>
<!-- Todoの挿入 -->
<insert id="insert" >
INSERT INTO TASK
(TASK,MANHOURS)
VALUES
(#{task}, #{manHours})
</insert>
<!-- Todoの検索 -->
<select id="search" resultMap="taskMap">
SELECT ID,TASK,MANHOURS,ALREADY FROM TASK
<where>
<choose>
<when test="task != null">
TASK LIKE concat('%', #{task}, '%')
</when>
<when test="already != 'all'">
ALREADY = #{already}
</when>
</choose>
</where>
</select>
</mapper>
注目したいのが、一番最後の部分(Todoの検索)。
whenタグで囲ってある部分は、taskやalreadyという変数の値によって、SQL文に追加されるかどうか条件分岐しします。
さらにwhereタグで囲うことで、もし追加される文があるならば、SQL文にWHEREが追加され、追加された文がSELECT文の条件として機能します。
MyBatisを用いないSpringBootでも、PreparedStatementなどを用いて同じように条件分岐したSQL文を書くことはできますが、MyBatisを用いてかなり簡単に書くことができました。
ただ、単純なSQL文の実行ならxmlファイルを作らずにアノテーションに書いてしまう方が便利そうです。
以下に作ったファイルを残します。
今回はMyBatisのお試しのため、Todoリストの使いやすさにはこだわらなかったため、その点はお許しください。
また、実際に使うことは考えていないためh2データベースを使っていて、springbootを実行し直したら、入力したデータが初期状態に戻ってしまうのでご注意ください。
TaskListController.java
package com.example.demo.apps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import com.example.demo.entity.Task;
import com.example.demo.mapper.TaskMapper;
@Controller
public class TaskListController {
@Autowired
private TaskMapper taskMapper;
@GetMapping("/")
public ModelAndView listAll() {
ModelAndView mav = new ModelAndView("index");
mav.addObject("tasks", taskMapper.selectAll());
mav.addObject("search", "all");
return mav;
}
@GetMapping("/search")
public ModelAndView search(Task task) {
ModelAndView mav = new ModelAndView("index");
mav.addObject("tasks", taskMapper.search(task));
mav.addObject("searchWord", task.getTask());
if(task.getAlready() != null){
mav.addObject("search", task.getAlready());
}else{
mav.addObject("search", "all");
}
return mav;
}
@GetMapping("/insert")
public ModelAndView moveInsert() {
ModelAndView mav = new ModelAndView("insertTask");
return mav;
}
@PostMapping("/confirmInsert")
public String confirmInsert(Task task) {
taskMapper.insert(task);
return "redirect:/";
}
@GetMapping("/update")
public ModelAndView moveUpdate() {
ModelAndView mav = new ModelAndView("updateTask");
return mav;
}
@PostMapping("/confirmUpdate")
public String confirmUpdate(Task task) {
taskMapper.update(task);
return "redirect:/";
}
@GetMapping("/delete")
public ModelAndView moveDelete() {
ModelAndView mav = new ModelAndView("deleteTask");
return mav;
}
@PostMapping("/confirmDelete")
public String confirmDelete(Task task) {
taskMapper.delete(task);
return "redirect:/";
}
@PostMapping("/done")
public String done(Task task) {
taskMapper.updateAlready(task);
return "redirect:/";
}
}
TaskMapper.java
package com.example.demo.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.entity.Task;
@Mapper
public interface TaskMapper {
/**
* 全ユーザーを取得
*
* @return 全ユーザーの情報
*
*/
List<Task> selectAll();
/**
* ユーザーの更新
* @param task 更新するユーザーの情報
*/
void update(Task task);
void updateAlready(Task task);
/**
* ユーザーの登録
* @param task 登録するユーザーの情報
*/
void insert(Task task);
/**
* ユーザーの削除
* @param task 削除するユーザーの情報
*/
void delete(Task task);
/**
* ユーザーの検索
*
* @return 入力した情報に一致したユーザーの情報
*
*/
List<Task> search(Task task);
}
Task.java
package com.example.demo.entity;
import lombok.Data;
@Data
public class Task {
private int taskId;
private String task;
private int manHours;
private String already;
public int getTaskId() {
return taskId;
}
public String getTask() {
return task;
}
public int getManHours() {
return manHours;
}
public void setTaskId(int taskId) {
this.taskId = taskId;
}
public void setManHours(int manHours) {
this.manHours = manHours;
}
public String getAlready() {
return already;
}
public void setAlready(String already) {
this.already = already;
}
public void setTask(String task) {
this.task = task;
}
}
schema.sql
DROP TABLE IF EXISTS TASK;
CREATE TABLE TASK (
ID INT AUTO_INCREMENT,
TASK VARCHAR(50),
MANHOURS INT,
ALREADY BOOLEAN NOT NULL DEFAULT 0,
PRIMARY KEY(ID)
);
index.html
<!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a th:href="@{/insert}" >タスク追加</a>
<a th:href="@{/update}" >タスク変更</a>
<a th:href="@{/delete}" >タスク削除</a>
<h1>Tasks</h1>
<form th:action="@{/search}" method="get">
<input type="text" name="task" th:value="${searchWord}">
<input type="submit" value="検索">
</form>
<form th:action="@{/search}" method="get">
<input type="radio" name="already" value="all" th:checked="${search == 'all'}">すべて
<input type="radio" name="already" value="false" th:checked="${search == 'false'}">未
<input type="radio" name="already" value="true" th:checked="${search == 'true'}">済
<input type="submit" value="検索">
</form>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>タスク</th>
<th>工数</th>
<th>済</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="task : ${tasks}">
<form action="/done" method="post">
<td th:text="${task.taskId}"></td>
<input name="taskId" th:value="${task.taskId}" type="hidden">
<td th:text="${task.task}"></td>
<td th:text="${task.manHours}"></td>
<block th:if="${task.already}">
<td><input name="already" value="done" type="checkbox" checked></td>
</block>
<block th:if="${!task.already}">
<td><input name="already" value="done" type="checkbox"></td>
</block>
<td><input type="submit" value="更新"></td>
</form>
</tr>
</tbody>
</table>
</body>
</html>
insertTask.html
<!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Insert Task</h1>
<form th:action="@{/confirmInsert}" method="post">
<input name="task" type="text">
<input name="manHours" type="number">
<input type="submit" value="送信">
</form>
</body>
</html>
updateTask.html
<!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Update Task</h1>
<form th:action="@{/confirmUpdate}" method="post">
<input name="taskId" type="text">
<input name="task" type="text">
<input name="manHours" type="number">
<input type="submit" value="送信">
</form>
</body>
</html>
deleteTask.html
<!DOCTYPE html>
<html lang="jp" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Delete Task</h1>
<form th:action="@{/confirmDelete}" method="post">
<input name="taskId" type="text">
<input type="submit" value="送信">
</form>
</body>
</html>
おまけ
上記のコードを用いたときのUIはこんな感じです。
この記事が気に入ったらサポートをしてみませんか?