【Java】MySQLの操作方法（Windows）　その２：Javaでの操作

#はじめに
前回、DBの準備編にて作成したDBを用いて
JavaでのDB操作をやってみたいと思います。

[前回の記事 → 【Java】MySQLの操作方法（Windows）　その１：DB準備編](https://qiita.com/ha_ru/items/76398ec77802a2fa3df0)

#前提
動作環境は下記の通りです。

| 項目 | 情報 | 備考 |
|:-:|:-:|:-:|
| Java バージョン |  14.0.2 | コマンドプロンプトにて java -version で確認
| MySQL バージョン  | 8.0.24  | コマンドプロンプトにて mysql --version で確認

```
C:\>java -version
java version "14.0.2" 2020-07-14
Java(TM) SE Runtime Environment (build 14.0.2+12-46)
Java HotSpot(TM) 64-Bit Server VM (build 14.0.2+12-46, mixed mode, sharing)

C:\>mysql --version
mysql  Ver 8.0.24 for Win64 on x86_64 (MySQL Community Server - GPL)
```

#準備
プロジェクトにJDBCドライバーを組み込みます。
・[プロジェクト] -> [プロパティ] を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/31ccc536-12b8-2943-9e0f-0de8e88cf083.png)

・[Javaのビルド・パス] -> [ライブラリ] -> [外部JARの追加] にて、
C:\Program Files (x86)\MySQL\Connector J 8.0\mysql-connector-java-8.0.24.jar
を選択し、追加します。
（パスは異なる場合がありますので、ご自身の環境に合わせてください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/e8d297d3-8bbe-4dbc-bee3-51a3d555cb57.png)





#実践（Javaでの操作）
###１．１　データベースへ接続
まずは、データベースへ接続してみます。

データベースへ接続するために、java.sql.Connectionのインスタンスを取得します。
今回は、DriverManagerのgetConnection()を使用して取得します。
（この他にも、DIコンテナを使用するなど様々な手法が存在する様子・・）

```SqlConnect.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class SqlConnect {
	public static void main(String[] args) {
		//	データベース接続で使用する情報
		final String jdbcId = "root";  // データベースへ接続するユーザ名
		final String jdbcPass = "root";  // パスワード
		final String jdbcUrl = "jdbc:mysql://localhost:3306/sample";  // ローカル実行なので、localhostを指定。ポートもデフォルトの3306。テスト用のデータベース名のsampleを指定。

		//	データベースへの接続
		try (Connection db = DriverManager.getConnection(jdbcUrl, jdbcId, jdbcPass)) {
			System.out.println("接続に成功！！");
		} catch (SQLException e) {
			System.out.println("接続に失敗：" + e.getMessage());
		}
	}
}
```
データベースへの接続が成功すると、コンソールに「接続に成功！！」と表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/ac9b6243-22bb-21d4-393c-8887bf75a1e8.png)

なお、データベースへの接続時のclose処理などの後片付けを自動的にしてくれる点では、try-with-resources構文を使用するのが良いらしい。
[参考：try-finallyよりもtry-with-resourcesを使おう](https://qiita.com/NagaokaKenichi/items/124f0e14ce5dfcbd0b6b)

しかし、後述のトランザクション処理利用時には、
tryブロックから抜ける際に、catchブロック実行前に自動的にclose処理が動作するため、
明示的なロールバックが記述できないなどのデメリットもある様子・・・


###１．２　データベースの検索
データベース内のデータを検索し、取得します。

```SqlQuery.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SqlQuery {
	public static void main(String[] args) {
		//	データベース接続用の情報
		final String jdbcId = "root";
		final String jdbcPass = "root";
		final String jdbcUrl = "jdbc:mysql://localhost:3306/sample";

		try (Connection db = DriverManager.getConnection(jdbcUrl, jdbcId, jdbcPass)) {
			PreparedStatement ps = db.prepareStatement("select * from members where sex = ?");
			ps.setString(1, "女");
			ResultSet rs = ps.executeQuery();

			while (rs.next()) {
				System.out.println(rs.getString("name"));
				System.out.println(rs.getString("sex"));
				System.out.println(rs.getString("age"));
				System.out.println("*****************************");
			}
		} catch (SQLException e) {
			System.out.println("接続に失敗：" + e.getMessage());
		}
	}
}
```
まずは、
PreparedStatementを使用して、実行したいSQLのひな型を作成します。
後で値を入れたい部分については、?マークを記述します。（パラメータと呼ばれる）

次に、
?部分にsetStringを使用して、具体的な値を入れます。
（今回は文字列を入れるので、setStringを使用）
setString（パラメータ番号, 文字列）の形。（パラメータ番号は１から始まる。?の位置に対応）

準備ができたので、
SQL文をexecuteQueryを使用して、送信します。
結果はResultSetを使用して受け取ります。

最後に、受け取ったResultSetの中身をwhile文を用いて取り出します。
ResultSetのnextメソッドは戻り値をboolean型で返すので、値がある分だけループで取り出せます。


実行結果は下記の通りとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/7c32c251-8c95-7afb-613e-8684bb92c335.png)

###１．３　データベースへのデータ挿入
INSERT文を用いて、データの登録を行います。

```SqlUpdate.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class SqlUpdate {
	public static void main(String[] args) {
		final String jdbcId = "root";
		final String jdbcPass = "root";
		final String jdbcUrl = "jdbc:mysql://localhost:3306/sample";

		String sql = "INSERT INTO members (id, name, sex, age) VALUES (?, ?, ?, ?)";

		try (Connection db = DriverManager.getConnection(jdbcUrl, jdbcId, jdbcPass)) {
			PreparedStatement ps = db.prepareStatement(sql);

			ps.setString(1, "20210430");
			ps.setString(2, "スーパー太郎");
			ps.setString(3, "男");
			ps.setInt(4, 35);

			int result = ps.executeUpdate();
			System.out.println(result + "行が追加されました。");
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
```
スーパー太郎さんを追加しています。
PreparedStatementを使用して、SQLのひな型を準備し、
setString、setIntを使用して、具体的な値を入れ込む部分は検索と同じです。

SQLを送信する際のメソッドが、executeUpdateメソッドになっています。
また、戻り値は「データベースで変更された行数」となるので、int型で受けています。

実行するとこの様な感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/6e39d45f-e036-e540-415f-514753f06552.png)

本当に追加されたかは、１．２の検索の条件を変更して実行すれば確認できます。

```
変更前：ps.setString(1, "女");
変更後：ps.setString(1, "男");
```

実行すると、下記のように追加されている事が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/1b7ba455-005f-ab25-6564-90d0b315e4d2.png)


###１．４　トランザクション処理
最後にトランザクション処理をやってみます。
なお、トランザクション処理そのものの解説は行いませんので、ググって調べてください。

```SqlTransact.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class SqlTransact {
	public static void main(String[] args) {
		//	データベース接続用の情報
		final String jdbcId = "root";
		final String jdbcPass = "root";
		final String jdbcUrl = "jdbc:mysql://localhost:3306/sample";

		String sql1 = "INSERT INTO members (id, name, sex, age) VALUES (?, ?, ?, ?)";
		String sql2 = "INSERT INTO members (id, name, sex, age) VALUES (?, ?, ?, ?)";

		try (Connection db = DriverManager.getConnection(jdbcUrl, jdbcId, jdbcPass)) {
			//	トランザクション処理のため、自動コミットをオフ
			db.setAutoCommit(false);

			try (PreparedStatement ps1 = db.prepareStatement(sql1) ; PreparedStatement ps2 = db.prepareStatement(sql2)) {
				ps1.setString(1, "20210431");
				ps1.setString(2, "田中春香");
				ps1.setString(3, "女");
				ps1.setInt(4, 20);
				ps1.executeUpdate();

				ps2.setString(1, "20210431");
				ps2.setString(2, "鈴木春香");
				ps2.setString(3, "女");
				ps2.setInt(4, 24);
				ps2.executeUpdate();

				//	データベースへコミット
				db.commit();
			} catch (SQLException e) {
				//	データベースをロールバック
				db.rollback();
				System.out.println("処理エラー：" + e.getMessage());
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

```

トランザクション処理を行うためには、自動コミットをオフにするために
データベース接続に取得したConnection（dbという変数）に対して、
setAutoCommit(false)を実行しています。

そして一連の処理が完了した後に、commit()を呼び出し、コミット処理を実行しています。
なお、途中で処理が失敗した場合には、
処理をキャンセルするために、rollback()を呼び出しています。

今回の例では、idがプライマリキーとなっているにも関わらず、
重複して登録しようとしているので、処理がエラーとなり、誰も追加されません。
（鈴木さんの追加時（ps2のほう）、エラーとなる。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/775272/be7eb4b9-0b2b-0cf2-d52a-daf305953aae.png)

下記のように変更を行えば、正常に登録がされます。

```
変更前：ps2.setString(1, "20210431");
変更後：ps2.setString(1, "20210432");
```

#おわりに
Javaを用いて、データベース操作を行ってみました。
今回のやり方以外にも手法が存在するようなので、都度、学習していけたらと思います。

[参考：Javaコード入門](https://java-code.jp/category/database)
[参考：スッキリわかるJava入門 実践編 第3版](https://sukkiri.jp/books/sukkiri_javap3)