JDBC勉強備忘録（Insert文）

## 備忘録目的
DAOに関しての自分が認識していることの確認と躓いたところや調査方法、仮定したことなど、過程を記録する。
（技術的なことはサイトのリンクを貼る）

## 環境
OS：macOS 12.5.1
IDE:Eclipse IDE for Enterprise Java Developers.
　　Version: 2019-12 (4.14.0)
　　Build id: 20191212-1212
DB：Ver 8.0.32 for macos12.6 on x86_64 (Homebrew)
クラスパス:mysql-connector-j-8.0.32.jar
その他：本文に追記

# 本文
## 値の設定
```Java:/Sample_4/src/j4_02/Sample4_02_1_Common_DAO.java
			//-------------------------------------------
			//SQL文の送信 ＆ 結果の取得
			//-------------------------------------------

			//発行するSQL文の生成（INSERT）
			StringBuffer buf = new StringBuffer();
			buf.append(" INSERT INTO uzuz_member ( ");
			buf.append("   id     ,                ");
			buf.append("   name   ,                ");
			buf.append("   gender ,                ");
			buf.append("   age    ,                ");
			buf.append("   course                  ");
			buf.append(" ) VALUES (                ");
			buf.append("   ?,                      ");  //第1パラメータ
			buf.append("   ?,                      ");  //第2パラメータ
			buf.append("   ?,                      ");  //第3パラメータ
			buf.append("   ?,                      ");  //第4パラメータ
			buf.append("   ?                       ");  //第5パラメータ
			buf.append(" )                         ");

			//PreparedStatementオブジェクトを生成＆発行するSQLをセット
			ps = con.prepareStatement(buf.toString());

			//パラメータをセット
			ps.setInt(    1, dto.getId()     );  //第1パラメータ：更新データ（ID）
			ps.setString( 2, dto.getName()   );  //第2パラメータ：更新データ（名前）
			ps.setString( 3, dto.getGender() );  //第3パラメータ：更新データ（性別）
			ps.setInt(    4, dto.getAge()    );  //第4パラメータ：更新データ（年齢）
			ps.setString( 5, dto.getCourse() );  //第5パラメータ：更新データ（コース）
```
上記のサンプルコードから、「実行の構成」に配列で必要であると仮定したため、
「Eclipse2019 実行の構成 配列」で検索をする。
[このサイト](https://workteria.forward-soft.co.jp/blog/detail/10315)で、実行の構成に配列で値を入れたい場合は、半角スペースを値の間に入れることでできると仮定。
`100 ogawa men 24 X`と値を入れる。

結果、`[INFO]該当のユーザー情報を取得できませんでした`と表示される。
![61C2A7F8-B764-48A9-AFE5-C0FBCC1D63A8_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551401/09929045-1a09-eed9-e86e-9eb3115fad2f.jpeg)
ただ、`Sel_Main`で終了していることから、以下２つの可能性を仮定。

１、Ins_Main（Insert処理のファイル）で実行していない。

２、作成ができておらず、例外処理が行われた。

まずは、データベースに変わりがないかを調べ、作成はできているが、select処理だけが例外となっている可能性がないかも調べる。
```
mysql> select * from UZUZ_MEMBER;
+----+--------+--------+-----+--------+
| ID | NAME   | GENDER | AGE | COURSE |
+----+--------+--------+-----+--------+
|  1 | MOCO   | F      |   4 | PG     |
|  2 | CHOCO  | M      |   7 | INFRA  |
|  3 | TARO   | M      |   5 | PG     |
|  4 | RINRIN | F      |   3 | PG     |
|  5 | CHAMP  | M      |   5 | MARKET |
+----+--------+--------+-----+--------+
5 rows in set (0.01 sec)
```
上記のことから、作成されていないことが確認できた。

１と２、どちらを先に確認しようかと考えたが、Ins_Mainで2に沿うような処理になっていないかを確認することにした。
```Java:/Sample_4/src/j4_02/Sample4_02_1_Ins_Main.java
package j4_02;

/**----------------------------------------------------------------------*
 *■■■Sample4_02_1_Ins_Mainクラス■■■
 *概要：メイン（ユーザーの登録）
 *----------------------------------------------------------------------**/
public class Sample4_02_1_Ins_Main {
	public static void main (String[] args) {

		//コマンドラインから登録するユーザーの情報を受け取りDTOに格納
		Sample4_02_1_Common_DTO subscribeDto = new Sample4_02_1_Common_DTO();
		subscribeDto.setId(     Integer.parseInt(args[0])  );
		subscribeDto.setName(   args[1]                    );
		subscribeDto.setGender( args[2]                    );
		subscribeDto.setAge(    Integer.parseInt(args[3])  );
		subscribeDto.setCourse( args[4]                    );

		//ビジネスロジック（ユーザーの登録）クラスのインスタンス化＆メソッド起動
		Sample4_02_1_Ins_BusinessLogic blDel = new Sample4_02_1_Ins_BusinessLogic();
		blDel.subscribe( subscribeDto );

	}
}
```
`Sample4_02_1_Ins_BusinessLogic`の確認も必要になった。

```java:/Sample_4/src/j4_02/Sample4_02_1_Ins_BusinessLogic.java
package j4_02;

/**----------------------------------------------------------------------*
 *■■■Sample4_02_1_Ins_BusinessLogicクラス■■■
 *概要：ビジネスロジック（ユーザーの登録）
 *----------------------------------------------------------------------**/
public class Sample4_02_1_Ins_BusinessLogic {

	/**----------------------------------------------------------------------*
	 *■subscribeメソッド
	 *概要　：対象のユーザーを登録する
	 *引数　：対象のユーザーデータ（Sample4_02_1_Common_DTO型）
	 *戻り値：なし
	 *----------------------------------------------------------------------**/
	public void subscribe (Sample4_02_1_Common_DTO targetUserDto) {

		//-------------------------------------------
		//データベースへの接続を実施
		//-------------------------------------------

		//DAOクラスをインスタンス化＆対象のユーザーデータを登録するよう依頼
		Sample4_02_1_Common_DAO dao = new Sample4_02_1_Common_DAO();
		boolean result = dao.insertMemberInfo(targetUserDto);

		//終了メッセージを表示
		if(result){
			System.out.println("[INFO]登録処理が正常終了しました" ) ;
		}else{
			System.out.println("[INFO]登録処理に失敗しました" ) ;
		}

	}
}
```
`dao.insertMemberInfo`の確認も必要。
```java:/Sample_4/src/j4_02/Sample4_02_1_Common_DAO.java
public boolean insertMemberInfo(Sample4_02_1_Common_DTO dto){

		//-------------------------------------------
		//JDBCドライバのロード
		//-------------------------------------------
		try {
			Class.forName(DRIVER_NAME);       //JDBCドライバをロード＆接続先として指定
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		//-------------------------------------------
		//SQL発行
		//-------------------------------------------

		//JDBCの接続に使用するオブジェクトを宣言
		//※finallyブロックでも扱うためtryブロック内で宣言してはいけないことに注意
		Connection        con = null ;   // Connection（DB接続情報）格納用変数
		PreparedStatement ps  = null ;   // PreparedStatement（SQL発行用オブジェクト）格納用変数

		//実行結果（真：成功、偽：例外発生）格納用変数
		//※最終的にreturnするため、tryブロック内で宣言してはいけないことに注意
		boolean isSuccess = true ;

		try {

			//-------------------------------------------
			//接続の確立（Connectionオブジェクトの取得）
			//-------------------------------------------
			con = DriverManager.getConnection(JDBC_URL, USER_ID, USER_PASS);

			//-------------------------------------------
			//トランザクションの開始
			//-------------------------------------------
			//オートコミットをオフにする（トランザクション開始）
			con.setAutoCommit(false);

			//-------------------------------------------
			//SQL文の送信 ＆ 結果の取得
			//-------------------------------------------

			//発行するSQL文の生成（INSERT）
			StringBuffer buf = new StringBuffer();
			buf.append(" INSERT INTO uzuz_member ( ");
			buf.append("   id     ,                ");
			buf.append("   name   ,                ");
			buf.append("   gender ,                ");
			buf.append("   age    ,                ");
			buf.append("   course                  ");
			buf.append(" ) VALUES (                ");
			buf.append("   ?,                      ");  //第1パラメータ
			buf.append("   ?,                      ");  //第2パラメータ
			buf.append("   ?,                      ");  //第3パラメータ
			buf.append("   ?,                      ");  //第4パラメータ
			buf.append("   ?                       ");  //第5パラメータ
			buf.append(" )                         ");

			//PreparedStatementオブジェクトを生成＆発行するSQLをセット
			ps = con.prepareStatement(buf.toString());

			//パラメータをセット
			ps.setInt(    1, dto.getId()     );  //第1パラメータ：更新データ（ID）
			ps.setString( 2, dto.getName()   );  //第2パラメータ：更新データ（名前）
			ps.setString( 3, dto.getGender() );  //第3パラメータ：更新データ（性別）
			ps.setInt(    4, dto.getAge()    );  //第4パラメータ：更新データ（年齢）
			ps.setString( 5, dto.getCourse() );  //第5パラメータ：更新データ（コース）

			//SQL文の送信＆戻り値として追加件数を取得
			int insCount = ps.executeUpdate();

			//SQL実行結果を表示
			System.out.println("[INFO]" + insCount + "行追加しました") ;

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

			//実行結果を例外発生として更新
			isSuccess = false ;

		} finally {
			//-------------------------------------------
			//トランザクションの終了
			//-------------------------------------------
			if(isSuccess){
				//明示的にコミットを実施
				try {
					con.commit();
				} catch (SQLException e) {
					e.printStackTrace();
				}

			}else{
				//明示的にロールバックを実施
				try {
					con.rollback();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

			//-------------------------------------------
			//接続の解除
			//-------------------------------------------

			//PreparedStatementオブジェクトの接続解除
			if (ps != null) {    //接続が確認できている場合のみ実施
				try {
					ps.close();  //接続の解除
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

			//Connectionオブジェクトの接続解除
			if (con != null) {    //接続が確認できている場合のみ実施
				try {
					con.close();  //接続の解除
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}

		}

		//実行結果を戻す
		return isSuccess ;

	}
```
わかりやすく、Sel_Mainを使用はしていないため、例外処理を実施したか、実行ファイルを間違えたと仮定。

次は1でやっていなかったか確認するため、もう一度行う。（できれば、ログがないかを確認し、確認できれば良いと思う）

![FF9D8A31-4519-4FEB-8DC7-FD479A244CDB.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551401/72ba650a-d8e5-0966-733f-9f64a37cbe3d.jpeg)

実行の構成を行う際に、上記画像のダイアログの内容だったことから、Sel_Mainでやっていたのだと仮定できる。
そのため、メイン・クラスを変更し、実行した。

結果、エラーになった。だが、エラーよりも、下記画像の赤枠部分が気になった。
![4545C941-F15C-4878-B837-CB334668C10D_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551401/18825c95-6116-ab5b-6844-22abd93e4bba.jpeg)

なぜ、Sel_Mainで終了してんの？
だが、エラー内容と`[INFO]登録処理に失敗しました`という表示から、Ins_Mainの処理が行われたと考えられる。

そのため、Sel_Mainで終了していることは一旦置いて、エラー内容を確認することにする。
（以下、エラー内容を記述している）
```
com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column 'GENDER' at row 1
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:104)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1061)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1009)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1320)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:994)
	at j4_02.Sample4_02_1_Common_DAO.insertMemberInfo(Sample4_02_1_Common_DAO.java:229)
	at j4_02.Sample4_02_1_Ins_BusinessLogic.subscribe(Sample4_02_1_Ins_BusinessLogic.java:23)
	at j4_02.Sample4_02_1_Ins_Main.main(Sample4_02_1_Ins_Main.java:20)
[INFO]登録処理に失敗しました
```
`Data truncation: Data too long for column 'GENDER' at row 1`という内容からGENDERの値が長すぎる（バリデーション？　カラムの設定？　String型だから標準的には問題なさそう）と考え、データベースの設定を確認する。

「mysql カラム 設定」で検索し、[このサイト](https://phpjavascriptroom.com/?t=mysql&p=add)を参考に、カラムを調べた。
以下の結果になった。
```
mysql> show full columns from UZUZ_MEMBER like 'GENDER';
+--------+---------+--------------------+------+-----+---------+-------+---------------------------------+----------------------------------------+
| Field  | Type    | Collation          | Null | Key | Default | Extra | Privileges                      | Comment                                |
+--------+---------+--------------------+------+-----+---------+-------+---------------------------------+----------------------------------------+
| GENDER | char(1) | utf8mb4_0900_ai_ci | NO   |     | NULL    |       | select,insert,update,references | ﾀ性ﾊ別｡（ﾃ男｡：M｡／ｽ女｡：F｡）          |
+--------+---------+--------------------+------+-----+---------+-------+---------------------------------+----------------------------------------+
1 row in set (0.26 sec)
```
`char(1)`であることから、アルファベット１文字文であると考えられる。
また、テーブルの中身を確認すると、以下のように表示された。
```
mysql> select * from UZUZ_MEMBER;
+----+--------+--------+-----+--------+
| ID | NAME   | GENDER | AGE | COURSE |
+----+--------+--------+-----+--------+
|  1 | MOCO   | F      |   4 | PG     |
|  2 | CHOCO  | M      |   7 | INFRA  |
|  3 | TARO   | M      |   5 | PG     |
|  4 | RINRIN | F      |   3 | PG     |
|  5 | CHAMP  | M      |   5 | MARKET |
+----+--------+--------+-----+--------+
5 rows in set (0.01 sec)
```
以上のことから、引数のGENDERのみをMに変更し、実行する。
```
[INFO]1行追加しました
[INFO]登録処理が正常終了しました
```
成功した。　と思いたいが、念のためにデータベースでも確認をする。
```
mysql> select * from UZUZ_MEMBER;
+-----+--------+--------+-----+--------+
| ID  | NAME   | GENDER | AGE | COURSE |
+-----+--------+--------+-----+--------+
|   1 | MOCO   | F      |   4 | PG     |
|   2 | CHOCO  | M      |   7 | INFRA  |
|   3 | TARO   | M      |   5 | PG     |
|   4 | RINRIN | F      |   3 | PG     |
|   5 | CHAMP  | M      |   5 | MARKET |
| 100 | ogawa  | M      |  24 | X      |
+-----+--------+--------+-----+--------+
6 rows in set (0.00 sec)
```
成功している。
ただ、いまだにSel_Mainで終了していると表示されるのはどうなんだろう？
Eclipse2019の仕様なのだろうか？

調べ方がぱっと思いつかないのと、重要性は低いと考えれるため、置いておくことにする。

ここにきて、前回のselect処理では諦めていた、複数の値を入れることができないかを確認して見ることにする。

やり方としては、「1 2 3 4 5 100」と入れる。その前に、「100」だけを入れて、新しい値も検索できるか確認して見る（ほぼ必要のない確認。　逆にエラーとか起きてくれた方が面白い）
```
**************取得結果***************
*ID：100
*名前：ogawa
*性別(M:男,F:女)：M
*年齢：24
*コース：X
*************************************
```
普通に表示できた（つまらん）。
複数を試してみる（配列が多いことによるエラーになると予想）
```
**************取得結果***************
*ID：1
*名前：MOCO
*性別(M:男,F:女)：F
*年齢：4
*コース：PG
*************************************
```
裏側の処理はどうなってんだ！？
select処理に複数の場合の処理はないと考えれるため、Eclipseの処理ではないかと考えられる。
知りたいとも思うが、JDBCの勉強に大きく関係はしないので、一旦置くことにした。

ここで、以下のことができないか考えた。
・IDは自動的に加算される仕様が良いと考えられるため、ユーザー（コマンドライン）の入力から、IDを除き、自動的にIDが加算されるように設定する。

・今回のようなデータベースの設定と合わずにエラーとなることや、セキュリティー、ビジネス面（年齢やコースの制限）を考え、バリデーションと入力方法の表示を行う。

とりあえず、上記のことは置いておき、残りのupdateとdelete処理を確認しようと思う。

次回、*JDBC勉強備忘録（Update文)*