Java のDB処理で Service
, Dao
(Data Accesss Object) を使ったときのはなし
Service
と DAO
と Entity
の関係をざっくりまとめる
細かいことは気にしない!!いったんは流れが分かればOK!!!!
説明で使ってるサンプルプロジェクトのおおもとは GitHub java-webapp-clump にあります
ここでは DB接続 の方法として以下の2つでやってみる
- JDBC (Java Database Connectivity)
昔から使われてるDB接続方法(DB処理を実行するための 標準仕様) - JPA (Java Persistence API) :
開発でよく使われるDB接続方法(DB処理を実行するための ORM (Object-Relational Mapping) の仕様)
# DB接続のORM の FW (フレームワーク) はいろいろある...ここではJPA標準の EclipseLink を使う
Service, Dao, Entity の役割
- Service
処理をおこなうやつ
入力(引数)もらって処理して結果(戻り値)かえす - Dao (Data Accesss Object)
DB処理をおこなうやつ
DB接続してCRUD (Create, Read, Update, Delete) を主に行う - Entity
DBの情報をもつやつ
DBのテーブルの1レコードの情報を持つクラス
両方に必要な準備(DB作る、JDBCドライバ参照追加、JSP・Servlet作る)
ここでは mysql がインストール・実行されてる前提で進める(MySQL バージョン 8.0.28
)
mysql がない人は...他のサイトを参考にインストール・実行お願いします!
準備でやることは以下です
- DB作る
SQL 流してDB処理で使うDB, ユーザ, テーブルを作成 - JDBCドライバ設定
maven (pom.xml) に mysql のJDBCドライバを追加 - JSP・Servlet 作る
処理で使う JSP・Servlet を作っておく
DB作る
SQL を実行してサンプルで使う以下を作る
- DB :
WEB_APP_DB
- ユーザ :
webapp
(パスワードも同じ) - テーブル :
DB_WORK
(カラムはID
,NAME
,VERSION
)
# DB作成
CREATE DATABASE WEB_APP_DB;
# ユーザ作成
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'webapp';
# ユーザ権限追加
GRANT ALL PRIVILEGES ON WEB_APP_DB.* TO 'webapp'@'localhost';
# 使用DB指定
USE WEB_APP_DB;
# テーブル作成
CREATE TABLE DB_WORK (
ID INT AUTO_INCREMENT,
NAME VARCHAR(20) NOT NULL,
VERSION INT NOT NULL DEFAULT 0,
PRIMARY KEY(ID)
);
# レコード登録
INSERT INTO DB_WORK(ID, NAME, VERSION) VALUES(1, 'test1', 0);
INSERT INTO DB_WORK(NAME) VALUES('test2');
JDBCドライバ参照追加 (MySQL)
-
/java-webapp-clump/pom.xml
を修正 -
<dependencies>
・・・</dependencies>
の間に MySQL のdependency
を追加
<dependencies>
・
・
<!-- mysql JDBC driver -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.31</version>
</dependency>
・
・
</dependencies>
JSP作る
-
/java-webapp-clump/src/main/webapp/WEB-INF/view
直下にdbWork.jsp
作成
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ page isELIgnored="false" %>
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>JavaDevelop</title>
<link rel="shortcut icon" href="<c:url value="/assets/favicon.ico" />">
<link rel="stylesheet" href="<c:url value="/assets/bootstrap.min.css" />">
<link rel="stylesheet" href="<c:url value="/assets/bootstrap-icons.css" />">
<link rel="stylesheet" href="<c:url value="/assets/styles.css" />">
</head>
<body>
<%@ include file="./_headerNavbar.jsp" %>
<main>
<div class="container workspace">
<h2>DB Work</h2>
<hr/>
<table class="table table-striped table-hover caption-top">
<caption>DB Table DB_WORK</caption>
<thead>
<tr>
<th></th>
<th>ID</th>
<th>NAME</th>
<th>VERSION</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${dbWorks}" var="dbWork">
<tr>
<form action="./db_work_update" method="post">
<td>
<i class="bi bi-person-fill"></i>
</td>
<td><c:out value="${dbWork.id}"/></td>
<td><input type="text" name="name" size="20" value="${dbWork.name}" required placeholder="Update Name" /></td>
<td><c:out value="${dbWork.version}"/></td>
<input type="hidden" name="id" value="${dbWork.id}">
<input type="hidden" name="version" value="${dbWork.version}">
<td><button><i class="bi bi-pencil-fill"></i></button></td>
</form>
<form action="./db_work_delete" method="post">
<input type="hidden" name="id" value="${dbWork.id}">
<input type="hidden" name="version" value="${dbWork.version}">
<td><button><i class="bi bi-trash-fill"></i></button></td>
</form>
</tr>
</c:forEach>
<tr>
<form action="./db_work_insert" method="post">
<td>
<i class="bi bi-person"></i>
</td>
<td></td>
<td><input type="text" name="name" size="20" required placeholder="Create Name" /></td>
<td></td>
<td><button><i class="bi bi-person-plus-fill"></i></button></td>
<td></td>
</form>
</tr>
</tbody>
</table>
<hr/>
</div>
</main>
<footer class="footer mt-auto fixed-bottom py-3 bg-secondary"></footer>
<script src="<c:url value="/assets/bootstrap.bundle.min.js" />"></script>
<script src="<c:url value="/assets/script.js" />"></script>
</body>
</html>
Servlet 作る
-
/java-webapp-clump/src/main/java/presentation/servlet
直下にクラスDBWorkServlet.java
を作成 -
@WebServlet
に設定する URL を/db_work
にする - JSP のパスを
/WEB-INF/view/dbWork.jsp
にする
package presentation.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* {@link DBWorkServlet}
*/
@WebServlet("/db_work")
public class DBWorkServlet extends HttpServlet {
/**
* {@inheritDoc}
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String view = "/WEB-INF/view/dbWork.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(view);
dispatcher.forward(request, response);
}
}
JSP・Servlet の動作確認
- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示を確認(以下みたいのが表示されればOK)
JDBC (Java Database Connectivity) を使った DB処理
全件取得処理
- テーブルのレコード情報を持つ
Entity
を作る - DBから全件取得する処理を持つ
Dao
を作る - 上で作った
Dao
を使うService
を作る - 上で作った
Service
を使うServlet
を作る
テーブルのレコード情報を持つ Entity
を作る
-
/java-webapp-clump/src/main/java/business
直下にパッケージentity
を作成 - 上のパッケージ直下にクラス
DBWork.java
を作成 - テーブル
DB_WORK
に合わせてインスタンス変数を持ちコンストラクタ、getter, setter を作成
package business.entity;
public class DBWork {
/** ID */
private Integer id;
/** 名前 */
private String name;
/** バージョン */
private Integer version;
/**
* コンストラクタ
*/
public DBWork() {
}
/**
* コンストラクタ
* @param id ID
* @param name 名前
* @param version バージョン
*/
public DBWork(Integer id, String name, Integer version) {
this.id = id;
this.name = name;
this.version = version;
}
/**
* @return id
*/
public Integer getId() {
return id;
}
/**
* @param id セットする id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* @return name
*/
public String getName() {
return name;
}
/**
* @param name セットする name
*/
public void setName(String name) {
this.name = name;
}
/**
* @return version
*/
public Integer getVersion() {
return version;
}
/**
* @param version セットする version
*/
public void setVersion(Integer version) {
this.version = version;
}
}
DBから全件取得する処理を持つ Dao
を作る
- パッケージ
/java-webapp-clump/src/main/java/business/dao
を作成 - 上のパッケージ直下にクラス
DBWorkDaoJDBC.java
を作成 - 全件取得のメソッドを作成
- JDBCクラスをロード(使えるように読み込む)
Class.forName("ドライバのパス");
- DB接続のオブジェクト
Connection
を生成Connection connection = DriverManager.getConnection("DB接続URL", "ユーザ", "パスワード");
- DB実行のオブジェクト
PreparedStatement
の取得PreparedStatement pstmt = connection.prepareStatement("SQL文")
- SQL を実行してSQL実行結果のオブジェクト
ResultSet
を取得ResultSet rs = pstmt.executeQuery();
-
ResultSet
からレコード取得-
while(rs.next())
でレコード件数分ループ処理 -
rs.getInt("カラム名")
でカラムの値を取得 - レコードの情報を設定した
Entity
を生成して戻り値に設定
-
- JDBCクラスをロード(使えるように読み込む)
-
Connection
,PreparedStatement
,ResultSet
は必ずclose
する
※ サンプルではConnection
をtry-with-resources
( try の後の()
で括ること) ですべての close 処理が実行される
※ close 処理を実行しないと Java からのDB接続が残ってしまいDBが使えなくなってしまうことがあるので注意
※try-with-resources
を使わないときはfinally
でエラー発生した時も必ず close 処理を実行する必要がある
※ Java、JDBC とかで検索してヒットするページを見て close 処理をする箇所を見ておくと良いかも...
package business.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import business.entity.DBWork;
/**
* {@link DBWorkDaoJDBC}
*/
public class DBWorkDaoJDBC {
/**
* DBWork を全件取得します
*/
public List<DBWork> findAll() {
String dbUrl = "jdbc:mysql://localhost:3306/WEB_APP_DB";
String dbUser = "webapp";
String dbPassword = "webapp";
try {
// JDBCクラスをロード
Class.forName("com.mysql.cj.jdbc.Driver");
// DBコネクション生成
try (Connection connection = DriverManager.getConnection(dbUrl, dbUser, dbPassword);) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection.prepareStatement("SELECT ID, NAME, VERSION FROM DB_WORK;");
// SQL実行
ResultSet rs = pstmt.executeQuery();
// SQL実行結果からデータを取得
List<DBWork> list = new ArrayList<>();
while (rs.next()) {
int id = rs.getInt("ID");
String name = rs.getString("NAME");
int version = rs.getInt("VERSION");
list.add(new DBWork(id, name, version));
}
return list;
}
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
上で作った Dao
を使う Service
を作る
-
/java-webapp-clump/src/main/java/business/service
直下にクラスDBWorkService.java
を作成 - インスタンス変数に
DBWorkDaoJDBC
を持つ - 全件取得のメソッドを作る(
dao
の全件取得処理を実行)
package business.service;
import java.util.List;
import business.dao.DBWorkDaoJDBC;
import business.entity.DBWork;
/**
* {@link DBWorkService}
*/
public class DBWorkService {
DBWorkDaoJDBC dao = new DBWorkDaoJDBC();
/**
* DBWork を全件取得します
*
* @return DBWorkリスト
*/
public List<DBWork> findAll() {
return dao.findAll();
}
}
上で作った Service
を使う Servlet
を作る
- 準備で作った
/java-webapp-clump/src/main/java/presentation/servlet/DBWorkServlet.java
を修正 - インスタンス変数に
DBWorkService
を持つようにする -
doGet
の中でService
の全件取得処理を呼び出し結果をrequest
に設定
package presentation.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import business.service.DBWorkService;
/**
* {@link DBWorkServlet}
*/
@WebServlet("/db_work")
public class DBWorkServlet extends HttpServlet {
// 以下の行を追加
DBWorkService service = new DBWorkService();
/**
* {@inheritDoc}
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 以下の行を追加
request.setAttribute("dbWorks", service.findAll());
String view = "/WEB-INF/view/dbWork.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(view);
dispatcher.forward(request, response);
}
}
全件取得の動作確認
- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示を確認(以下みたいのが表示されればOK)
登録処理
- テーブルのレコード情報を持つ
Entity
を作る ※ 全件取得で作成済 - DBに登録する処理を持つ
Dao
を作る ※ 全件取得で作ったやつにメソッド追加 - 上で作った
Dao
を使うService
を作る ※ 全件取得で作ったやつにメソッド追加 - 上で作った
Service
を使うServlet
を作る ※ 全件取得で作ったやつを継承して作る
DBに登録する処理を持つ Dao
を作る ※ 全件取得で作ったやつにメソッド追加
-
/java-webapp-clump/src/main/java/business/dao/DBWorkDaoJDBC.java
に追加, 修正 - 登録のメソッドを作成
- DB実行のオブジェクト
PreparedStatement
の取得までは全件取得と同じ - SQLにパラメータを設定
- 実行する SQL のパラメータにしたいところを
?
にする -
?
にしたところに値を設定pstmt.setInt(?の入れたい場所, 設定する値);
,pstmt.setString(?の入れたい場所, 設定する値);
etc..
- 実行する SQL のパラメータにしたいところを
- SQL を実行
pstmt.executeUpdate();
, 実行結果として更新件数が返ってくる
- DB実行のオブジェクト
-
Connection
,PreparedStatement
は必ずclose
する
※ 細々は全件取得を参照、close 処理をしないで終わるとDB使えなくなってしまうことがあるので注意!! - かぶってる
Connection
の生成処理はメソッドにした方が楽そう
package business.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import business.entity.DBWork;
/**
* {@link DBWorkDaoJDBC}
*/
public class DBWorkDaoJDBC {
/**
* DBコネクション生成します
* @return DBコネクション
* @throws ClassNotFoundException クラスロード時の例外
* @throws SQLException コネクション生成時の例外
*/
private Connection createConnection() throws ClassNotFoundException, SQLException {
String dbUrl = "jdbc:mysql://localhost:3306/WEB_APP_DB";
String dbUser = "webapp";
String dbPassword = "webapp";
// JDBCドライバーをロード
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(dbUrl, dbUser, dbPassword);
}
/**
* DBWork を全件取得します
*/
public List<DBWork> findAll() {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection.prepareStatement("SELECT ID, NAME, VERSION FROM DB_WORK;");
// SQL実行
ResultSet rs = pstmt.executeQuery();
// SQL実行結果からデータを取得
List<DBWork> list = new ArrayList<>();
while (rs.next()) {
int id = rs.getInt("ID");
String name = rs.getString("NAME");
int version = rs.getInt("VERSION");
list.add(new DBWork(id, name, version));
}
return list;
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を登録します
* @param dbWork dbWork
* @return 登録件数
*/
public int insert(DBWork dbWork) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("INSERT INTO DB_WORK(NAME) VALUES(?);");
// SQL パラメータ設定
pstmt.setString(1, dbWork.getName());
// SQL実行
return pstmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
上で作った Dao
を使う Service
を作る ※ 全件取得で作ったやつにメソッド追加
- /java-webapp-clump/src/main/java/business/service/DBWorkService.java` に追加
- 登録のメソッドを作る(
dao
の登録処理を実行)
/**
* DBWork を登録します
*
* @param name 名前
* @return 登録件数
*/
public int insert(String name) {
DBWork dbWork = new DBWork();
dbWork.setName(name);
return dao.insert(dbWork);
}
上で作った Service
を使う Servlet
を作る ※ 全件取得で作ったやつを継承して作る
-
/java-webapp-clump/src/main/java/presentation/servlet
直下にクラスDBWorkInsertServlet.java
を作成 -
@WebServlet
に設定する URL を/db_work_insert
にする -
DBWorkServlet
を継承 -
doPost
の中でService
の登録処理を呼び出す
package presentation.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* {@link DBWorkInsertServlet}
*/
@WebServlet("/db_work_insert")
public class DBWorkInsertServlet extends DBWorkServlet {
/**
* {@inheritDoc}
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
service.insert(request.getParameter("name"));
doGet(request, response);
}
}
登録の動作確認
- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示
- 一番最後の行の NAME に適当な文言を入れて登録のアイコンを押下して確認(入力したNAMEで1行追加されればOK)
更新・削除処理
- テーブルのレコード情報を持つ
Entity
を作る ※ 全件取得で作成済 - DBに更新・削除する処理を持つ
Dao
を作る ※ 全件取得で作ったやつにメソッド追加 - 上で作った
Dao
を使うService
を作る ※ 全件取得で作ったやつにメソッド追加 - 上で作った
Service
を使うServlet
を作る ※ 全件取得で作ったやつを継承して作る
DBに更新・削除する処理を持つ Dao
を作る ※ 全件取得で作ったやつにメソッド追加
- /java-webapp-clump/src/main/java/business/dao/DBWorkDaoJDBC.java` に追加
- 更新・削除のメソッドを作成 ※ SQL が違うだけで登録と同じ
- 更新・削除で使うID指定の1件取得のメソッド作成 ※ SQL が違うだけで全件取得と同じ
-
Connection
,PreparedStatement
は必ずclose
する
※ 細々は全件取得を参照、くどいけど...close 処理をしないで終わるとDB使えなくなってしまうことがあるので注意!!!
/**
* DBWork を更新します
* @param dbWork dbWork
* @return 更新件数
*/
public int update(DBWork dbWork) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("UPDATE DB_WORK SET NAME = ?, VERSION = VERSION + 1 WHERE ID = ?;");
// SQL パラメータ設定
pstmt.setString(1, dbWork.getName());
pstmt.setInt(2, dbWork.getId());
// SQL実行
return pstmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を削除します
* @param dbWork dbWork
* @return 削除件数
*/
public int delete(DBWork dbWork) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("DELETE FROM DB_WORK WHERE ID = ? AND VERSION = ?;");
// SQL パラメータ設定
pstmt.setInt(1, dbWork.getId());
pstmt.setInt(2, dbWork.getVersion());
// SQL実行
return pstmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を取得します
* @param id ID
*/
public DBWork find(int id) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("SELECT ID, NAME, VERSION FROM DB_WORK WHERE ID = ?;");
// SQL パラメータ設定
pstmt.setInt(1, id);
// SQL実行
ResultSet rs = pstmt.executeQuery();
DBWork dbWork = null;
// SQL実行結果からデータを取得
while (rs.next()) {
String name = rs.getString("NAME");
int version = rs.getInt("VERSION");
dbWork = new DBWork(id, name, version);
}
return dbWork;
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
上で作った Dao
を使う Service
を作る ※ 全件取得で作ったやつにメソッド追加
-
/java-webapp-clump/src/main/java/business/service/DBWorkService.java
に追加 - 更新・削除のメソッドを作る(
dao
の登録処理を実行) - バージョンを使った楽観ロック
/**
* DBWork を更新します
*
* @param id ID
* @param name 名前
* @param version バージョン
* @return 更新件数
*/
public int update(int id, String name, int version) {
DBWork dbWork = dao.find(id);
if (dbWork == null || !dbWork.getVersion().equals(version)) {
return 0;
}
dbWork.setName(name);
return dao.update(dbWork);
}
/**
* DBWork を削除します
*
* @param id ID
* @param version バージョン
* @return 削除件数
*/
public int delete(int id, int version) {
DBWork dbWork = dao.find(id);
if (dbWork == null || !dbWork.getVersion().equals(version)) {
return 0;
}
return dao.delete(dbWork);
}
上で作った Service
を使う Servlet
を作る ※ 全件取得で作ったやつを継承して作る
-
/java-webapp-clump/src/main/java/presentation/servlet
直下にクラスDBWorkUpdateServlet.java
,DBWorkDeleteServlet.java
を作成 -
@WebServlet
に設定する URL を/db_work/update
,/db_work/delete
にする -
DBWorkServlet
を継承 -
doPost
の中でService
の更新・削除処理を呼び出す
package presentation.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* {@link DBWorkUpdateServlet}
*/
@WebServlet("/db_work_update")
public class DBWorkUpdateServlet extends DBWorkServlet {
/**
* {@inheritDoc}
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
service.update( //
Integer.parseInt(request.getParameter("id")) //
, request.getParameter("name") //
, Integer.parseInt(request.getParameter("version")));
doGet(request, response);
}
}
package presentation.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* {@link DBWorkDeleteServlet}
*/
@WebServlet("/db_work_delete")
public class DBWorkDeleteServlet extends DBWorkServlet {
/**
* {@inheritDoc}
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
service.delete( //
Integer.parseInt(request.getParameter("id")) //
, Integer.parseInt(request.getParameter("version")));
doGet(request, response);
}
}
更新・削除の動作確認
- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示
- 適当な行の NAME に適当な文言を入れて更新のアイコンを押下して確認(入力したNAMEで更新されればOK)
- 適当な行で削除のアイコンを押下して確認 (対象の行が削除されればOK)
参考:JDBC を使った DB処理のざっくり流れ
- JDBCドライバのロード処理
Class.forName("ドライバのパス");
※ ドライバのパスは使う DB によって違う -
Connection
の生成Connection connection = DriverManager.getConnection("DB接続URL", "ユーザ", "パスワード");
-
PreparedStatement
の取得PreparedStatement pstmt = connection.prepareStatement("実行したいSQL")
- SQLにパラメータ設定
- 実行する SQL のパラメータにしたいところを
?
にする -
?
にしたところに値を設定pstmt.setInt(?の入れたい場所, 設定する値);
,pstmt.setString(?の入れたい場所, 設定する値);
etc..
- 実行する SQL のパラメータにしたいところを
- SQL 実行と結果取得
- 参照系(select) の SQL実行
ResultSet rs = pstmt.executeQuery();
, 実行結果はResutlSet
でレコード件数が返ってくる - 更新系(insert, update, delete) の SQL実行
pstmt.executeUpdate()
, 実行結果はint
で更新件数で返ってくる
- 参照系(select) の SQL実行
-
ResultSet
からレコード取得-
while(rs.next())
でレコード件数分ループ処理 -
rs.getInt("カラム名")
でカラムの値を取得 - レコードの情報を設定した
Entity
を生成して戻り値に設定
-
package business.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import business.entity.DBWork;
/**
* {@link DBWorkDaoJDBC}
*/
public class DBWorkDaoJDBC {
/**
* DBコネクション生成します
* @return DBコネクション
* @throws ClassNotFoundException クラスロード時の例外
* @throws SQLException コネクション生成時の例外
*/
private Connection createConnection() throws ClassNotFoundException, SQLException {
String dbUrl = "jdbc:mysql://localhost:3306/WEB_APP_DB";
String dbUser = "webapp";
String dbPassword = "webapp";
// JDBCドライバーをロード
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(dbUrl, dbUser, dbPassword);
}
/**
* DBWork を全件取得します
*/
public List<DBWork> findAll() {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection.prepareStatement("SELECT ID, NAME, VERSION FROM DB_WORK;");
// SQL実行
ResultSet rs = pstmt.executeQuery();
// SQL実行結果からデータを取得
List<DBWork> list = new ArrayList<>();
while (rs.next()) {
int id = rs.getInt("ID");
String name = rs.getString("NAME");
int version = rs.getInt("VERSION");
list.add(new DBWork(id, name, version));
}
return list;
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を取得します
* @param id ID
*/
public DBWork find(int id) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("SELECT ID, NAME, VERSION FROM DB_WORK WHERE ID = ?;");
// SQL パラメータ設定
pstmt.setInt(1, id);
// SQL実行
ResultSet rs = pstmt.executeQuery();
DBWork dbWork = null;
// SQL実行結果からデータを取得
while (rs.next()) {
String name = rs.getString("NAME");
int version = rs.getInt("VERSION");
dbWork = new DBWork(id, name, version);
}
return dbWork;
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を登録します
* @param dbWork dbWork
* @return 登録件数
*/
public int insert(DBWork dbWork) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("INSERT INTO DB_WORK(NAME) VALUES(?);");
// SQL パラメータ設定
pstmt.setString(1, dbWork.getName());
// SQL実行
return pstmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を更新します
* @param dbWork dbWork
* @return 更新件数
*/
public int update(DBWork dbWork) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("UPDATE DB_WORK SET NAME = ?, VERSION = VERSION + 1 WHERE ID = ?;");
// SQL パラメータ設定
pstmt.setString(1, dbWork.getName());
pstmt.setInt(2, dbWork.getId());
// SQL実行
return pstmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
/**
* DBWork を削除します
* @param dbWork dbWork
* @return 削除件数
*/
public int delete(DBWork dbWork) {
// DBコネクション生成
try (Connection connection = createConnection()) {
// SQL実行オブジェクト生成
PreparedStatement pstmt = connection
.prepareStatement("DELETE FROM DB_WORK WHERE ID = ? AND VERSION = ?;");
// SQL パラメータ設定
pstmt.setInt(1, dbWork.getId());
pstmt.setInt(2, dbWork.getVersion());
// SQL実行
return pstmt.executeUpdate();
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
JPA (Java Persistence API) を使った DB処理
「JDBC 使ったDB処理」をやってることを前提にして以下をやってく感じになる
「JDBC 使ったDB処理」をやってないときは Entity, Service を新規作成, Servlet の修正が必要になる
- pom 更新 JPA eclipselink 参照追加
- persistence.xml 作る
- Entity アノテーション追加
- Daoつくる
- EntityManager 使って処理
- JPQL でSQL作成
- Service で使うDaoを変更
pom 更新 JPA eclipselink 参照追加
-
/java-webapp-clump/pom.xml
を修正 -
<dependencies>
・・・</dependencies>
の間に JPA eclipselink のdependency
を追加
<!-- JPA eclipselink -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.5.2</version>
</dependency>
persistence.xml(JPA の定義ファイル) 作る
- フォルダ
/java-webapp-clump/src/main/resources
直下にフォルダMETA-INF
を作成 - 上のフォルダ直下に
persistence.xml
を作成 -
persistence-unit
でリソースの定義を設定 -
property
で driver, url, user, password を設定 (サンプルではSQLログ出力する設定もあり)
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="webappdb" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/WEB_APP_DB"/>
<property name="javax.persistence.jdbc.user" value="webapp"/>
<property name="javax.persistence.jdbc.password" value="webapp"/>
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
</properties>
</persistence-unit>
</persistence>
Entity アノテーション追加
-
/java-webapp-clump/src/main/java/business/entity/DBWork.java
を修正 - クラスのアノテーションに
@Entity(name = "DB_WORK")
を追記 - インスタンス変数
id
のアノテーションに@Id
,@GeneratedValue(strategy = GenerationType.IDENTITY)
を追記 - インスタンス変数
version
のアノテーションに@Version
を追記 - インポートは必要に応じて追加
package business.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;
// この下の1行を追記
@Entity(name = "DB_WORK")
public class DBWork {
/** ID */
// この下の2行を追記
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/** 名前 */
private String name;
/** バージョン */
// この下の1行を追記
@Version
private Integer version;
・
・
}
Dao作る
-
/java-webapp-clump/src/main/java/business/dao
直下にDBWorkDaoJPA.java
を作成 -
EntityManeger
を生成Persistence.createEntityManagerFactory("xmlで定義した").createEntityManager();
- 全件取得
findAll
- SQL実行, 結果取得
entityManager.createQuery(query.select(query.from(DBWork.class))).getResultList()
-
CriteriaQuery
で SQLを作る ※JPQLで調べると情報がいろいろと出てきます
- SQL実行, 結果取得
- 1件取得
-
entityManager.find(Entityクラス, プライマリーキー);
で取得
-
- 登録, 更新, 削除 の共通処理
- トランザクション取得
entityManager.getTransaction();
- 登録・更新・削除処理の前にトランザクション開始
entityTransaction.begin();
- 登録・更新・削除処理の後にコミット
entityTransaction.commit();
- トランザクション取得
- 登録
entityManager.persist(Entityクラス);
- 更新
entityManager.merge(Entityクラス);
- 削除
entityManager.remove(Entityクラス);
- 引数の
Entityクラス
は同一トランザクション内でentityManager
で取得したものを渡す必要がある
package business.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.criteria.CriteriaQuery;
import business.entity.DBWork;
/**
* {@link DBWorkDaoJPA}
*/
public class DBWorkDaoJPA {
public EntityManager createEntityManager() {
return Persistence.createEntityManagerFactory("webappdb").createEntityManager();
}
/**
* DBWork を全件取得します
*/
public List<DBWork> findAll() {
EntityManager entityManager = createEntityManager();
CriteriaQuery<DBWork> query = entityManager.getCriteriaBuilder().createQuery(DBWork.class);
return entityManager.createQuery(query.select(query.from(DBWork.class))).getResultList();
}
/**
* DBWork を取得します
* @param id ID
*/
public DBWork find(int id) {
EntityManager entityManager = createEntityManager();
return entityManager.find(DBWork.class, id);
}
/**
* DBWork を登録します
* @param dbWork dbWork
* @return 登録件数
*/
public int insert(DBWork dbWork) {
EntityManager entityManager = createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
entityManager.persist(dbWork);
entityTransaction.commit();
return 1;
}
/**
* DBWork を更新します
* @param dbWork dbWork
* @return 更新件数
*/
public int update(DBWork dbWork) {
EntityManager entityManager = createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
entityManager.merge(dbWork);
entityTransaction.commit();
return 1;
}
/**
* DBWork を削除します
* @param dbWork dbWork
* @return 削除件数
*/
public int delete(DBWork dbWork) {
EntityManager entityManager = createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
entityManager.remove(entityManager.find(DBWork.class, dbWork.getId()));
entityTransaction.commit();
return 1;
}
}
Service で使うDaoを変更
-
/java-webapp-clump/src/main/java/business/service/DBWorkService.java
を修正 - dao を
DBWorkDaoJPA
に変更
package business.service;
import java.util.List;
import business.dao.DBWorkDaoJPA;
import business.entity.DBWork;
/**
* {@link DBWorkService}
*/
public class DBWorkService {
// この下の1行目がコメントアウト、2行目を追加
//DBWorkDaoJDBC dao = new DBWorkDaoJDBC();
DBWorkDaoJPA dao = new DBWorkDaoJPA();
・
・
}
Dao変更後の動作確認
- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示
- 更新・削除・追加などを適当にやって動作確認して動けばOK!!
参考:JPA を使った Dao を汎用的にする
JPAなどのORMを使った場合、汎用的な Dao を作って処理することが多い
そうするとテーブルに紐づく Entity を作るだけで DB 処理ができるようになっていく
また Entity についても DB 定義から自動生成する仕組みがあったりする
細かくは書かないので参考程度に興味あれば...
Entity のインターフェース作成
- id, version の getter を作る
- 各Entity は implements すれば OK
public class Entityクラス implements IFEntity<Idクラス>
- 例
public class DBWork implements IFEntity<Integer>
package business.entity;
public interface IFEntity<K> {
/**
* @return id
*/
public K getId();
/**
* @return version
*/
public Integer getVersion();
}
@Entity(name = "DB_WORK")
public class DBWork implements IFEntity<Integer> {
・
・
}
Dao を汎用的にする
- Type で entity クラス, id クラス を指定
- インスタンス変数に entityクラス を持つ
- コンストラクタで entity クラスを引数でもらって設定
- トランザクション開始する、しないで汎用的なメソッドを作成
- Service でインスタンス生成はこんな感じになる
DaoJPA<entityクラス, idクラス> dao = new DaoJPA<>(Class<entityクラス>);
- 例:
DaoJPA<DBWork, Integer> dao = new DaoJPA<>(DBWork.class);
package business.dao;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.criteria.CriteriaQuery;
import business.entity.IFEntity;
/**
* {@link DaoJPA}
*/
public class DaoJPA<T extends IFEntity<K>, K> {
Class<T> entityClass;
/**
* コンストラクタ
* @param entityClass entityClass
*/
public DaoJPA(Class<T> entityClass) {
this.entityClass = entityClass;
}
/**
* EntityManagerを生成します
* @return EntityManager
*/
public EntityManager createEntityManager() {
return Persistence.createEntityManagerFactory("webappdb").createEntityManager();
}
/**
* SQLを実行します
* @param function function
* @return entity
*/
public <E> E executeQuery(Function<EntityManager, E> function) {
EntityManager entityManager = createEntityManager();
return function.apply(entityManager);
}
/**
* 更新SQLをトランザクションを開始して実行します
* @param consumer consumer
*/
public void executeUpdate(Consumer<EntityManager> consumer) {
EntityManager entityManager = createEntityManager();
EntityTransaction entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
consumer.accept(entityManager);
entityTransaction.commit();
}
/**
* Entity を全件取得します
* @return entityリスト
*/
public List<T> findAll() {
return executeQuery(entityManager -> {
CriteriaQuery<T> query = entityManager.getCriteriaBuilder().createQuery(entityClass);
return entityManager.createQuery(query.select(query.from(entityClass))).getResultList();
});
}
/**
* Entity を取得します
* @param id ID
* @param entity
*/
public T find(K id) {
return executeQuery(entityManager -> entityManager.find(entityClass, id));
}
/**
* Entity を登録します
* @param entity entity
*/
public void insert(T entity) {
executeUpdate(entityManager -> entityManager.persist(entity));
}
/**
* Entity を更新します
* @param entity entity
*/
public void update(T entity) {
executeUpdate(entityManager -> entityManager.merge(entity));
}
/**
* Entity を削除します
* @param entity entity
*/
public void delete(T entity) {
executeUpdate(entityManager -> entityManager.remove(entityManager.find(entityClass, entity.getId())));
}
}
DaoJPA<DBWork, Integer> dao = new DaoJPA<>(DBWork.class);