#はじめに
SpringBootを使ってToDoアプリを作っていきます。
一度動くものを作ってみるとイメージしやすいと思うので、
これからSpringBootの学習を始める方、参考にしてみてください。
基本的な機能のみですが、SpringBootの動く感覚をつかめると思います。
エラーや分からない所、コメントしてください。
(OSはwindowsです)
駆け出しなので、おかしな点もあるかもしれません。
教えてもらえたら嬉しいです。
- テンプレートエンジン Thymeleaf
- データベース PostgreSQL
- ORマッパー Mybatis
勉強始めたばかりの方は、PostgreSQLを使うという事を懸念せず、
SpringBootの勉強だと思って、やってみてください。
同じ方が簡単な気がします。もちろんMySQLでやってもいいです。
#最終的なディレクトリ構成
#流れ#
- PostgreSQLの準備
- SpringBootプロジェクトの作成
- DB接続設定
- ビューからDBまで動かしてみる
- Entityの作成
- Mapperの作成
- XMLファイルの作成
- Thymeleafの作成
- Controllerの作成
- 確認
- 追加機能を加える
- 更新機能を加える
- 完了機能を加える
- 完了ボタンを作る
- 未完了のみ表示する
- 完了も表示する
- 削除機能を加える
#PostgreSQLの準備#
インストールについては、こちらの方の記事を参考にしてください。
PostgreSQL を Windows にインストールするには
記事通りに進めるとインストール時にエラーが出てしまったので、
ダウンロードのみは、[ここ](https: //get.enterprisedb.com/postgresql/postgresql-11.2-1-windows-x64.exe)からしました。
あとは、そのまま説明通りに進めてください。
インストールができたら、ToDoアプリで使用するDBを作っていきます。
参考にした記事で使用されていた、「SQL Shell (psql)」を開きます。
ユーザーを作成します。
postgres=# create role todo_user login password 'todo_pass';
ユーザーが作成されたことを確認します。
postgres=# select usename from pg_user;
usename
-----------
postgres
todo_user
(2 行)
作成されていることが確認できました。
データベースを作成します。
postgres=# create database todo_db with owner = todo_user;
データベースが作成された事を確認します。
postgres=# \l
データベース一覧
名前 | 所有者 | エンコーディング | 照合順序 | Ctype(変換演算子) | アクセス権限
-----------+-----------+------------------+----------+-------------------+-----------------------
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C | C | =c/postgres +
| | | | | postgres=CTc/postgres
todo_db | todo_user | UTF8 | C | C |
(4 行)
作成されている事が確認できました。
作成したデータベースに、作成したユーザーで接続してみます。
postgres=# \connect todo_db todo_user
ユーザ todo_user のパスワード:
データベース "todo_db" にユーザ "todo_user" として接続しました。
接続できました。
テーブルを作成します。
今回は、練習なので、簡単にこんな感じにします。
todo_db=> create table todo_items(
todo_db(> id serial,
todo_db(> title varchar(40),
todo_db(> done_flg numeric(1) default 0,
todo_db(> time_limit date);
CREATE TABLE
※どういう時にどのデータ型が適切かがまだわかってないので、大目に見てください。
テーブルの確認してみます。
todo_db=> \d todo_items
テーブル "public.todo_items"
列 | 型 | 照合順序 | Null 値を許容 | デフォルト
------------+-----------------------+----------+---------------+----------------------------------------
id | integer | | not null | nextval('todo_items_id_seq'::regclass)
title | character varying(40) | | |
done_flg | numeric(1,0) | | | 0
time_limit | date | | |
PostgreSQL、DBの準備はこんな感じです。
#SpringBootプロジェクトの作成#
eclipseを起動します。
起動したら、「ファイル(F)」→「新規(N)」→「プロジェクト(R)...」と進み
「Spring スターター・プロジェクト」を選択します。
eclipseのインストールやSpringBootプロジェクト作成方法はこちらの記事も参考にしてみてください。
https://qiita.com/toki_k/items/52c222433823268472a1
今回は、Maven、Jarを選択します。初期値のままです。
名前等は好きにつけて構いません。
ですが、練習なので、同じで大丈夫だと思います。
依存関係は、Lombok・Mybatis Framework・PostgreSQL Driver・Thymeleaf・Spring Webを選択しました。
選択した依存関係は、pom.xmlで確認できます。依存関係の追加などもこのファイルでできます。
1度確認してみてください。
これで、SpringBootプロジェクトの作成ができました。
#DB接続設定#
先程、作成したDBに接続するために、「application.properties」を編集します。
src/main/resourcesの下のapplication.propertiesを開きます。
今は、何も書かれていないです。
以下の記述を加えることで、DBに接続できるようになります。
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/todo_db
spring.datasource.username=todo_user
spring.datasource.password=todo_pass
todo_db、todo_user、todo_passは先ほど、自分が作成したものです。
#ビューからDBまで動かしてみる#
##Entityの作成
VO、Entity、Dtoとかの違いについて、あまり理解できていないので、
これから勉強していきます。すみません。
今回はEntityとします。
src/main/java/com/todo/appの下に、「entity」パッケージを作成します。
app(com.todo.app)を右クリックし、「新規(W)」→「パッケージ」と進みます。
名前欄の初期値が「com.todo.app」となっているので、「com.todo.app.entity」とします。
※簡単なアプリケーションではパッケージを分ける必要はないようですが、応用する時のため分けておきます。
パッケージの下にTodo.javaクラスを作成します。
entityを右クリックし、「新規(W)」→「クラス」と進みます。
名前(M)に「Todo」と入力し、完了を押します。
Todo.javaが作成されたので、編集していきます。
これは、テーブルのデータを保持するためのものです。
package com.todo.app.entity;
import java.sql.Date;
import lombok.Data;
@Data
public class Todo {
private long id;
private String title;
private int done_flg;
private String time_limit;
}
@Dataは、lombokのGetter・Setter自動生成のためのものです。
以下のようなものを1つずつ記述する必要がなくなります。
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
##Mapperの作成
RepositoryやDAO、Mapperとかの違いについて、あまり理解できていないので、
これから勉強していきます。すみません。
今回はMapperとします。
src/main/java/com/todo/appの下に、「mapper」パッケージを作成します。
作り方は、先程と同様です。
パッケージの下にTodoMapper.javaクラスを作成します。
mapperを右クリックし、「新規(W)→「インターフェース」と進みます。
名前(M)に「TodoMapper」と入力し、完了を押します。
TodoMapper.javaが作成されたので、編集していきます。
これは、DBを操作するメソッドを呼び出すためのインターフェースです。
このインターフェースから、メソッドを呼び出します。
このインターフェースの具象クラスは、 MyBatis が XML の SQL 情報から自動で作成してくれます。
package com.todo.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.todo.app.entity.Todo;
@Mapper
public interface TodoMapper {
public List<Todo> selectAll();
}
@Mapperをつけるだけで、Mapperのインターフェースとなります。
今回は、selectAll()に対応するSQL文は、XMLファイルに記載していきます。
※このインターフェースに書いて、SQLを実装する事も可能です。
##XMLファイルの作成
Mapperインターフェースと同じ構成のフォルダを作成し、XMLファイルを作成していきます。
src/main/resourcesを右クリックし、
「新規(W)」→「フォルダー」と進みます。
フォルダー名に、「com/todo/app/mapper」と入力し、完了します。
※パッケージのように、「.」でつなぐとエラーになるようです。
mapperフォルダを右クリックし、
「新規(W)」→「その他」→「XMLファイル」と進みます。
ファイル名は、「TodoMapper.xml」とします。
TodoMapper.xmlファイルが作成されたので、編集していきます。
ここには、SQL文を記述していきます。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
</mapper>
ここでは、todo_items(テーブル)から、すべてのデータをselectしています。
Todoクラス型で返されています。Todoクラスの変数にそれぞれ値が入ります。
idがメソッド名、resultTypeが戻される型です。
##Thymeleafの作成
画面に表示される部分を作っていきます。
src/main/resources/templatesの下に、index.htmlを作成します。
index.htmlが作成できたら、編集していきます。
今は、TodoListという文字と、Todoリストのタイトルを表示させます。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>TodoApp</title>
</head>
<body>
<h1>TodoList</h1>
<p th:each="todo : ${todos}" th:text="${todo.title}" />
</body>
</html>
${todos}の部分はリストで、Controllerからデータをもってきます。
リストを1つずつ、todoとして格納し、画面に表示させていきます。リストの数だけ、<p>が表示されます。
th:text="${todo.title}"が実際に表示されるテキストです。Todoのtitleが表示されます。
##Controllerの作成
src/main/java/com/todo/appの下に、「controller」パッケージを作成します。
controllerパッケージの下に、「TodoController.java」を作成します。
TodoController.javaが作成できたら、編集していきます。
このクラスから、Mapperインターフェースのメソッドを呼び出し、
そのメソッドによってDBからデータを取得し、そのデータを埋め込んで、ブラウザに表示します。
package com.todo.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.todo.app.entity.Todo;
import com.todo.app.mapper.TodoMapper;
@Controller
public class TodoController {
@Autowired
TodoMapper todoMapper;
@RequestMapping(value="/")
public String index(Model model) {
List<Todo> list = todoMapper.selectAll();
model.addAttribute("todos",list);
return "index";
}
}
@Controllerでコントローラだと認識されます。
@RequestMappingに対応するURLが入力された時にこのメソッドが呼び出されます。
今回なら、「http://localhost:8080/ 」です。(value="/todo")の場合は、「http://localhost:8080/todo 」です。
todoMapper.selectAll()でインターフェースのメソッドを実行し、
xml内のSQLの結果を戻り値としてlistに格納しています。
listの値にtodosという名前を付けて、ビュー(index.html側)に渡します。
Thymeleafの${todos}の部分になります。
returnで先程作ったindex.htmlを指定しています。
##確認
プロジェクトを実行します。
todoappを右クリックし、「実行(R)」→「Spring Boot App」を選択します。
実効ができたら、ブラウザを開き、以下のURLを入力します。
「http://localhost:8080/ 」
成功していれば、画面にTodoListが表示されます。
次に、「SQL Shell(psql)」を使って、テーブルにデータを格納してみます。
はじめに行ったように、todo_userでtodo_dbに接続します。
接続できたら、以下のinsert文を実行してみます。
今は、期限の値はなしで、題名の値だけを追加しています。
todo_db=> insert into todo_items (title) values ('ご飯を食べる![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506867/0fb055bd-f665-a932-54b6-70949ba92e38.png)
');
もう1度ブラウザを開き、以下のURLを入力します。(先ほどのブラウザが開いてあれば、リロードするだけ)
「http://localhost:8080/ 」
TodoListの下に、ご飯を食べるが追加されれば成功です。
成功したら、他にも自分の好きな値を入れてinsert文を実行してみてください。
ブラウザのご飯を食べるの下に、追加されていきます。
#追加機能を加える
タスクの追加機能を追加していきます。
Mapperを編集します。
メソッドを定義します。
ビュー側(index.html)で入力したデータをDBに追加するので、引数が必要です。
この引数のtodoはXMLファイルのSQLで使います。
package com.todo.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.todo.app.entity.Todo;
@Mapper
public interface TodoMapper {
public List<Todo> selectAll();
public void add(Todo todo); // ここを追加しました。
}
XMLファイルを編集します。
addメソッドに対応するSQLを追加します。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
<insert id="add" parameterType="com.todo.app.entity.Todo">
insert into todo_items (title,time_limit)
values (#{title},to_date(#{time_limit},'yy-mm-dd'))
</insert>
</mapper>
to_date(#{time_limit},'yy-mm-dd')はString型の日付をデータ型で格納しています。
Controllerを編集します。
「/add」で呼び出すメソッドを作成します。
呼び出しと同時にビュー側から渡される値を、Mapperのaddメソッドの引数に渡しています。
メソッドを実行した後、indexメソッドにリダイレクトしています。
package com.todo.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.todo.app.entity.Todo;
import com.todo.app.mapper.TodoMapper;
@Controller
public class TodoController {
@Autowired
TodoMapper todoMapper;
@RequestMapping(value="/")
public String index(Model model) {
List<Todo> list = todoMapper.selectAll();
model.addAttribute("todos",list);
return "index";
}
@RequestMapping(value="/add")
public String add(Todo todo) {
todoMapper.add(todo);
return "redirect:/";
}
}
thymeleafを編集します。
2つの入力欄と追加ボタンを作成します。
入力欄に入力した値が、コントローラのメソッドの引数(Todo)になります。
name属性の値と同名の(Todoの)フィールドに、それぞれ入力値が入ります。
追加ボタン(submit)を押すと、コントローラの/addが実行されます。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>TodoApp</title>
</head>
<body>
<h1>TodoList</h1>
<p th:each="todo : ${todos}" th:text="${todo.title}" />
<form method="post" th:action="@{/add}">
<input type="text" name="title" />
<input type="date" name="time_limit"/>
<input type="submit" value="追加" />
</form>
</body>
</html>
これで、追加できるようになりました。
#更新機能を加える
タスクの編集・更新機能を追加していきます。
今回は、1つのタスクごとに更新する事にします。
Mapperを編集します。
メソッドを定義します。
ビュー側(index.html)で入力したデータを受け取ってDBを更新するので、引数が必要です。
package com.todo.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.todo.app.entity.Todo;
@Mapper
public interface TodoMapper {
public List<Todo> selectAll();
public void add(Todo todo);
public void update(Todo todo); //ここを追加しました。
}
XMLファイルを編集します。
updateメソッドに対応するSQLを追加します。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
<insert id="add" parameterType="com.todo.app.entity.Todo">
insert into todo_items (title,time_limit)
values (#{title},to_date(#{time_limit},'yy-mm-dd'))
</insert>
<update id="update" parameterType="com.todo.app.entity.Todo">
update todo_items set
title = #{title},
time_limit = to_date(#{time_limit},'yy-mm-dd')
where id = #{id}
</update>
</mapper>
テーブルのどのデータを更新するのか、whereを使い識別する必要があります。
add()では、title、time_limitだけ充分でしたが、今回は識別に使うidが必要です
このidはindex.htmlから持ってきます。
Controllerを編集します。
「/update」で呼び出すメソッドを作成します。
addとほとんど同じです。
package com.todo.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.todo.app.entity.Todo;
import com.todo.app.mapper.TodoMapper;
@Controller
public class TodoController {
@Autowired
TodoMapper todoMapper;
@RequestMapping(value="/")
public String index(Model model) {
List<Todo> list = todoMapper.selectAll();
model.addAttribute("todos",list);
return "index";
}
@RequestMapping(value="/add")
public String add(Todo todo) {
todoMapper.add(todo);
return "redirect:/";
}
@RequestMapping(value="/update")
public String update(Todo todo) {
todoMapper.update(todo);
return "redirect:/";
}
}
thymeleafを編集します。
ビュー側の編集していきます。
ここでやる事は以下3つです。
- 文字を書き換えられるようにする
- 更新ボタンをつける
- idを取得できるようにする
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>TodoApp</title>
</head>
<body>
<h1>TodoList</h1>
<h3>マイタスク</h3>
<form method="post" th:action="@{/update}" th:each="todo : ${todos}" >
<input type="hidden" name="id" th:value="${todo.id}" />
<input type="text" name="title"th:value="${todo.title}" />
<input type="date" name="time_limit"th:value="${todo.time_limit}" />
<input type="submit" value="更新" />
</form>
<h3>新しいタスクを追加</h3>
<form method="post" th:action="@{/add}">
<input type="text" name="title" />
<input type="date" name="time_limit"/>
<input type="submit" value="追加" />
</form>
</body>
</html>
追加の時と同じようにformにしました。
追加と違うのは、以下3点です。
- ループさせる必要がある (編集前の一覧と同様)
- 初めから値が入っている (編集前の一覧と同様)
- idは隠す (type="hidden"で隠せます)
実際には、このformのセットが何個もできる感じです。
タスク一覧と追加の箇所の区別がしづらくなったので、見出しをつけておきました。
これで、更新できるようになりました。
※日付を入力しないとエラーになります。
※更新する場合も、日付を入れてください。
#完了機能を加える
##完了ボタンを作る
thymeleafを編集します。
タスクの横に、完了したら押すためのボタンを作っていきます。
input(checkbox)を1行だけ追加しました。
完了したら、このボタンを押します。
チェックがついている時に、valueの値(1)を返します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>TodoApp</title>
</head>
<body>
<h1>TodoList</h1>
<h3>マイタスク</h3>
<form method="post" th:action="@{/update}" th:each="todo : ${todos}" >
<input type="checkbox"name="done_flg" value="1"/>
<input type="hidden" name="id" th:value="${todo.id}" />
<input type="text" name="title"th:value="${todo.title}" />
<input type="date" name="time_limit"th:value="${todo.time_limit}" />
<input type="submit" value="更新" />
</form>
<h3>新しいタスクを追加</h3>
<form method="post" th:action="@{/add}">
<input type="text" name="title" />
<input type="date" name="time_limit"/>
<input type="submit" value="追加" />
</form>
</body>
</html>
更新ボタンで、完了しているかどうかの値も送信するようになりました。
XMLファイルを編集します。
ControllerとMapperインターフェースに変更はないので、
XMLのSQL文だけ編集していきます。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
<insert id="add" parameterType="com.todo.app.entity.Todo">
insert into todo_items (title,time_limit)
values (#{title},to_date(#{time_limit},'yy-mm-dd'))
</insert>
<update id="update" parameterType="com.todo.app.entity.Todo">
update todo_items set
title = #{title},
time_limit = to_date(#{time_limit},'yy-mm-dd'),
done_flg = #{done_flg}
where id = #{id}
</update>
</mapper>
done_flgも同時に更新するようにしました。
まだ画面に変化は現れないので、確認したければ
SQL Shell psqlで以下を実行してみてください。
todo_db=> select * from todo_items;
##未完了のみ表示する
未完了のみを表示していきます。
Mapperを編集します。
未完了のみを検索するselectIncompleteメソッドを定義します。
package com.todo.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.todo.app.entity.Todo;
@Mapper
public interface TodoMapper {
public List<Todo> selectAll();
public List<Todo> selectIncomplete();
public void add(Todo todo);
public void update(Todo todo);
}
XMLファイルを編集します。
selectIncompleteを実装していきます。
select文に、未完了のみを取得するという条件が加わります。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
<select id="selectIncomplete" resultType="com.todo.app.entity.Todo">
select * from todo_items where done_flg = 0
</select>
<insert id="add" parameterType="com.todo.app.entity.Todo">
insert into todo_items (title,time_limit)
values (#{title},to_date(#{time_limit},'yy-mm-dd'))
</insert>
<update id="update" parameterType="com.todo.app.entity.Todo">
update todo_items set
title = #{title},
time_limit = to_date(#{time_limit},'yy-mm-dd'),
done_flg = #{done_flg}
where id = #{id}
</update>
</mapper>
Controllerを編集します。
今は、1度、selectAllを消しておきます。(コメントにした)
selectAllの代わりに、selectIncompleteが呼べるようになりました。
表示する件数が変わるだけで、他はほとんど変わっていません。
package com.todo.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.todo.app.entity.Todo;
import com.todo.app.mapper.TodoMapper;
@Controller
public class TodoController {
@Autowired
TodoMapper todoMapper;
@RequestMapping(value="/")
public String index(Model model) {
// List<Todo> list = todoMapper.selectAll();
List<Todo> list = todoMapper.selectIncomplete();
model.addAttribute("todos",list);
return "index";
}
@RequestMapping(value="/add")
public String add(Todo todo) {
todoMapper.add(todo);
return "redirect:/";
}
@RequestMapping(value="/update")
public String update(Todo todo) {
todoMapper.update(todo);
return "redirect:/";
}
}
これで、ブラウザを起動してみると、未完了のタスクのみが表示されるようになりました。
完了ボタンを押して、更新すると画面から消えます。
##完了も表示する
完了したタスクも表示していきます。
Mapperを編集します。
完了のみを検索するselectCompleteメソッドを定義します。
package com.todo.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.todo.app.entity.Todo;
@Mapper
public interface TodoMapper {
public List<Todo> selectAll();
public List<Todo> selectIncomplete();
public List<Todo> selectComplete();
public void add(Todo todo);
public void update(Todo todo);
}
XMLファイルを編集します。
selectCompleteを実装していきます。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
<select id="selectIncomplete" resultType="com.todo.app.entity.Todo">
select * from todo_items where done_flg = 0
</select>
<select id="selectComplete" resultType="com.todo.app.entity.Todo">
select * from todo_items where done_flg = 1
</select>
<insert id="add" parameterType="com.todo.app.entity.Todo">
insert into todo_items (title,time_limit)
values (#{title},to_date(#{time_limit},'yy-mm-dd'))
</insert>
<update id="update" parameterType="com.todo.app.entity.Todo">
update todo_items set
title = #{title},
time_limit = to_date(#{time_limit},'yy-mm-dd'),
done_flg = #{done_flg}
where id = #{id}
</update>
</mapper>
Controllerを編集します。
selectIncompleteとselectCompleteを両方読んで、
完了済みのリストと未完了のリストをそれぞれ作りました。
それぞれのリストを、名前を付けてビュー側に渡しています。
package com.todo.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.todo.app.entity.Todo;
import com.todo.app.mapper.TodoMapper;
@Controller
public class TodoController {
@Autowired
TodoMapper todoMapper;
@RequestMapping(value="/")
public String index(Model model) {
// List<Todo> list = todoMapper.selectAll();
List<Todo> list = todoMapper.selectIncomplete();
List<Todo> doneList = todoMapper.selectComplete();
model.addAttribute("todos",list);
model.addAttribute("doneTodos",doneList);
return "index";
}
@RequestMapping(value="/add")
public String add(Todo todo) {
todoMapper.add(todo);
return "redirect:/";
}
@RequestMapping(value="/update")
public String update(Todo todo) {
todoMapper.update(todo);
return "redirect:/";
}
}
thymeleafを編集します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>TodoApp</title>
</head>
<body>
<h1>TodoList</h1>
<h3>マイタスク</h3>
<form method="post" th:action="@{/update}" th:each="todo : ${todos}" >
<input type="checkbox"name="done_flg" value="1"/>
<input type="hidden" name="id" th:value="${todo.id}" />
<input type="text" name="title"th:value="${todo.title}" />
<input type="date" name="time_limit"th:value="${todo.time_limit}" />
<input type="submit" value="更新" />
</form>
<h3>完了済み</h3>
<form method="post" th:action="@{/update}" th:each="todo : ${doneTodos}" >
<input type="checkbox"name="done_flg" value="1"/>
<input type="hidden" name="id" th:value="${todo.id}" />
<input type="text" name="title"th:value="${todo.title}" style="text-decoration:line-through"/>
<input type="date" name="time_limit"th:value="${todo.time_limit}" />
<input type="submit" value="更新" />
</form>
<h3>新しいタスクを追加</h3>
<form method="post" th:action="@{/add}">
<input type="text" name="title" />
<input type="date" name="time_limit"/>
<input type="submit" value="追加" />
</form>
</body>
</html>
完了済みのリストを作成しました。
完了した感を出すために、取り消し線を引きます。
style="text-decoration:line-through"で取り消し線を引いています。
これで、完了と未完了分けての表示ができました。
今は、チェックボックスにチェックなしで、更新すると未完了にもどるので、変な感じです(笑)
#削除機能を加える
完了済みが増え続けてしまうので、一括で削除できるようにします。
Mapperを編集します。
deleteメソッドを定義します。
一括削除なので、引数も戻り値もなしにします。
package com.todo.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.todo.app.entity.Todo;
@Mapper
public interface TodoMapper {
public List<Todo> selectAll();
public List<Todo> selectIncomplete();
public List<Todo> selectComplete();
public void add(Todo todo);
public void update(Todo todo);
public void delete();
}
XMLファイルを編集します。
deleteを実装していきます。
<?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.todo.app.mapper.TodoMapper">
<select id="selectAll" resultType="com.todo.app.entity.Todo">
select * from todo_items
</select>
<select id="selectIncomplete" resultType="com.todo.app.entity.Todo">
select * from todo_items where done_flg = 0
</select>
<select id="selectComplete" resultType="com.todo.app.entity.Todo">
select * from todo_items where done_flg = 1
</select>
<insert id="add" parameterType="com.todo.app.entity.Todo">
insert into todo_items (title,time_limit)
values (#{title},to_date(#{time_limit},'yy-mm-dd'))
</insert>
<update id="update" parameterType="com.todo.app.entity.Todo">
update todo_items set
title = #{title},
time_limit = to_date(#{time_limit},'yy-mm-dd'),
done_flg = #{done_flg}
where id = #{id}
</update>
<delete id="delete" parameterType="com.todo.app.entity.Todo">
delete from todo_items where done_flg = 1
</delete>
</mapper>
Controllerを編集します。
「/delete」で呼び出すメソッドを作成します。
package com.todo.app.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.todo.app.entity.Todo;
import com.todo.app.mapper.TodoMapper;
@Controller
public class TodoController {
@Autowired
TodoMapper todoMapper;
@RequestMapping(value="/")
public String index(Model model) {
// List<Todo> list = todoMapper.selectAll();
List<Todo> list = todoMapper.selectIncomplete();
List<Todo> doneList = todoMapper.selectComplete();
model.addAttribute("todos",list);
model.addAttribute("doneTodos",doneList);
return "index";
}
@RequestMapping(value="/add")
public String add(Todo todo) {
todoMapper.add(todo);
return "redirect:/";
}
@RequestMapping(value="/update")
public String update(Todo todo) {
todoMapper.update(todo);
return "redirect:/";
}
@RequestMapping(value="/delete")
public String delete() {
todoMapper.delete();
return "redirect:/";
}
}
thymeleafを編集します。
削除ボタンを作成します。
submitすることで、Controllerの/deleteを実行します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>TodoApp</title>
</head>
<body>
<h1>TodoList</h1>
<h3>マイタスク</h3>
<form method="post" th:action="@{/update}" th:each="todo : ${todos}" >
<input type="checkbox"name="done_flg" value="1"/>
<input type="hidden" name="id" th:value="${todo.id}" />
<input type="text" name="title"th:value="${todo.title}" />
<input type="date" name="time_limit"th:value="${todo.time_limit}" />
<input type="submit" value="更新" />
</form>
<h3>完了済み</h3>
<form method="post" th:action="@{/update}" th:each="todo : ${doneTodos}" >
<input type="checkbox"name="done_flg" value="1"/>
<input type="hidden" name="id" th:value="${todo.id}" />
<input type="text" name="title"th:value="${todo.title}" style="text-decoration:line-through"/>
<input type="date" name="time_limit"th:value="${todo.time_limit}" />
<input type="submit" value="更新" />
</form>
<h3>新しいタスクを追加</h3>
<form method="post" th:action="@{/add}">
<input type="text" name="title" />
<input type="date" name="time_limit"/>
<input type="submit" value="追加" />
</form>
<form method="post" th:action="@{/delete}">
<input type="submit" value="完了済みを削除" />
</form>
</body>
</html>
削除ボタンを作成することができました。
ここを押すと、完了済みが一気に消せます。
これで、データベースへの追加・削除・更新・検索の機能は実装することができました。
#まとめ
本当に簡単な機能だけですが、SpringBootで動くものが作れました。
難しい参考書を使ったり、1つずつ全部調べて時間をかけるよりも先に、
動くものを作って、何となく雰囲気を掴むというのがすごい大事だなと思っています。
自分が今からプログラミングの勉強を始めるとして、役に立つようにと思って書いてきました。
これから、SpringBootの勉強を始めるひとの役に立てれば幸いです。
今は、本当に機能を詰め込んだだけの物なので、好きに改造したり、色々遊んでみてください。
こういう機能だけでなく、例外処理やバリデーションなどを実装してみるのも勉強になると思います。
まだ、すごい駆け出しでキータもあまり書いたことないので、
LGTM1つ来るだけでもすごい喜びます。
もしよかったら、LGTMよろしくお願いします。
間違ってるところやおかしなところ、エラーが出たなどがあったらコメントもお願いします。
長くなってしまいましたがありがとうございました!!
↓ソースファイルです
https://github.com/tokio-k/TodoApp-springboot/tree/intermediate
今回作ったものを、jQueryを使って少し改良した記事です↓
https://qiita.com/toki_k/items/e207157c679018579e21
##参考記事
https://www.kakiro-web.com/postgresql/postgresql-create-user.html
https://qiita.com/tom-sato/items/037b8f8cb4b326710f71
https://www.dbonline.jp/postgresql/table/index1.html
https://www.dbonline.jp/postgresql/database/index2.html
https://www.dbonline.jp/postgresql/type/index5.html
https://mam-mam.net/mytech/show.php?cd=112
https://qiita.com/y_ogawa_naaaa/items/26da3b019f090abcfe36
https://www.shookuro.com/entry/2017/11/23/203318