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

Java のDB処理で `Service` , `Dao` (Data Accesss Object) を使ったときのはなし
`Service` と `DAO` と `Entity` の関係をざっくりまとめる
細かいことは気にしない！！いったんは流れが分かればOK！！！！
説明で使ってるサンプルプロジェクトのおおもとは GitHub [java-webapp-clump](https://github.com/sueasen/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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/86e3566e-9b1a-8a19-0773-1ed46616073f.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`)

```SQL
# 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` を追加

```XML: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` 作成

```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` にする

```Java: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 の動作確認

- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示を確認（以下みたいのが表示されればOK）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/232b5ea1-4dfe-4bde-e107-8231f04ac8f1.png)


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

## 全件取得処理

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

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

- `/java-webapp-clump/src/main/java/business` 直下にパッケージ `entity` を作成
- 上のパッケージ直下にクラス `DBWork.java` を作成
- テーブル `DB_WORK` に合わせてインスタンス変数を持ちコンストラクタ、getter, setter を作成

```Java: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` する
    ※ サンプルでは `Connection` を `try-with-resources` ( try の後の `()` で括ること) ですべての close 処理が実行される
    ※ close 処理を実行しないと Java からのDB接続が残ってしまいDBが使えなくなってしまうことがあるので注意
    ※ `try-with-resources` を使わないときは `finally` でエラー発生した時も必ず close 処理を実行する必要がある
    ※ Java、JDBC とかで検索してヒットするページを見て close 処理をする箇所を見ておくと良いかも．．．

```Java: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` の全件取得処理を実行）

```Java: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` に設定

```Java: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);
	}

}
```

### 全件取得の動作確認

- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示を確認（以下みたいのが表示されればOK）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/75bfb190-e21d-2baf-4515-85ad88c0f08b.png)


## 登録処理

1. テーブルのレコード情報を持つ `Entity` を作る ※ 全件取得で作成済
1. DBに登録する処理を持つ `Dao` を作る ※ 全件取得で作ったやつにメソッド追加
1. 上で作った `Dao` を使う `Service` を作る ※ 全件取得で作ったやつにメソッド追加
1. 上で作った `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` の生成処理はメソッドにした方が楽そう

```Java: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` の登録処理を実行）

```Java: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` の登録処理を呼び出す

```Java: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);
	}

}
```

### 登録の動作確認

- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示
- 一番最後の行の NAME に適当な文言を入れて登録のアイコンを押下して確認（入力したNAMEで1行追加されればOK）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/4f0641ac-fbaf-289d-8da0-b696b62bf77e.png)


## 更新・削除処理

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

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

- /java-webapp-clump/src/main/java/business/dao/DBWorkDaoJDBC.java` に追加
- 更新・削除のメソッドを作成 ※ SQL が違うだけで登録と同じ
- 更新・削除で使うID指定の1件取得のメソッド作成 ※ SQL が違うだけで全件取得と同じ
- `Connection`, `PreparedStatement` は必ず `close` する
    ※ 細々は全件取得を参照、くどいけど．．．close 処理をしないで終わるとDB使えなくなってしまうことがあるので注意！！！ 

```Java: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` の登録処理を実行）
- バージョンを使った楽観ロック

```Java: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` の更新・削除処理を呼び出す

```Java: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);
	}

}
```

```Java: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/66b0f920-dbf3-8046-fed3-d85b18e97bf4.png)

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

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

```Java: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 参照追加 
1. persistence.xml 作る
1. Entity アノテーション追加
1. Daoつくる
    - EntityManager 使って処理
    - JPQL でSQL作成
1. Service で使うDaoを変更

### pom 更新 JPA eclipselink 参照追加

- `/java-webapp-clump/pom.xml` を修正
- `<dependencies>` ・・・ `</dependencies>` の間に JPA eclipselink の `dependency` を追加

```XML
    <!-- 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: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` を追記
- インポートは必要に応じて追加

```Java: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` で取得したものを渡す必要がある

```Java: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` に変更

```Java: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変更後の動作確認

- http://localhost:8080/java-wabapp-clump/db_work でブラウザ表示
- 更新・削除・追加などを適当にやって動作確認して動けばOK！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/1d6a84ca-acca-64ae-08ca-3180b8e9ad5a.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>`

```Java:IFEntity.java
package business.entity;

public interface IFEntity<K> {

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

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

}
```

```Java: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);`

```Java: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())));
	}

}

```

```Java:Service

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

```