ホーム画面からユーザー一覧画面に遷移し、ユーザーの詳細を表示するアプリケーションを作成して
Spring JDBCの使い方について学びます⭐️
今回はまずJDBC Templateを実装するための画面作成を行います
構成はこれまでの記事を参考にしてください
【Java・SpringBoot・Thymeleaf】ブラウザ言語設定でエラーメッセージの言語を変更(SpringBootアプリケーション実践編8)
【Java・SpringBoot】Spring AOP実践
#Spring JDBCとは
- JDBCはJavaでDBにアクセスするためのライブラリ
- SpringJDBCは、JDBCを使ってデータベースにアクセス
- 通常のJDBCでは、DBへの接続やクローズ処理などを毎回書かないといけない。。。
- →🌟SpringJDBCを使えば、DBの接続やクローズなどの処理を書かなくてOK^^
-
🌟DB製品固有のエラーコードを解釈して、適切な例外を投げてくれる
- 一意制約違反(他の行の値と重複禁止)が発生した場合、SQLServerでは
2627
、OracleではORA0001
- 一意制約違反が発生した場合は、
DuplicateKeyException
という例外クラスでキャッチ - →製品ごとに実装を分ける必要がない!
- 一意制約違反(他の行の値と重複禁止)が発生した場合、SQLServerでは
-
DataAccessException
クラス:すべての例外クラスのスーパークラス- データベース関連のエラーをすべてキャッチできる
#SpringJDBC実践!
- 以下の画面を作成しながら、SpringJDBCを使います
- それぞれの画面は、テンプレート部分とコンテンツ部分に分かれている
- テンプレート部分:共通部分のhtml(ヘッダー、サイドバーなど)
- レイアウトを変更する場合、テンプレート用のファイルを修正するだけでOK!
###ホーム画面
- ログインボタンを押した後に表示される
* ログイン画面については過去の記事のコードを参考にしてください- ログアウトやユーザー一覧画面に飛ぶことができる<>
【Java・SpringBoot・Thymeleaf】ログイン・新規登録画面作成(SpringBootアプリケーション実践編1)
- ログアウトやユーザー一覧画面に飛ぶことができる<>
###ユーザー一覧画面
- DBから全ユーザーの情報を取得して、画面に表示
-
詳細ボタン
を押すと、各ユーザーの詳細画面に移る - ユーザー一覧をCSV出力できる
-
###ユーザー詳細画面
- ユーザーの詳細を表示
- ユーザーの更新、削除をする
- 更新・削除した後はユーザー一覧画面に移る
##画面作成
- 先に画面を作成して、タイムリーフでテンプレート画面を作る方法について学びます
- 構成は以下のようになってます
Project Root
└─src
└─ main
└─ java
└─ com.example.demo
└─ login
└─ aspect ...AOP用パッケージ
└─ controller ...コントローラクラス用パッケージ
└─ HomeController.java
└─ domain ...ビジネスロジック用パッケージ
└─ model ...Model(DTO)用パッケージ
└─ User.java
└─ repository ...リポジトリクラス用パッケージ
└─ UserDao.java
└─ jdbc
└─ UserDaoJdbcImpl.java
└─ service ...サービスクラス用パッケージ
└─ UserService.java
└─ resouces
└─ static ...css,js用フォルダ
└─ cs
└─ home.css
└─ templates
└─ login
└─ home.html
└─ homeLayout.html
##ホーム画面のテンプレート用htmlを作成
###th:include属性
- タグ内に別ファイルのコンテンツ部分のhtmlが追加される
-
th:includeの値th:include="<ファイルパス>::<th:fragment属性の値>"
- ファイルパス:コンテンツ部分のhtmlファイルのファイルパスです。
-
th:fragment属性:コンテンツ部分のhtmlで使う属性
- ex: loginフォルダー内にあるhome.htmlというコンテンツ用のhtml内に、**
th:fragment="home_contents"
と書き、
th:includeには、th:include="login/home::home_contents"
**と記述
- ex: loginフォルダー内にあるhome.htmlというコンテンツ用のhtml内に、**
- コンテンツ部分を動的に変更する場合は、
th:include
の値はModelに登録された値を参照するようにする- Modelに
login/home::home_contents
という文字列を登録 - ※動的にコンテンツ部分のhtmlを変える場合、プリプロセッシングを使う必要がある
- Modelに
###プリプロセッシング
- 通常の式よりも先に評価させる仕組み
- プリプロセッシングでは変数(${変数名})に__(アンダースコア2つ)を前後に付ける
-
th:include="__${contents}__"
の部分がth:include="login/home::home_contents"
と先に評価され、その後でhtmlが作成される
-
- →コンテンツ部分のhtmlを表示させることができる!
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8"></meta>
<!-- Bootstrap -->
<link th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}" rel="stylesheet"></link>
<script th:src="@{/webjars/jquery/1.11.1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
<!-- CSS読込 -->
<link th:href="@{/css/home.css}" rel="stylesheet"></link>
<title>Home</title>
</head>
<body>
<!-- ヘッダー -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">SpringBoot</a>
</div>
<form method="post" th:action="@{/logout}">
<button class="btn btn-link pull-right navbar-brand" type="submit">
ログアウト
</button>
</form>
</div>
</nav>
<!-- サイドバー -->
<div class="container-fluid">
<div class="row">
<div class="col-sm-2 sidebar">
<ul class="nav nav-pills nav-stacked">
<li role="presentation">
<a th:href="@{'/userList'}">ユーザ管理</a>
</li>
</ul>
</div>
</div>
</div>
<!-- コンテンツ -->
<div class="container-fluid">
<div class="row">
<div class="col-sm-10 col-sm-offset-2 main">
<div th:include="__${contents}__"></div>
</div>
</div>
</div>
</body>
</html>
##ホーム画面のhtmlを作成
###th:fragment
- th:fragment属性が付いているタグ内のhtmlが、テンプレート用のhtml内に追加される
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8"></meta>
</head>
<body>
<!-- ポイント:th:fragment -->
<div th:fragment="home_contents">
<div class="page-header">
<h1>ホーム</h1>
</div>
</div>
</body>
</html>
##ホーム画面用のコントローラークラスを作成
###ホーム画面にGETリクエスト
- /homeにGETリクエストが来たときに、Modelクラスの"contents"というキーに
"login/home::home_contents"
という値をセット - この値がth:include属性に入る
th:include="login/home::home_contents"
- ログアウトボタンが押されたら、ログイン画面にリダイレクトする
package com.example.demo.login.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.example.demo.login.domain.service.UserService;
@Controller
public class HomeController {
@Autowired
UserService userService;
@GetMapping("/home")
public String getHome(Model model) {
//コンテンツ部分にユーザー詳細を表示するための文字列を登録
model.addAttribute("contents", "login/home :: home_contents");
return "login/homeLayout";
}
@PostMapping("/logout")
public String postLogout() {
return "redirect:/login";
}
}
##ホーム画面のcssを作成
body {
padding-top: 50px;
}
.sidebar {
position: fixed;
display: block;
top: 50px;
bottom: 0;
background-color: #F4F5F7;
}
.main {
padding-top: 50px;
padding-left: 20px;
position: fixed;
display: block;
top: 0px;
bottom: 0;
}
.page-header {
margin-top: 0px;
}
##ユーザーテーブルのカラムをフィールドに保持
- データベースから取得した値を、コントローラークラスやサービスクラスなどの間でやり取りするためのクラスを用意
- @Dataアノテーション:Lombokでgetterやsetterを自動で作る
package com.example.demo.login.domain.model;
import java.util.Date;
import lombok.Data;
@Data
public class User {
private String userId; //ユーザーID
private String password; //パスワード
private String userName; //ユーザー名
private Date birthday; //誕生日
private int age; //年齢
private boolean marriage; //結婚ステータス
private String role; //ロール
}
##リポジトリークラスのインターフェース
- インターフェースを作る理由は、後で中身の実装クラスを簡単に切替えられるようにするため
-
DataAccessException
- Springでは、データベース操作で例外が発生した場合、Springが提供しているDataAccessExceptionを投げる
- SpringJDBCだけでなく、Spring+MyBatisを使った時にも投げられる
package com.example.demo.login.domain.repository;
import java.util.List;
import org.springframework.dao.DataAccessException;
import com.example.demo.login.domain.model.User;
public interface UserDao {
// Userテーブルの件数を取得.
public int count() throws DataAccessException;
// Userテーブルにデータを1件insert.
public int insertOne(User user) throws DataAccessException;
// Userテーブルのデータを1件取得
public User selectOne(String userId) throws DataAccessException;
// Userテーブルの全データを取得.
public List<User> selectMany() throws DataAccessException;
// Userテーブルを1件更新.
public int updateOne(User user) throws DataAccessException;
// Userテーブルを1件削除.
public int deleteOne(String userId) throws DataAccessException;
//SQL取得結果をサーバーにCSVで保存する
public void userCsvOut() throws DataAccessException;
}
##インターフェース実装クラスを作成
- 各メソッドの中身は、まだ空の状態
- JdbcTemplateはSpringが用意しているため、既にBean定義がされている
- →@AutowiredするだけでOK
- このクラスのメソッドでSQLを実行
package com.example.demo.login.domain.repository.jdbc;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.example.demo.login.domain.model.User;
import com.example.demo.login.domain.repository.UserDao;
@Repository("UserDaoJdbcImpl")
public class UserDaoJdbcImpl implements UserDao {
@Autowired
JdbcTemplate jdbc;
// Userテーブルの件数を取得.
@Override
public int count() throws DataAccessException {
return 0;
}
// Userテーブルにデータを1件insert.
@Override
public int insertOne(User user) throws DataAccessException {
return 0;
}
// Userテーブルのデータを1件取得
@Override
public User selectOne(String userId) throws DataAccessException {
return null;
}
// Userテーブルの全データを取得.
@Override
public List<User> selectMany() throws DataAccessException {
return null;
}
// Userテーブルを1件更新.
@Override
public int updateOne(User user) throws DataAccessException {
return 0;
}
// Userテーブルを1件削除.
@Override
public int deleteOne(String userId) throws DataAccessException {
return 0;
}
//SQL取得結果をサーバーにCSVで保存する
@Override
public void userCsvOut() throws DataAccessException {
}
}
##サービス用のクラスを作成
- クラスを用意するだけ
package com.example.demo.login.domain.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.login.domain.repository.UserDao;
@Service
public class UserService {
@Autowired
UserDao dao;
}
##ログ出力用のアスペクトクラス
- ユーザーDaoクラスのメソッドが呼び出されたときに、どのクラスのどのメソッドが呼ばれたのかをログ出力できるようにする
package com.example.demo.login.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspct {
@Around("execution(* *..*.*Controller.*(..))")
public Object startLog(ProceedingJoinPoint jp) throws Throwable {
System.out.println("メソッド開始: " + jp.getSignature());
try {
//メソッド実行
Object result = jp.proceed();
System.out.println("メソッド終了: " + jp.getSignature());
return result;
} catch (Exception e) {
System.out.println("メソッド異常終了: " + jp.getSignature());
e.printStackTrace();
throw e;
}
}
/**
* Daoクラスのログ出力用アスペクトを追加
*/
@Around("execution(* *..*.*UserDao*.*(..))")
public Object daoLog(ProceedingJoinPoint jp) throws Throwable {
System.out.println("メソッド開始: " + jp.getSignature());
try {
Object result = jp.proceed();
System.out.println("メソッド終了: " + jp.getSignature());
return result;
} catch (Exception e) {
System.out.println("メソッド異常終了: " + jp.getSignature());
e.printStackTrace();
throw e;
}
}
}
##テーブルを作成します
###ユーザーテーブル作成
/* 従業員テーブル */
CREATE TABLE IF NOT EXISTS employee (
employee_id INT PRIMARY KEY,
employee_name VARCHAR(50),
age INT
);
/* ユーザーマスタ */
CREATE TABLE IF NOT EXISTS m_user (
user_id VARCHAR(50) PRIMARY KEY,
password VARCHAR(100),
user_name VARCHAR(50),
birthday DATE,
age INT,
marriage BOOLEAN,
role VARCHAR(50)
);
###ユーザーテーブルの初期化データを作成
/* 従業員テーブルのデータ */
INSERT INTO employee (employee_id, employee_name, age)
VALUES(1, 'Teshita Neko', 3);
/* ユーザーマスタのデータ(ADMIN) */
INSERT INTO m_user (user_id, password, user_name, birthday, age, marriage, role)
VALUES('nekomofu@xxx.co.jp', 'password', 'Oyakata Neko', '2020-01-01', 3, false, 'ROLE_ADMIN');
##ログイン画面からホーム画面に遷移
- ログインボタンを押すとホーム画面に遷移
return "redirect:/home";
package com.example.demo.login.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class LoginController {
@GetMapping("/login")
public String getLogin(Model model) {
//login.htmlに画面遷移
return "login/login";
}
@PostMapping("/login")
public String postLogin(Model model) {
//ホーム画面に遷移
return "redirect:/home";
}
}
#SpringBootを起動して画面を確認!
- http://localhost:8080/home
- ホーム画面を作成できました〜〜^^
- 次回はJDBC Templateを実装します🌟