LoginSignup
80
81

More than 3 years have passed since last update.

SpringBootでToDoアプリを作ってみよう【誰でも作れます・初心者向け】

Last updated at Posted at 2021-02-17

はじめに

SpringBootを使ってToDoアプリを作っていきます。
一度動くものを作ってみるとイメージしやすいと思うので、
これからSpringBootの学習を始める方、参考にしてみてください。

基本的な機能のみですが、SpringBootの動く感覚をつかめると思います。
エラーや分からない所、コメントしてください。

(OSはwindowsです)

駆け出しなので、おかしな点もあるかもしれません。
教えてもらえたら嬉しいです。

  • テンプレートエンジン Thymeleaf
  • データベース PostgreSQL
  • ORマッパー  Mybatis

勉強始めたばかりの方は、PostgreSQLを使うという事を懸念せず、
SpringBootの勉強だと思って、やってみてください。

同じ方が簡単な気がします。もちろんMySQLでやってもいいです。

最終的なディレクトリ構成

無題.png

流れ

  • PostgreSQLの準備
  • SpringBootプロジェクトの作成
  • DB接続設定
  • ビューからDBまで動かしてみる
    • Entityの作成
    • Mapperの作成
    • XMLファイルの作成
    • Thymeleafの作成
    • Controllerの作成
    • 確認
  • 追加機能を加える
  • 更新機能を加える
  • 完了機能を加える
    • 完了ボタンを作る
    • 未完了のみ表示する
    • 完了も表示する
  • 削除機能を加える

PostgreSQLの準備

インストールについては、こちらの方の記事を参考にしてください。
PostgreSQL を Windows にインストールするには

記事通りに進めるとインストール時にエラーが出てしまったので、
ダウンロードのみは、ここからしました。
あとは、そのまま説明通りに進めてください。

インストールができたら、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を選択します。初期値のままです。
名前等は好きにつけて構いません。
ですが、練習なので、同じで大丈夫だと思います。
無題.png
依存関係は、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が作成されたので、編集していきます。
これは、テーブルのデータを保持するためのものです。

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 情報から自動で作成してくれます。

TodoMapper.java
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文を記述していきます。

TodoMapper.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.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リストのタイトルを表示させます。

index.html
<!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からデータを取得し、そのデータを埋め込んで、ブラウザに表示します。

TodoController.java
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文を実行してみてください。
ブラウザのご飯を食べるの下に、追加されていきます。
無題.png

追加機能を加える

タスクの追加機能を追加していきます。

Mapperを編集します。
メソッドを定義します。
ビュー側(index.html)で入力したデータをDBに追加するので、引数が必要です。
この引数のtodoはXMLファイルのSQLで使います。

TodoMapper.java
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を追加します。

TodoMapper.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.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メソッドにリダイレクトしています。

TodoController.java
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が実行されます。

index.html
<!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>

これで、追加できるようになりました。

無題.png

更新機能を加える

タスクの編集・更新機能を追加していきます。
今回は、1つのタスクごとに更新する事にします。

Mapperを編集します。
メソッドを定義します。
ビュー側(index.html)で入力したデータを受け取ってDBを更新するので、引数が必要です。

TodoMapper.java
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を追加します。

TodoMapper.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.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とほとんど同じです。

TodoController.java
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を取得できるようにする
index.html
<!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のセットが何個もできる感じです。

タスク一覧と追加の箇所の区別がしづらくなったので、見出しをつけておきました。

これで、更新できるようになりました。
※日付を入力しないとエラーになります。
※更新する場合も、日付を入れてください。

無題.png

完了機能を加える

完了ボタンを作る

thymeleafを編集します。
タスクの横に、完了したら押すためのボタンを作っていきます。

input(checkbox)を1行だけ追加しました。
完了したら、このボタンを押します。
チェックがついている時に、valueの値(1)を返します。

index.html
<!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文だけ編集していきます。

TodoMapper.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.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も同時に更新するようにしました。

無題.png

まだ画面に変化は現れないので、確認したければ
SQL Shell psqlで以下を実行してみてください。

todo_db=> select * from todo_items;

未完了のみ表示する

未完了のみを表示していきます。

Mapperを編集します。

未完了のみを検索するselectIncompleteメソッドを定義します。

TodoMapper.java
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文に、未完了のみを取得するという条件が加わります。

TodoMapper.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.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が呼べるようになりました。
表示する件数が変わるだけで、他はほとんど変わっていません。

TodoController.java
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メソッドを定義します。

TodoMapper.java
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を実装していきます。

TodoMapper.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.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を両方読んで、
完了済みのリストと未完了のリストをそれぞれ作りました。
それぞれのリストを、名前を付けてビュー側に渡しています。

TodoController.java
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を編集します。

index.html
<!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"で取り消し線を引いています。

これで、完了と未完了分けての表示ができました。
今は、チェックボックスにチェックなしで、更新すると未完了にもどるので、変な感じです(笑)

無題.png

削除機能を加える

完了済みが増え続けてしまうので、一括で削除できるようにします。

Mapperを編集します。

deleteメソッドを定義します。
一括削除なので、引数も戻り値もなしにします。

TodoMapper.java
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を実装していきます。

TodoMapper.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.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」で呼び出すメソッドを作成します。

TodoController.java
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を実行します。

index.html
<!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>

削除ボタンを作成することができました。

無題.png

ここを押すと、完了済みが一気に消せます。
これで、データベースへの追加・削除・更新・検索の機能は実装することができました。

まとめ

本当に簡単な機能だけですが、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

80
81
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
80
81