はじめに
Spring Bootを使うと、最小限の設定でWebアプリケーションを素早く開発できます。
今回はその中でも、学習用として最もシンプルな構成である 「Spring Boot + H2データベース + Thymeleaf」 を使って、
ランチ情報を管理するCRUDアプリ(登録・一覧・更新・削除)を作成してみましょう。
このチュートリアルを通して、以下のポイントを学ぶことができます。
- Spring BootによるWebアプリの基本構成
- H2データベースを使ったデータ永続化(組み込みDBの利用)
- ThymeleafによるHTMLテンプレートとの連携
- コントローラ、リポジトリ、エンティティの役割と関係
環境構築からCRUDの実装までを通して、Spring Bootアプリ開発の流れを一通り体験できます!
プロジェクトの作成
- エクリプスにて新規 > Spring Starter project(名前はLunchApp)
- Build ToolにMaven
- 以下の項目にチェックを入れてプロジェクト立ち上げる

- 各項目の詳細は以下
| チェック項目 | 実際のスターター名 | 役割 | 
|---|---|---|
| H2 Database | com.h2database:h2 | 組み込みDB | 
| Spring Boot DevTools | spring-boot-devtools | 自動リロードなどの開発支援 | 
| Spring Web | spring-boot-starter-web | MVC構築(Tomcat含む) | 
| Spring Data JDBC | spring-boot-starter-data-jdbc | JDBCによるデータアクセス | 
| Thymeleaf | spring-boot-starter-thymeleaf | HTMLテンプレートエンジン | 
テーブルの作成
今回は学習用アプリのため、DBはメモリ上に作成しデータの永続化は行わない。(再起動を行うたびに初期データ3件の状態で始まる)

resourcesフォルダの直下に以下の2枚のファイルを作成する
- schema.sql
CREATE TABLE lunch (
  id INT PRIMARY KEY AUTO_INCREMENT,
  shop VARCHAR(30),
  menu VARCHAR(30)
);- data.sql
INSERT INTO lunch (shop, menu) VALUES
('subway', 'チリチキン'),
('はなまる', 'かけうどん(小)'),
('吉野家', '牛丼(並)');Entityクラスの作成
先ほど作成したテーブル lunches に対応するエンティティクラスを作成する。
demoフォルダを右クリック新規クラス > Lunchクラスを作成する

Lunch.javaの内容は以下
package com.example.demo.entity;
import org.springframework.data.annotation.Id;
public class Lunch {
	@Id
    private int id;
    private String shop;
    private String menu;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getShop() {
        return shop;
    }
    public void setShop(String shop) {
        this.shop = shop;
    }
    public String getMenu() {
        return menu;
    }
    public void setMenu(String menu) {
        this.menu = menu;
    }
}
Spring Data JDBCでは、テーブル名とクラス名、カラム名とフィールド名が自動的に対応するため、
特別なアノテーション指定(@Table, @Columnなど)は不要である。
Repositoryの作成
com.example.demo.repository.LunchRepositoryインターフェイスを作成する

内容は以下
package com.example.demo.repository;
import org.springframework.data.repository.CrudRepository;
import com.example.demo.entity.Lunch;
public interface LunchRepository extends CrudRepository<Lunch,Integer>{
}Controllerの作成
- com.example.demo.controller.LunchController.javaを作成

- 内容は以下
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.demo.repository.LunchRepository;
@Controller
public class LunchController {
	private final LunchRepository lunchRepository;
	
	public LunchController(LunchRepository lunchRepository) {
		this.lunchRepository=lunchRepository;
	}
	//一覧
	@GetMapping("/lunches")
	public String list(Model model) {
		model.addAttribute("lunches",lunchRepository.findAll());
		return "lunch/list";
	}
	
}
View(list.html)の作成
- src/main/resources/templates/lunch/list.htmlを作成

- 内容は以下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ランチ一覧</title>
</head>
<body>
    <h1>ランチ一覧</h1>
    <p>
        <a th:href="@{'/lunches/new'}">新規作成</a>
    </p>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>お店</th>
            <th>メニュー</th>
            <th>操作</th>
        </tr>
        <tr th:each="lunch : ${lunches}">
            <td th:text="${lunch.id}"></td>
            <td th:text="${lunch.shop}"></td>
            <td th:text="${lunch.menu}"></td>
            <td>
                <a th:href="@{'/lunches/edit/' + ${lunch.id}}">編集</a> |
                <a th:href="@{'/lunches/delete/' + ${lunch.id}}"
                   onclick="return confirm('削除してよろしいですか?');">
                   削除
                </a>
            </td>
        </tr>
    </table>
</body>
</html>
アプリの実行
いよいよ実行してみよう。
あらかじめ作成されている、LunchAppApplication.javaを右クリック>実行>Srping Bootアプリケーション

無事に起動したらブラウザから以下のURLを入力
localhost:8080/lunches
以下のように表示されたら成功だ!

新規作成処理
データを新規に挿入する処理を作成する。
LunchControllerに以下を追記する
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.entity.Lunch;
import com.example.demo.repository.LunchRepository;
@Controller
public class LunchController {
    private final LunchRepository lunchRepository;
    public LunchController(LunchRepository lunchRepository) {
        this.lunchRepository = lunchRepository;
    }
    // 一覧
    @GetMapping("/lunches")
    public String list(Model model) {
        model.addAttribute("lunches", lunchRepository.findAll());
        return "lunch/list";
    }
    // 新規作成フォーム
    @GetMapping("/lunches/new")
    public String newLunch(Model model) {
        model.addAttribute("lunch", new Lunch());
        return "lunch/new";
    }
    // 登録処理
    @PostMapping("/lunches")
    public String create(@ModelAttribute Lunch lunch) {
        lunchRepository.save(lunch);
        return "redirect:/lunches";
    }
}
new.htmlの作成
- src/main/resources/templates/lunch/new.htmlを作成

- 内容は以下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>新規ランチ作成</title>
</head>
<body>
    <h1>新規ランチ作成</h1>
    <form th:action="@{/lunches}" th:object="${lunch}" method="post">
        <p>お店:<input type="text" th:field="*{shop}"></p>
        <p>メニュー:<input type="text" th:field="*{menu}"></p>
        <p><button type="submit">登録</button></p>
    </form>
    <p><a th:href="@{/lunches}">戻る</a></p>
</body>
</html>
新規作成ボタンを押して、データを入力して登録ボタンを押そう

データが追加されれば成功だ

更新処理
LunchControllerに以下を追記する
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.entity.Lunch;
import com.example.demo.repository.LunchRepository;
@Controller
public class LunchController {
    private final LunchRepository lunchRepository;
    public LunchController(LunchRepository lunchRepository) {
        this.lunchRepository = lunchRepository;
    }
    // 一覧
    @GetMapping("/lunches")
    public String list(Model model) {
        model.addAttribute("lunches", lunchRepository.findAll());
        return "lunch/list";
    }
    // 新規作成フォーム
    @GetMapping("/lunches/new")
    public String newLunch(Model model) {
        model.addAttribute("lunch", new Lunch());
        return "lunch/new";
    }
    // 登録処理
    @PostMapping("/lunches")
    public String create(@ModelAttribute Lunch lunch) {
        lunchRepository.save(lunch);
        return "redirect:/lunches";
    }
    
    // 編集フォーム表示
    @GetMapping("/lunches/edit/{id}")
    public String edit(@PathVariable Integer id, Model model) {
        Lunch lunch = lunchRepository.findById(id).orElseThrow();
        model.addAttribute("lunch", lunch);
        return "lunch/edit";
    }
    
    // 更新処理
    @PostMapping("/lunches/update")
    public String update(@ModelAttribute Lunch lunch) {
        lunchRepository.save(lunch); // idが存在すればUPDATE
        return "redirect:/lunches";
    }
}
edit.htmlの作成
src/main/resources/templates/lunch/edit.htmlを作成

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>ランチ編集</title>
</head>
<body>
  <h1>ランチ編集</h1>
  <form th:action="@{/lunches/update}" th:object="${lunch}" method="post">
      <!-- 重要:idをhiddenで送信 -->
      <input type="hidden" th:field="*{id}">
      <p>お店:<input type="text" th:field="*{shop}"></p>
      <p>メニュー:<input type="text" th:field="*{menu}"></p>
      <p><button type="submit">更新</button></p>
  </form>
  <p><a th:href="@{/lunches}">戻る</a></p>
</body>
</html>
- 編集ボタンを押してみよう

- 復元されるので編集をして更新ボタンを押す。データが更新されれば成功だ

削除処理
LunchControllerに以下を追記する。
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.entity.Lunch;
import com.example.demo.repository.LunchRepository;
@Controller
public class LunchController {
    private final LunchRepository lunchRepository;
    public LunchController(LunchRepository lunchRepository) {
        this.lunchRepository = lunchRepository;
    }
    // 一覧
    @GetMapping("/lunches")
    public String list(Model model) {
        model.addAttribute("lunches", lunchRepository.findAll());
        return "lunch/list";
    }
    // 新規作成フォーム
    @GetMapping("/lunches/new")
    public String newLunch(Model model) {
        model.addAttribute("lunch", new Lunch());
        return "lunch/new";
    }
    // 登録処理
    @PostMapping("/lunches")
    public String create(@ModelAttribute Lunch lunch) {
        lunchRepository.save(lunch);
        return "redirect:/lunches";
    }
    
    // 編集フォーム表示
    @GetMapping("/lunches/edit/{id}")
    public String edit(@PathVariable Integer id, Model model) {
        Lunch lunch = lunchRepository.findById(id).orElseThrow();
        model.addAttribute("lunch", lunch);
        return "lunch/edit";
    }
    
    // 更新処理
    @PostMapping("/lunches/update")
    public String update(@ModelAttribute Lunch lunch) {
        lunchRepository.save(lunch); // idが存在すればUPDATE
        return "redirect:/lunches";
    }
    
    // 削除処理
    @GetMapping("/lunches/delete/{id}")
    public String delete(@PathVariable Integer id) {
        lunchRepository.deleteById(id);
        return "redirect:/lunches";
    }
}
削除してみよう。データが削除されるはずだ。
完成!
これで Spring Boot + H2データベース + Thymeleaf を使った基本的なCRUDアプリが完成しました。
本チュートリアルを通して、Spring Bootの基本的な構成とWebアプリ開発の流れを理解できたと思います。
次のステップとしては、以下のような拡張もおすすめです。
- MySQLやPostgreSQLなどの外部DBに接続してみる
- バリデーション(入力チェック)を追加してみる
- Bootstrapで画面デザインを整える
今回の内容が、Spring Bootを使ったWebアプリ開発の第一歩になれば幸いです!
 
  
  
  
  
コメント