JavaでSQL ServerのCRUD

前章でSelectを実行して表示してみましたので、少し加えてCRUDしてみましょう。

JavaでMVCでCRUDしてみる

MVCをご存じでしょうか。以前やっていますので、Servletでデータベースを読んでください。

  1. MVCするのでModelを作る
  2. DAOを作る
  3. これらを組み立てる

MVCのためのModelを作る

Model,View,Cotrolを組み合わせて作っていきます

JavaBeansでMVCしていく

今回扱うテーブルは、前回の章で扱ったshohinテーブルです。主キーがshohin_idでshohin_mei,shohin_bunrui,hanbai_tanka,shiire_tanka,torokubiの項目があります。

これをやり取りするクラスを作りましょう。

作る場所としてフォルダーを作っていきます。

フォルダー階層
	|   Main.java
	|   Main.class
	|
	+---dao
	|
	\---model
			ShohinData.java

Mainの階層の下にmodelフォルダーを作り、そこにShohinData.javaでクラスを作ります。daoはこれから作っていきます。

ShohinData.java(テーブルとのやり取りのクラス)
	package model;

	import java.io.Serializable;
	import java.util.Date;
	
	public class ShohinData implements Serializable{
		private String si; 	//shohin_id
		private String sm; 	//shohin_mei
		private String sb; 	//shohin_bunrui
		private int ht;		//hanbai_tanka
		private int st;		//shiire_tanka
		private Date tb;	//torokubi
	
		public ShohinData() {}
		public ShohinData(String si, String sm, String sb, int ht, int st, Date tb) {
			this.si = si;
			this.sm = sm;
			this.sb = sb;
			this.ht = ht;
			this.st = st;
			this.tb = tb;
		}
		public String getSi() {
			return si;
		}
		public void setSi(String si) {
			this.si = si;
		}
		public String getSm() {
			return sm;
		}
		public void setSm(String sm) {
			this.sm = sm;
		}
		public String getSb() {
			return sb;
		}
		public void setSb(String sb) {
			this.sb = sb;
		}
		public int getHt() {
			return ht;
		}
		public void setHt(int ht) {
			this.ht = ht;
		}
		public int getSt() {
			return st;
		}
		public void setSt(int st) {
			this.st = st;
		}
		public Date getTb() {
			return tb;
		}
		public void setTb(Date tb) {
			this.tb = tb;
		}	
	}
	

フィールドとコンストラクタとセッターゲッターです。非常に単純です。

実は、VsCodeでセッターゲッターは簡単に作れます。フィールドを宣言したら、その下で右クリック ソースアクション->Generate getter and setter を選択するだけです。

DAOを作る

前回作ったMain.javaは、その中でデータベースへの接続、SQLの発行、内容表示、クローズをやっています。これをメインでやってしまうと、それぞれのRead,Updateがメインの中で煩雑に混じってしまうので、それをそれぞれのソースに分けて、それぞれでオープン、操作、クローズをするようにするのがDAOです。

まずはSelect文を発行している前章の内容をListDAO.javaというソースにまとめてdaoフォルダーに作成します。

今後作っていくファイルについてもまとめて紹介しておきます。

フォルダー階層(ファイル全部)
	|   Main.java
	|
	+---dao
	|       DeleteDAO.java
	|       InsertDAO.java
	|       ListDAO.java
	|       UpdateDAO.java
	|
	\---model
			DeleteLogic.java
			InsertLogic.java
			ListLogic.java
			ShohinData.java
			UpdateLogic.java

ListDAO.javaをMain配下daoフォルダーに作ります。

フォルダー階層(ファイル全部)
	package dao;

	import java.sql.Connection;
	import java.sql.DriverManager;
	import java.sql.PreparedStatement;
	import java.sql.ResultSet;
	import java.sql.SQLException;
	import java.util.Date;
	import java.util.ArrayList;
	
	import model.ShohinData;
	
	public class ListDAO {
		public static ArrayList getShohinData() throws ClassNotFoundException{
	
			Connection con = null;
			ArrayList am = new ArrayList<>();
			try {
				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
			} catch(ClassNotFoundException e) {
				throw new IllegalStateException("ドライバーのロードに失敗しました");
			}
	
			try {
				con = DriverManager.getConnection("jdbc:sqlserver://localhost\\SQLEXPRESS;databaseName=shop;IntegratedSecurity=true;TrustServerCertificate=true;");
				//step2 送信すべきSQL文のひな形を準備
				PreparedStatement pstmt  = con.prepareStatement
	//				("SELECT * FROM Shohin");
					( "Select shohin_id,shohin_mei,shohin_bunrui,hanbai_tanka,coalesce(shiire_tanka,0) as shiire,coalesce(torokubi,'1970-01-01') as toroku from shohin");
	
				//DBMSに検索系を送信
				ResultSet r = pstmt.executeQuery();
				//処理結果を見る
				   while(r.next()) {
					String si = r.getString("shohin_id");
					String sm = r.getString("shohin_mei");
					String sb = r.getString("shohin_bunrui");
					int ht = r.getInt("hanbai_tanka");
					int st = r.getInt("shiire");
					java.sql.Timestamp ts = r.getTimestamp("toroku");
					long l = ts.getTime();
					java.util.Date d = new Date(l);
					ShohinData sd = new ShohinData(si,sm,sb,ht,st,d); 
					am.add(sd);
				}
				r.close();
				pstmt.close();
				return am;
			} catch (SQLException e) {
				e.printStackTrace();
				return null;
			} finally {
				if (con != null) {
					try {
						con.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	

select文が以前と変わっています。今回使ったテーブルのデータがところどころ実験のためにNULLで抜けているので、普通に読み込むとエラーで止まってしまいます。そこでcoalesceを使って変換しています。

executeQueryで読み込んだレコードセットを.netx()で回して先ほど作成したShohinDataクラスに詰め込んでArrayListを作り、呼びもとに返しています。

同様に、Insert,Update,Deleteに関してもDAOを作成しておきましょう。

InsertDAO.java
	package dao;

	import java.sql.Connection;
	import java.sql.DriverManager;
	import java.sql.PreparedStatement;
	import java.sql.SQLException;
	import java.util.Date;
	
	import model.ShohinData;
	
	public class InsertDAO {
		public static boolean insertData(ShohinData sd ) throws ClassNotFoundException{
			Connection con = null;
			try {
				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
			} catch(ClassNotFoundException e) {
				throw new IllegalStateException("ドライバーのロードに失敗しました");
			}
	
			try {
				con = DriverManager.getConnection("jdbc:sqlserver://localhost\\SQLEXPRESS;databaseName=shop;IntegratedSecurity=true;TrustServerCertificate=true;");
				//step2 送信すべきSQL文のひな形を準備
				PreparedStatement pstmt = con.prepareStatement(
					"insert into shohin (shohin_id,shohin_mei,shohin_bunrui,hanbai_tanka,shiire_tanka,torokubi) VALUES (?,?,?,?,?,?)");
				pstmt.setString(1, sd.getSi());
				pstmt.setString(2, sd.getSm());
				pstmt.setString(3,sd.getSb());
				pstmt.setInt(4, sd.getHt());
				pstmt.setInt(5, sd.getSt());
				Date d = new Date();
				long l = d.getTime();
				java.sql.Timestamp ts = new java.sql.Timestamp(l);
				pstmt.setTimestamp(6, ts);
				int r = pstmt.executeUpdate();
				//処理結果を見る
				pstmt.close();
	
				if (r==1) {
					return true;
				} else {
					return false;
				}
			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			} finally {
				if (con != null) {
					try {
						con.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	

このように一度DAOを作っておけば、その応用でほかのDAOを作ることができます。

つづいてUpdateをやってみましょう

UpdateDAO.java
	package dao;

	import java.sql.Connection;
	import java.sql.DriverManager;
	import java.sql.PreparedStatement;
	import java.sql.SQLException;
	import java.util.Date;
	
	import model.ShohinData;
	
	public class UpdateDAO {
		public static boolean updateData(ShohinData sd ) throws ClassNotFoundException{
			Connection con = null;
			try {
				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
			} catch(ClassNotFoundException e) {
				throw new IllegalStateException("ドライバーのロードに失敗しました");
			}
	
			try {
				con = DriverManager.getConnection("jdbc:sqlserver://localhost\\SQLEXPRESS;databaseName=shop;IntegratedSecurity=true;TrustServerCertificate=true;");
				//step2 送信すべきSQL文のひな形を準備
				PreparedStatement pstmt = con.prepareStatement(
					"update shohin set shohin_mei=?,shohin_bunrui=?,hanbai_tanka=?,shiire_tanka=?,torokubi=? WHERE shohin_id=?");
				pstmt.setString(6, sd.getSi());
				pstmt.setString(1, sd.getSm());
				pstmt.setString(2,sd.getSb());
				pstmt.setInt(3, sd.getHt());
				pstmt.setInt(4, sd.getSt());
				Date d = new Date();
				long l = d.getTime();
				java.sql.Timestamp ts = new java.sql.Timestamp(l);
				pstmt.setTimestamp(5, ts);
				int r = pstmt.executeUpdate();
				//処理結果を見る
				pstmt.close();
	
				if (r==1) {
					return true;
				} else {
					return false;
				}
			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			} finally {
				if (con != null) {
					try {
						con.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	

次は、delete行います。今回は1でtrueとしてますが、1以上にしても大丈夫です。

DeleteDAO.java
	package dao;

	import java.sql.Connection;
	import java.sql.DriverManager;
	import java.sql.PreparedStatement;
	import java.sql.SQLException;
	
	public class DeleteDAO {
		public static boolean deleteData(String si ) throws ClassNotFoundException{
			Connection con = null;
			try {
				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
			} catch(ClassNotFoundException e) {
				throw new IllegalStateException("ドライバーのロードに失敗しました");
			}
	
			try {
				con = DriverManager.getConnection("jdbc:sqlserver://localhost\\SQLEXPRESS;databaseName=shop;IntegratedSecurity=true;TrustServerCertificate=true;");
				//step2 送信すべきSQL文のひな形を準備
				PreparedStatement pstmt  = con.prepareStatement
					("DELETE FROM shohin WHERE shohin_id=?");
				//ひな形に値を流し込む
				pstmt.setString(1,si);
				//DBMSに検索系を送信
				int r = pstmt.executeUpdate();
				//処理結果を見る
				pstmt.close();
	
				if (r==1) {
					return true;
				} else {
					return false;
				}
			} catch (SQLException e) {
				e.printStackTrace();
				return false;
			} finally {
				if (con != null) {
					try {
						con.close();
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	

これらを組み立てる

daoができたので、これらをつないでいきましょう

modelにLogic.javaを追加する

以前のMainからListDAOを作りましたが、するとMainにはデータベースに関することを書かなくて済むようにできるはずです。

今回はmodelにLogic.javaを作成して、そこからDAOを呼び出すようにします。そしてMainからこのLogic.javaを呼び出します。

ListLogic.java
	package model;

	import java.util.ArrayList;
	
	import dao.ListDAO;
	
	public class ListLogic {
		public ArrayList execute() {
			ArrayList sd = new ArrayList<>();
			try {
				sd = ListDAO.getShohinData();
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
			return sd;
		}
	}
	
InsertLogic.java
	package model;

	import dao.InsertDAO;
	
	public class InsertLogic {
		public boolean execute(ShohinData sd) {
			boolean st = true;
			try {
				st = InsertDAO.insertData(sd);
			} catch (Exception e) {
				System.out.println(e.getMessage());
				st = false;
			}
			return st;
		}
	}
	
UpdateLogic.java
	package model;

	import dao.UpdateDAO;
	
	public class UpdateLogic {
		public boolean execute(ShohinData sd) {
			boolean st = true;
			try {
				st = UpdateDAO.updateData(sd);
			} catch (Exception e) {
				System.out.println(e.getMessage());
				st = false;
			}
			return st;
		}
	}
	
DeleteLogic.java
	package model;

	import dao.DeleteDAO;
	
	public class DeleteLogic {
		public boolean execute(String si) {
			boolean st = true;
			try {
				st = DeleteDAO.deleteData(si);
			} catch (Exception e) {
				System.out.println(e.getMessage());
				st = false;
			}
			return st;
		}
	}
	

これで4つのLogic.javaができましたので、これをMainから呼び出します。

今回は単純のためにフォーム画面は作りません。コマンドプロンプトで実行します。

Select,Insert,Update,Deleteを選択してもらい、必要がデータの入力して、結果を表示してまたループに戻るようにします。

Main.java
	// CLASSPATH に.;c:\Program Files\Java\jdk-21\lib\mssql-jdbc-12.6.1.jre11.jarを入れる
	//jarファイルをlibに authのdllをsystem32に入れる
	//javac Main.java
	// ただしdao,model配下はjavacが届かない時があるので javac dao\ListDAO.java等でコンパイル
	//java Main
	import java.text.SimpleDateFormat;
	import java.util.Scanner;
	
	import model.DeleteLogic;
	import model.InsertLogic;
	import model.ListLogic;
	import model.UpdateLogic;
	import model.ShohinData;
	
	import java.util.ArrayList;
	import java.util.Date;
	
	public class Main {
		public static void main(String[] args) {
			Scanner scanner = new Scanner(System.in,"Shift-JIS");
			try {
				String cmd = "";
				String si = ""; //shohin id
				String sm = ""; //shohin mei
				String sb = ""; //shohin_bunrui
				int ht = 0;     //hanbai tanka
				int st = 0;     //shiire tanka
				//bunrui = '事務用品'固定 登録日:今日
				boolean loopf = true;
	
				while(loopf) {
					System.out.print("Enter command: E(end) I(insert) U(update) D(delete) S(select) :");
					switch(cmd = scanner.nextLine()) {
						case "E"-> {    //end
							loopf = false;
						}
						case "I"-> {    //insert
							System.out.print("shohin_id(00nn): ");
							si = scanner.nextLine();                   
							System.out.print("shohin_mei: ");
							sm = scanner.nextLine();
							System.out.println("bunri:キッチン用品,事務用品,衣服");
							System.out.print("shohin_bunrui: ");
							sb = scanner.nextLine();
							System.out.print("hanbai_tanka: ");
							ht = scanner.nextInt();                   
							System.out.print("shiire_tanka: ");
							st = scanner.nextInt(); 
							Date now = new Date();  //今日の日付
							long l = now.getTime();
							java.sql.Timestamp ts = new java.sql.Timestamp(l); 
							ShohinData sd = new ShohinData(si,sm,sb,ht,st,ts);
							//Insert処理
							InsertLogic insertLogic = new InsertLogic();
							boolean ir = insertLogic.execute(sd);
							if (ir==true) {
								System.out.println("Insert Scucess");
							} else {
								System.out.println("Error Occured");
							}
						}
						case "U"-> {    //update
							System.out.print("指定するshohin_id(00nn): ");
							si = scanner.nextLine();                   
							System.out.print("shohin_mei: ");
							sm = scanner.nextLine();
							System.out.println("bunri:キッチン用品,事務用品,衣服");
							System.out.print("shohin_bunrui: ");
							sb = scanner.nextLine();
							System.out.print("hanbai_tanka: ");
							ht = scanner.nextInt();                   
							System.out.print("shiire_tanka: ");
							st = scanner.nextInt(); 
							Date now = new Date();  //今日の日付
							long l = now.getTime();
							java.sql.Timestamp ts = new java.sql.Timestamp(l); 
							ShohinData sd = new ShohinData(si,sm,sb,ht,st,ts);
							//Update処理
							UpdateLogic updateLogic = new UpdateLogic();
							boolean ur = updateLogic.execute(sd);
							if (ur==true) {
								System.out.println("Insert Scucess");
							} else {
								System.out.println("Error Occured");
							}
						}
						case "D"-> {    //delete
							System.out.print("shohin_id(00nn): ");
							si = scanner.nextLine();                   
							//Delete処理
							DeleteLogic deleteLogic = new DeleteLogic();
							boolean dr = deleteLogic.execute(si);
							if (dr==true) {
								System.out.println("Delete Scucess");
							} else {
								System.out.println("Error Occured");
							}
						}
						case "S"-> {    //select
							ArrayList shohinList = new ArrayList<>();
							ListLogic logic = new ListLogic();
							shohinList = logic.execute();
							final String SFORMAT = "id:%-4s\t name:%-14s\t bunrui:%-8s\t ht:%-8d\t st:%-8d\t Date:%-10s";
							SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd");
							for( ShohinData sd : shohinList) {
								String stb = f.format(sd.getTb());
								String s = String.format(SFORMAT, sd.getSi(),sd.getSm(),sd.getSb(),sd.getHt(),
									sd.getSt(),stb);
									System.out.println(s);
							}
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				scanner.close();
			}
		}
	}
	

コメントにもあるように javac Main.javaでコンパイルしますが、パッケージのコンパイルが完全ではないようなので、それぞれコンパイルした方がよいと思います。mavenでやれば管理できると思いますが、今回はやっていません。

実行結果
	>java Main
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :S
	id:0001  name:Tシャツ            bunrui:衣服             ht:1000         st:500          Date:2009/09/20
	id:0002  name:穴あけパンチ               bunrui:事務用品         ht:500          st:320          Date:2009/09/11
	id:0003  name:カッターシャツ             bunrui:衣服             ht:4000         st:2800         Date:1970/01/01
	id:0004  name:包丁               bunrui:キッチン用品     ht:3000         st:2800         Date:2009/09/20
	id:0005  name:圧力鍋             bunrui:キッチン用品     ht:6800         st:5000         Date:2009/01/15
	id:0006  name:フォーク                   bunrui:キッチン用品     ht:500          st:0            Date:2009/09/20
	id:0007  name:おろしがね                 bunrui:キッチン用品     ht:880          st:790          Date:2008/04/28
	id:0008  name:ボールペン                 bunrui:事務用品         ht:100          st:0            Date:2009/11/11
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :I
	shohin_id(00nn): 0009
	shohin_mei: 印鑑
	bunri:キッチン用品,事務用品,衣服
	shohin_bunrui: 事務用品
	hanbai_tanka: 95
	shiire_tanka: 10
	Insert Scucess
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :Enter command: E(end) I(insert) U(update) D(delete) S(select) :U
	指定するshohin_id(00nn): 0009
	shohin_mei: 印鑑
	bunri:キッチン用品,事務用品,衣服
	shohin_bunrui: 事務用品
	hanbai_tanka: 130
	shiire_tanka: 30
	Insert Scucess
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :Enter command: E(end) I(insert) U(update) D(delete) S(select) :S
	id:0001  name:Tシャツ            bunrui:衣服             ht:1000         st:500          Date:2009/09/20
	id:0002  name:穴あけパンチ               bunrui:事務用品         ht:500          st:320          Date:2009/09/11
	id:0003  name:カッターシャツ             bunrui:衣服             ht:4000         st:2800         Date:1970/01/01
	id:0004  name:包丁               bunrui:キッチン用品     ht:3000         st:2800         Date:2009/09/20
	id:0005  name:圧力鍋             bunrui:キッチン用品     ht:6800         st:5000         Date:2009/01/15
	id:0006  name:フォーク                   bunrui:キッチン用品     ht:500          st:0            Date:2009/09/20
	id:0007  name:おろしがね                 bunrui:キッチン用品     ht:880          st:790          Date:2008/04/28
	id:0008  name:ボールペン                 bunrui:事務用品         ht:100          st:0            Date:2009/11/11
	id:0009  name:印鑑               bunrui:事務用品         ht:130          st:30           Date:2024/04/08
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :D
	shohin_id(00nn): 0009
	Delete Scucess
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :S
	id:0001  name:Tシャツ            bunrui:衣服             ht:1000         st:500          Date:2009/09/20
	id:0002  name:穴あけパンチ               bunrui:事務用品         ht:500          st:320          Date:2009/09/11
	id:0003  name:カッターシャツ             bunrui:衣服             ht:4000         st:2800         Date:1970/01/01
	id:0004  name:包丁               bunrui:キッチン用品     ht:3000         st:2800         Date:2009/09/20
	id:0005  name:圧力鍋             bunrui:キッチン用品     ht:6800         st:5000         Date:2009/01/15
	id:0006  name:フォーク                   bunrui:キッチン用品     ht:500          st:0            Date:2009/09/20
	id:0007  name:おろしがね                 bunrui:キッチン用品     ht:880          st:790          Date:2008/04/28
	id:0008  name:ボールペン                 bunrui:事務用品         ht:100          st:0            Date:2009/11/11
	Enter command: E(end) I(insert) U(update) D(delete) S(select) :E

コマンドプロンプトからの入力にしていますので、Scannerから Shift-JISで入力しています。これをしないと文字化けします。

さて、これでCRUDができました。教科書のサンプルはPostgreSQLですので、それと合わせて参考にしてください。