8
10

More than 1 year has passed since last update.

Service と Dao(DB処理) と Entity ざっくりまとめ

Last updated at Posted at 2022-11-06

Java のDB処理で Service , Dao (Data Accesss Object) を使ったときのはなし
ServiceDAOEntity の関係をざっくりまとめる
細かいことは気にしない!!いったんは流れが分かれば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 を使う

image.png

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 を追加
pom.xml
  <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 作成
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 にする
DBWorkServlet.java
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 の動作確認

image.png

JDBC (Java Database Connectivity) を使った DB処理

全件取得処理

  1. テーブルのレコード情報を持つ Entity を作る
  2. DBから全件取得する処理を持つ Dao を作る
  3. 上で作った Dao を使う Service を作る
  4. 上で作った Service を使う Servlet を作る

テーブルのレコード情報を持つ Entity を作る

  • /java-webapp-clump/src/main/java/business 直下にパッケージ entity を作成
  • 上のパッケージ直下にクラス DBWork.java を作成
  • テーブル DB_WORK に合わせてインスタンス変数を持ちコンストラクタ、getter, setter を作成
DBWork.java
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 を生成して戻り値に設定
  • Connection, PreparedStatement, ResultSet は必ず close する
    ※ サンプルでは Connectiontry-with-resources ( try の後の () で括ること) ですべての close 処理が実行される
    ※ close 処理を実行しないと Java からのDB接続が残ってしまいDBが使えなくなってしまうことがあるので注意
    try-with-resources を使わないときは finally でエラー発生した時も必ず close 処理を実行する必要がある
    ※ Java、JDBC とかで検索してヒットするページを見て close 処理をする箇所を見ておくと良いかも...
DBWorkDaoJDBC.java
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 の全件取得処理を実行)
DBWorkService.java
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 に設定
DBWorkServlet.java
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);
	}

}

全件取得の動作確認

image.png

登録処理

  1. テーブルのレコード情報を持つ Entity を作る ※ 全件取得で作成済
  2. DBに登録する処理を持つ Dao を作る ※ 全件取得で作ったやつにメソッド追加
  3. 上で作った Dao を使う Service を作る ※ 全件取得で作ったやつにメソッド追加
  4. 上で作った Service を使う Servlet を作る ※ 全件取得で作ったやつを継承して作る

DBに登録する処理を持つ Dao を作る ※ 全件取得で作ったやつにメソッド追加

  • /java-webapp-clump/src/main/java/business/dao/DBWorkDaoJDBC.java に追加, 修正
  • 登録のメソッドを作成
    • DB実行のオブジェクト PreparedStatement の取得までは全件取得と同じ
    • SQLにパラメータを設定
      • 実行する SQL のパラメータにしたいところを ? にする
      • ? にしたところに値を設定 pstmt.setInt(?の入れたい場所, 設定する値);, pstmt.setString(?の入れたい場所, 設定する値); etc..
    • SQL を実行 pstmt.executeUpdate(); , 実行結果として更新件数が返ってくる
  • Connection, PreparedStatement は必ず close する
    ※ 細々は全件取得を参照、close 処理をしないで終わるとDB使えなくなってしまうことがあるので注意!!
  • かぶってる Connection の生成処理はメソッドにした方が楽そう
DBWorkDaoJDBC.java
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 の登録処理を実行)
DBWorkService.java
	/**
	 * 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 の登録処理を呼び出す
DBWorkInsertServlet.java
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);
	}

}

登録の動作確認

image.png

更新・削除処理

  1. テーブルのレコード情報を持つ Entity を作る ※ 全件取得で作成済
  2. DBに更新・削除する処理を持つ Dao を作る ※ 全件取得で作ったやつにメソッド追加
  3. 上で作った Dao を使う Service を作る ※ 全件取得で作ったやつにメソッド追加
  4. 上で作った Service を使う Servlet を作る ※ 全件取得で作ったやつを継承して作る

DBに更新・削除する処理を持つ Dao を作る ※ 全件取得で作ったやつにメソッド追加

  • /java-webapp-clump/src/main/java/business/dao/DBWorkDaoJDBC.java` に追加
  • 更新・削除のメソッドを作成 ※ SQL が違うだけで登録と同じ
  • 更新・削除で使うID指定の1件取得のメソッド作成 ※ SQL が違うだけで全件取得と同じ
  • Connection, PreparedStatement は必ず close する
    ※ 細々は全件取得を参照、くどいけど...close 処理をしないで終わるとDB使えなくなってしまうことがあるので注意!!!
DBWorkDaoJDBC.java
	/**
	 * 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 の登録処理を実行)
  • バージョンを使った楽観ロック
DBWorkService.java
	/**
	 * 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 の更新・削除処理を呼び出す
DBWorkUpdateServlet.java
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);
	}

}
DBWorkDeleteServlet.java
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)

image.png

参考:JDBC を使った DB処理のざっくり流れ

  1. JDBCドライバのロード処理 Class.forName("ドライバのパス");
    ※ ドライバのパスは使う DB によって違う
  2. Connection の生成 Connection connection = DriverManager.getConnection("DB接続URL", "ユーザ", "パスワード");
  3. PreparedStatement の取得 PreparedStatement pstmt = connection.prepareStatement("実行したいSQL")
  4. SQLにパラメータ設定
    • 実行する SQL のパラメータにしたいところを ? にする
    • ? にしたところに値を設定 pstmt.setInt(?の入れたい場所, 設定する値);, pstmt.setString(?の入れたい場所, 設定する値); etc..
  5. SQL 実行と結果取得
    • 参照系(select) の SQL実行 ResultSet rs = pstmt.executeQuery();, 実行結果はResutlSet でレコード件数が返ってくる
    • 更新系(insert, update, delete) の SQL実行 pstmt.executeUpdate(), 実行結果は int で更新件数で返ってくる
  6. ResultSet からレコード取得
    • while(rs.next()) でレコード件数分ループ処理
    • rs.getInt("カラム名") でカラムの値を取得
    • レコードの情報を設定した Entity を生成して戻り値に設定
DBWorkDaoJDBC.java
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 の修正が必要になる

  1. pom 更新 JPA eclipselink 参照追加
  2. persistence.xml 作る
  3. Entity アノテーション追加
  4. Daoつくる
    • EntityManager 使って処理
    • JPQL でSQL作成
  5. 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ログ出力する設定もあり)
persistence.xml
<?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 を追記
  • インポートは必要に応じて追加
DBWork.java
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で調べると情報がいろいろと出てきます
  • 1件取得
    • entityManager.find(Entityクラス, プライマリーキー); で取得
  • 登録, 更新, 削除 の共通処理
    • トランザクション取得 entityManager.getTransaction();
    • 登録・更新・削除処理の前にトランザクション開始 entityTransaction.begin();
    • 登録・更新・削除処理の後にコミット entityTransaction.commit();
  • 登録
    • entityManager.persist(Entityクラス);
  • 更新
    • entityManager.merge(Entityクラス);
  • 削除
    • entityManager.remove(Entityクラス);
    • 引数の Entityクラス は同一トランザクション内で entityManager で取得したものを渡す必要がある
DBWorkDaoJPA.java
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 に変更
DBWorkService.java
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変更後の動作確認

image.png

参考: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>
IFEntity.java
package business.entity;

public interface IFEntity<K> {

	/**
	 * @return id
	 */
	public K getId();

	/**
	 * @return version
	 */
	public Integer getVersion();

}
Entity

@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);
DaoJPA.java
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())));
	}

}

Service

	DaoJPA<DBWork, Integer> dao = new DaoJPA<>(DBWork.class);

8
10
0

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
8
10