Spring フレームワークの自己学習メモです。Udemy、サーチマンさんの「ゼロから環境構築,文法,JSP,レイヤー,依存性,DB接続,Webアプリを一気に学ぶ」の講座をなぞっています。
開発環境
- Mac mini (Apple M1 2020) 16GB メモリー
- macOS Sequoia 15.0.1
Pleiades All in One Eclipse のインストール
リリース2024 Standard Edition を選択しました。
- pleiades-2024-09-java-mac_20240917.dmg
Java バージョンを切り替えたい
現在のバージョン
% javac -version
javac 23.0.1
% java -version
openjdk version "23.0.1" 2024-10-15
OpenJDK Runtime Environment (build 23.0.1+11-39)
OpenJDK 64-Bit Server VM (build 23.0.1+11-39, mixed mode, sharing)
%
$JAVA_HOME を確認しておきます。
% echo $JAVA_HOME
/Users/m/Dev/openjdk/jdk-23.0.1.jdk/Contents/Home
%
このバージョンでいいので、このまま進めます。
もしバージョンを切り替えるのは、こんな感じかと
インストール済みの Java を確認
% /usr/libexec/java_home -V
Matching Java Virtual Machines (1):
17.0.4.1 (arm64) "Eclipse Adoptium" - "OpenJDK 17.0.4.1" /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
%
切り替えと確認
% export JAVA_HOME="/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home"
% PATH=$JAVA_HOME/bin:$PATH
% source ~/.zshrc
%
% javac -version
javac 17.0.4.1
%
% java -version
openjdk version "17.0.4.1" 2022-08-12
OpenJDK Runtime Environment Temurin-17.0.4.1+1 (build 17.0.4.1+1)
OpenJDK 64-Bit Server VM Temurin-17.0.4.1+1 (build 17.0.4.1+1, mixed mode)
%
DB
MySQL
インストール
% brew install mysql
% which mysql
/opt/homebrew/bin/mysql
%
起動
% mysql.server start
Starting MySQL
.. SUCCESS!
%
初期設定
% mysql_secure_installation
停止
% mysql.server stop
Shutting down MySQL
. SUCCESS!
%
ログイン
% mysql -u root -p
Enter password:
(snip)
mysql>
アンインストール
% mysql.server stop
Shutting down MySQL
. SUCCESS!
% brew uninstall mysql
DB作成
- create database (DB名) default character set utf8mb4 collate utf8mb4_general_ci;
mysql> create database user default character set utf8mb4 collate utf8mb4_general_ci;
Query OK, 1 row affected (0.00 sec)
mysql>
DB一覧
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| user |
+--------------------+
5 rows in set (0.00 sec)
mysql>
DB切替
mysql> use user;
Database changed
mysql>
create table
mysql> create table user (
-> id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> name varchar(255),
-> email varchar(255));
Query OK, 0 rows affected (0.01 sec)
mysql>
mysql> alter table user add constraint user_u1 unique (email);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql>
テーブル一覧
mysql> show tables;
+----------------+
| Tables_in_user |
+----------------+
| user |
+----------------+
1 row in set (0.01 sec)
mysql>
データ投入
mysql> insert into user (name, email) values ('ishi32', 'ishi32@example.com');
Query OK, 1 row affected (0.01 sec)
mysql> select * from user;
+----+--------+--------------------+
| id | name | email |
+----+--------+--------------------+
| 1 | ishi32 | ishi32@example.com |
+----+--------+--------------------+
1 row in set (0.00 sec)
mysql>
Spring プロジェクト作成
- 「プロジェクト・エクスプローラ」で右クリック
- [新規] -> [その他] -> [Spring Boot] -> [Spring スターター・プロジェクト (Spring Initializer)] -> [次へ]
- 名前: app1
- タイプ: Maven
- グループ: com.bkiban
- 説明: User manage
- パッケージ: com.bkiban.app1
- [次へ] -> (デフォルトで)[次へ] -> (デフォルトで)[完了]
pom.xml
に以下を追加
<dependencies>
(snip)
<!-- Spring Boot Starter Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- JSP support -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- Spring Boot JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
src/main/resources/application.properties
以下を追加
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/user
spring.datasource.username=root
spring.datasource.password=********
spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp
jsp の格納場所を作成
-
src/main
で右クリック - [新規] -> [フォルダ]
- 「フォルダ名」に
webapp
- [完了]
-
src/main/webapp
で右クリック - [新規] -> [フォルダ]
- 「フォルダ名」に
WEB-INF
- [完了]
各レイアを作成
- controller
-
src/main/java/com/bkiban/app1
で右クリック - [新規] -> [フォルダ]
- 「フォルダ名」に
- [完了]
-
src/main/webapp
で右クリック - [新規] -> [フォルダ]
- 「フォルダ名」に
controller
- [完了]
-
- entity
- repository
- service
Spring の各レイヤー (パッケージ) の役割
- Controller (HTTPの処理)
ブラウザからの HTTP リクエストを受け取り、必要の応じて Service を呼び出し、その処理結果がある場合は View (JSP や HTML) へ返却する。 - Service (ビジネスロジック)
- Repository (DBアクセス)
- Entity (DBのテーブルに対応)
テーブルごとに、各項目の定義と Getter、Setter をオブジェクトとして定義する。
entity
-
src/main/java/com/bkiban/app1/entity
で右クリック - [新規] -> [その他] -> [Java] -> [クラス]
- 名前: User
- [完了]
package com.bkiban.app1.entity;
public class User {
private int id;
private String name;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
repository (インターフェース)
インターフェースには、設計で必要とされるメソッドを全て「抽象メソッド」として定義しておく。こうすることで実装をあらかじめ担保しておく。
-
src/main/java/com/bkiban/app1/repository
で右クリック - [新規] -> [その他] -> [Java] -> [インターフェース]
- [次へ]
- 「名前」: UserRepository
- [完了]
package com.bkiban.app1.repository;
import java.util.List;
import com.bkiban.app1.entity.User;
public interface UserRepository {
//ユーザリストを取得
List<User> findAll();
//idによるユーザ取得
User findById(int id);
//ユーザ挿入
void insertUser(User user);
//ユーザ更新
void updateUser(User user);
//ユーザ削除
void deleteUser(int id);
}
ArrayList<User>
の User
がエラーになっていたら、import が足りていないので User
にマウスをあてて、「User をインポートします (com.bkiban.app1.entity)」をクリックする
service (インターフェース)
-
src/main/java/com/bkiban/app1/service
で右クリック - [新規] -> [その他] -> [Java] -> [インターフェース]
- [次へ]
- 「名前」: UserService
- [完了]
- コードは UserRepository.java とほぼ同じ
package com.bkiban.app1.service;
import java.util.List;
import com.bkiban.app1.entity.User;
public interface UserService {
//全ユーザを取得
List<User> findAll();
//リクエストからユーザオブジェクトを作成
User makeUser(User request);
//ユーザ挿入
void insertUser(User user);
//idによるユーザ取得
User findById(int id);
//ユーザ更新
void updateUser(User user);
//ユーザ削除
void deleteUser(int id);
}
repository (実装)
-
src/main/java/com/bkiban/app1/repository
で右クリック - [新規] -> [その他] -> [Java] -> [クラス]
- [次へ]
- 「名前」: UserRepositoryImpl
- 「インターフェース:」で [追加] をクリック
- 「Search」に User といれると表示される「UserRepository」をクリック
- [OK]
- [完了]
- 以下が自動的に生成される
package com.bkiban.app1.repository;
import java.util.List;
import com.bkiban.app1.entity.User;
public class UserRepositoryImpl implements UserRepository {
@Override
public List<User> findAll() {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override
public User findById(int id) {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override
public void insertUser(User user) {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public void updateUser(User user) {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public void deleteUser(int id) {
// TODO 自動生成されたメソッド・スタブ
}
}
- @Repository を追記
-
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate; を追記 - 各メソッドを以下のように更新する
package com.bkiban.app1.repository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import com.bkiban.app1.entity.User;
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public List<User> findAll() {
//SQL文の作成
final String sql = "select id, name, email from user";
//SQLの実行
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
return userList;
}
@Override
public User findById(int id) {
//SQL文の作成
final String sql
= "select id, name, email from user where id = :id";
// パラメータの作成
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("id", id);
// SQLの実行
List<User> userList =
jdbcTemplate.query(sql, param, new BeanPropertyRowMapper<User>(User.class));
//リストを判定して戻す
return userList.isEmpty() ? null : userList.get(0);
}
@Override
public void insertUser(User user) {
//SQL文の作成
final String sql = "insert into user(name, email) "
+ "values(:name,:email)";
// パラメータの作成
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("name", user.getName());
param.addValue("email", user.getEmail());
// SQLの実行
jdbcTemplate.update(sql, param);
}
@Override
public void updateUser(User user) {
//SQL文の作成
final String sql
= "update user set name=:name, email=:email "
+ "where id=:id";
// パラメータの作成
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("id", user.getId());
param.addValue("name", user.getName());
param.addValue("email", user.getEmail());
// SQLの実行
jdbcTemplate.update(sql, param);
}
@Override
public void deleteUser(int id) {
//SQL文の作成
final String sql = "delete from user where id=:id";
// パラメータの作成
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("id", id);
// SQLの実行
jdbcTemplate.update(sql, param);
}
}
service (実装)
-
src/main/java/com/bkiban/app1/service
で右クリック - [新規] -> [その他] -> [Java] -> [クラス]
- [次へ]
- 「名前」: UserServiceImpl
- 「インターフェース:」で [追加] をクリック
- 「Search」に User といれると表示される「UserService」をクリック
- [OK]
- [完了]
- 以下が自動的に生成される
package com.bkiban.app1.service;
import java.util.List;
import com.bkiban.app1.entity.User;
public class UserServiceImpl implements UserService {
@Override
public List<User> findAll() {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override
public User makeUser(User request) {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override
public void insertUser(User user) {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public User findById(int id) {
// TODO 自動生成されたメソッド・スタブ
return null;
}
@Override
public void updateUser(User user) {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public void deleteUser(int id) {
// TODO 自動生成されたメソッド・スタブ
}
}
- @Service を追記
-
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate; を追記 - 各メソッドを以下のように更新する
package com.bkiban.app1.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bkiban.app1.entity.User;
import com.bkiban.app1.repository.UserRepository;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserRepository userRepository;
@Override
public List<User> findAll() {
return userRepository.findAll();
}
@Override
public User makeUser(User request) {
//社員オブジェクトの作成
User user = new User();
//社員オブジェクトに値を代入
user.setId(request.getId());
user.setName(request.getName());
user.setEmail(request.getEmail());
//社員オブジェクトを戻す
return user;
}
@Override
public void insertUser(User user) {
userRepository.insertUser(user);
}
@Override
public User findById(int id) {
return userRepository.findById(id);
}
@Override
public void updateUser(User user) {
userRepository.updateUser(user);
}
@Override
public void deleteUser(int id) {
userRepository.deleteUser(id);
}
}
controller
-
src/main/java/com/bkiban/app1/controller
で右クリック - [新規] -> [その他] -> [Java] -> [クラス]
- [次へ]
- 「名前」: UserController
- [完了]
- 以下が自動的に生成される
package com.bkiban.app1.controller;
public class UserController {
}
- @Controller を追記
-
@Autowired
private ShainService shainService; を追記 - 各メソッドを以下のように追加する
package com.bkiban.app1.controller;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.bkiban.app1.entity.User;
import com.bkiban.app1.service.UserService;
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/index")
public String index(Model model) {
// リスト取得
List<User> userList = userService.findAll();
// JSPに渡す
model.addAttribute("userList", userList);
// JSPに転送
return "index";
}
@GetMapping("/insert")
public String insert() {
// JSPに転送
return "insert";
}
@PostMapping("/insert")
public String insert(@ModelAttribute User request) {
//public void insert(@ModelAttribute User request) {
//System.out.println(shain.getId() + ":" + shain.getName()
//+ ":" + shain.getSei() + ":" + shain.getNen()
//+ ":" + shain.getAddress());
System.out.println(request + ":" + request.getName() + ":" + request.getEmail());
System.out.println(ToStringBuilder.reflectionToString(request, ToStringStyle.DEFAULT_STYLE));
//パラメータ値から社員作成
User user = userService.makeUser(request);
//DBに挿入
userService.insertUser(user);
// リダイレクト
return "redirect:/index";
}
@GetMapping("/update")
public String update(
@RequestParam("id") int id,
Model model) {
//対象データを取得
User user = userService.findById(id);
// JSPに渡す
model.addAttribute("user", user);
// JSPに転送
return "update";
}
@PostMapping("/update")
public String update(@ModelAttribute User request) {
//パラメータ値から社員作成
User user = userService.makeUser(request);
//DB更新
userService.updateUser(user);
// リダイレクト
return "redirect:/index";
}
@GetMapping("/delete")
public String delete(
@RequestParam("id") int id,
Model model) {
//対象データを取得
User user = userService.findById(id);
// JSPに渡す
model.addAttribute("user", user);
// JSPに転送
return "delete";
}
@PostMapping("/delete")
public String delete(
@RequestParam("id") int id) {
//DBから削除
userService.deleteUser(id);
// リダイレクト
return "redirect:/index";
}
}
public String index(Model model) の Model がエラーになっている。マウスをあてて、「'Model' をインポートします (org.springframework.ui)」をクリックする
JSP
-
src/main/webapp/WEB-INF
で右クリック - [新規] -> [その他] -> [Web] -> [JSP ファイル]
- [次へ]
- 「名前」: index
- [完了]
- 以下の内容に更新する
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="java.util.ArrayList"%>
<%@page import="com.bkiban.app1.entity.User"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザ一覧</title>
<style>
table {
border-collapse: collapse; /* セルの境界線を重ねて単線にする */
}
th, td {
border: 1px solid black; /* セルの境界線のスタイルを設定 */
padding: 10px;
}
</style>
</head>
<body>
<h1>ユーザ一覧</h1>
<table border="1">
<tr bgcolor="#cccccc">
<th>ID</th>
<th>名前</th>
<th>メールアドレス</th>
<th>変更</th>
<th>削除</th>
</tr>
<%
ArrayList<User> userList = (ArrayList<User>) request.getAttribute("userList");
%>
<%
for (User user : userList) {
%>
<tr>
<td><%=user.getId()%></td>
<td><%=user.getName()%></td>
<td><%=user.getEmail()%></td>
<td><a href="update?id=<%=user.getId()%>">変更</a></td>
<td><a href="delete?id=<%=user.getId()%>">削除</a></td>
</tr>
<%
}
%>
</table>
<p></p>
<!-- 「ユーザを登録する」ボタン -->
<form action="insert" method="get">
<input type="submit" value="ユーザを登録する">
</form>
</body>
</html>
- insert.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>社員登録画面</title>
<style>
.form-input {
width: 100%;
}
.form-table td {
padding: 5px;
}
.form-table label {
text-align: right;
}
.form-button {
margin-top: 10px;
}
</style>
</head>
<body>
<h1>社員登録画面</h1>
<form action="insert" method="post">
<table class="form-table">
<tr>
<td><label for="name">名前:</label></td>
<td><input type="text" id="name" name="name" class="form-input"
required></td>
</tr>
<tr>
<td><label for="email">メールアドレス:</label></td>
<td><input type="text" id="email" name="email"
class="form-input" required></td>
</tr>
</table>
<button type="submit" class="form-button">登録</button>
</form>
</body>
</html>
- update.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="com.bkiban.app1.entity.User"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザ更新画面</title>
<style>
.form-input {
width: 100%;
}
.form-table td {
padding: 5px;
}
.form-table label {
text-align: right;
}
.form-button {
margin-top: 10px;
}
</style>
</head>
<body>
<%-- shainから社員情報を取得する --%>
<%
User user = (User) request.getAttribute("user");
%>
<h1>ユーザ更新画面</h1>
<form action="update" method="post">
<table class="form-table">
<tr>
<td><label for="id">ID:</label></td>
<td><%=user.getId()%></td>
</tr>
<tr>
<td><label for="name">名前:</label></td>
<td><%=user.getName()%></td>
</tr>
<tr>
<td><label for="email">メールアドレス:</label></td>
<td><input type="text" id="email" name="email"
value="<%=user.getEmail()%>" class="form-input" required></td>
</tr>
</table>
<button type="submit" class="form-button">更新</button>
<input type="hidden" name="id" value="<%=user.getId()%>">
<input type="hidden" name="name" value="<%=user.getName()%>">
</form>
</body>
</html>
- delete.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="com.bkiban.app1.entity.User"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ユーザ削除画面</title>
<style>
.form-input {
width: 100%;
}
.form-table td {
padding: 5px;
}
.form-table label {
text-align: right;
}
.form-button {
margin-top: 10px;
}
</style>
</head>
<body>
<%-- shainから社員情報を取得する --%>
<%
User user = (User) request.getAttribute("user");
%>
<h1>ユーザ削除画面</h1>
<form action="delete" method="post">
<table class="form-table">
<tr>
<td><label for="id">ID:</label></td>
<td><%=user.getId()%></td>
</tr>
<tr>
<td><label for="name">名前:</label></td>
<td><%=user.getName()%></td>
</tr>
<tr>
<td><label for="email">メールアドレス:</label></td>
<td><%=user.getEmail()%></td>
</tr>
</table>
<button type="submit" class="form-button">削除</button>
<input type="hidden" name="id" value="<%=user.getId()%>">
</form>
</body>
</html>
動作確認
- Bootダッシュボードで app1 を起動
- ブラウザから localhost:8080/index にアクセス
- [ユーザを登録する] をクリック
- [登録] をクリック
- ID: 1 の [削除] をクリック
- [削除] をクリック