2018年6月7日星期四

Eclipse 上使用 Java DB ( Apache Derby ) (PART 3 / 3 )

上一回【Eclipse 上使用 Java DB ( Apache Derby ) (PART 2 / 3 )

首先要「做得到」,然後才想「做得好」,這個範例止於「做得到」範疇,想深入鑽研如何「做得好」,文章最後會有額外提示。

Derby_Prepare_1

廢話到此為止,開始之前,輸入資料,然後備份。你都懂的,不會的話,請回去前幾 PART 溫習。


———————————簡易的分隔線————————————

運行設定及預備圖片︰


Derby_Prepare_2

【Run】 --> 【Run Configurations】

Derby_Prepare_3

確認 Project 及 Main class 是否選對。

Derby_Prepare_4

有【Embedded Mode】及【Client / Server Mode】吧? 複製一份。

Derby_Prepare_5

改名及打上 Arguments,然後 Apply,這是【Embedded Mode】設定。

Derby_Prepare_6

重覆以上動作,修改設定 ( 還記得 DERBY_OPTS 嗎?這是告訴 Derby 放 Database 的位置)。

Derby_Prepare_7

好了,現在應該有一個 Embedded 及 Server 版設定,沒甚麼,只是 Argument 不同,方便測試。console output encoding,一定要設好,不然 console 會出亂碼。

Derby_Prepare_10

修改程式碼或跟圖中路徑存放檔案,之後 write BLOB 範例中會用到。

———————————簡易的分隔線————————————

運行 Client / Server Mode︰


連接方法視乎 Argument 輸入而定,Embedded 之前 SQL 教學時用過,這次就用 Client / Server Driver。

Derby_Prepare_8

首先開啟 Derby Network Server,要用合適權限執行,否則會失敗,會有開不到 derby.log 之類的 error message 出現。

Derby_Prepare_9

這是成功開啟 Derby Network Server 畫面,以供參考。

Derby_Prepare_11

在【Run Configurations】執行過一次後,可以在 ToolsBar 按下箭頭選 Profile 執行程式。

———————————簡易的分隔線————————————

程式碼解說︰


JDBC Driver︰


public ConnectDerby(String url) {
dbURL = url;
Properties properties = new Properties();
properties.put("create", "true");
properties.put("user", "Stanley");
properties.put("password", "javatoybox");
try {
con = DriverManager.getConnection(dbURL, properties);
} catch (SQLException e) {
e.printStackTrace();
}
}
final String ServerMode = "jdbc:derby://localhost:1527/FirstDB";
final String EmbeddedMode = "jdbc:derby:A:\\javatoybox\\Database\\FirstDB";
ConnectDerby derbyDB;
if (args[0].equalsIgnoreCase(ServerMode))
derbyDB = new ConnectDerby(ServerMode);
else
derbyDB = new ConnectDerby(EmbeddedMode);
view raw FirstDerby.java hosted with ❤ by GitHub
不需要在【Data Source Explorer】連線資料庫,因為直接以【SQL URL】連接,之前【Run Configurations】設定好。

相反,在【Data Source Explorer】連接資料庫,會令程式連不到資料庫,出現 Exception。所以﹐執行程式前,記謹斷開所有資料庫連線

———————————簡易的分隔線————————————

執行次序︰


不完善的範例,不跟順序執行會發生錯誤,反正是展示用,略過 error checking、exception handling、logic 等等。

// Show menu and allow input
Scanner input = new Scanner(System.in);
boolean exit = false;
do {
System.out.println(" \n Please select operation number ( 1-5 ) :\n"
+ "1. read data \n"
+ "2. delete \n"
+ "3. insert BLOB \n"
+ "4. update BLOB \n"
+ "5. extract BLOB\n"
+ "Press any else key to exit program\n");
switch (input.nextInt()) {
case 1:
derbyDB.read("SELECT id, actor, product FROM acg_character.profile");
break;
case 2:
derbyDB.delete("DELETE FROM acg_character.profile WHERE id = ?", 3);
break;
case 3:
derbyDB.writeBLOB("INSERT INTO acg_character.profile (media_1_filename,media_1,product) VALUES (?,?,\'ご注文はうさぎですか?\')",
"A:\\javatoybox\\test_data\\test\\insert.jpg");
break;
case 4:
derbyDB.writeBLOB("UPDATE acg_character.profile SET media_1_filename = ?, media_1 = ? WHERE id = 1",
"A:\\javatoybox\\test_data\\test\\insert.jpg");
break;
case 5:
derbyDB.extractBLOB("SELECT media_1_filename, media_1 FROM acg_character.profile WHERE id = 1",
"media_1_filename");
break;
default:
derbyDB.shudown();
input.close();
exit = true;
break;
} // end case
} while (!exit);// end do-while loop
雖然寫【Press any else key to exit program】,輸入不是數字會有 exception。

———————————簡易的分隔線————————————

執行過程及結果︰



時間軸 註解
0:00 預備測試用圖檔 ( insert.jpg )
0:25 檢查 Run Configurations 設定
1:29 執行 Derby Network Server
2:09 執行程式

時間軸 Select case 註解
2:27 case 1 read 讀取資料,檢視資料狀態
2:58 case 3 write 寫入 BLOB ( insert.jpg ) 同時寫入其他資訊 ( 檔案名稱連副檔名, etc )
3:09 case 1 read 再次讀取資料,觀看寫入結果
3:24 case 2 delete 刪除指定資料
3:35 case 1 read 檢視刪除後結果
3:51 case 4 update BLOB 更新 BLOB Type 資料
4:07 case 5 Extract BLOB 滙出 BLOB 資料

———————————簡易的分隔線————————————

Connection、Statement / PreparedStatement、ResultSet 及 ResultSetMetaData 關係︰


Connection︰


以【Connection URL】設定 JDBC Driver,用這範例來說,用 Properties 把 User Name 及 Password 連接 指定的 Database。連接成功後,要做甚麼?執行 SQL 指令……怎做?這時候需要用 Connection 建立 Statement / PreparedStatement。

Connecting to databases within the system
https://db.apache.org/derby/docs/10.14/devguide/cdevdvlp846369.html

Statement / PreparedStatement︰


Statement 這個很容易理解,建立好之後,用 execute() 或 executeQuery() 執行 SQL。

用 Statement 可以做到,為甚麼還有 PreparedStatement?因為不知道實際 value 是甚麼,本範例來說,不會知道檔案名稱,所以不能把 SQL 寫死。

用 SQL 建立 PreparedStatement 時,以【?】作為變數,跟據輸入做點手腳,最後設定變數把 SQL 完成。另外,PreparedStatement 也是 Object,重用 Object 比每次建立新 Object 效能會比較好。

Prepared statements and streaming columns
https://db.apache.org/derby/docs/10.14/ref/rrefjavsqlprst.html

ResultSet︰


例如 INSERT 只有成功與否,但用 SELECT 呢?會回傳結果,那個結果用 ResultSet 接住。statement.executeQuery() 回傳 ResultSet,得到 ResultSet 後可以做甚麼?看 API 文件。

Interface ResultSet
https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

ResultSetMetaData︰


得到 ResultSet 後,怎知道入面的 Data Types?為甚麼要知道 Data Types?

讀取 ResultSet,需要用 getXXX 之類 method 撈資料,不知道資料 Data Types 就不知道用甚麼 method 取得資料。知道 Data Types 方法有兩個,一是記得資訊結構,例如 table schema,另一個方法是用 ResultSetMetaData。

Supported Data Types
https://docs.oracle.com/cd/E19501-01/819-3659/gcmaz/

Interface ResultSetMetaData
https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSetMetaData.html

———————————簡易的分隔線————————————

Best practice tips︰


ASAP release resource ( connection, resultset, etc )︰


網絡不穩定,不知道何時會斷線時,程式又不懂重新連線,有機會發生 Null pointer Exception。

不需要頻繁讀寫 Database,而長期佔用連線也是個問題,database 有連線上限數,不斷建立連線,又不釋放資源,會導致程式及資料庫崩潰。

所以最好完成一連串動作後,要把連線斷開,通知 JVM 做 Garbage Collection ( GC ) 釋放資源,有需要時才重新連線。

result_set.close();
prepare_statment.close();
connection.close();

GC 不到你控制,JVM 何時真正釋放資源,你不會知道,就算程式碼順序寫 close ResultSet 到 PrepareStatment 最後到 Connection,也不知道 GC 是不是順序釋放資源。這個動作只為了通知 JVM 可以回收 Object,免得資源被鎖死。

Use connection pool︰


經常斷線連線,重用 Object 是個好選擇,有機會把 DriverManager.getConnection() 連線方式,改造成

Connection con = datasource.getConnection( username, password );

Derby_DataSource.png

datasource 有很多選擇,跟據需要選擇合適 Datasource,有些 datasource 可以配合 JNDI 及 J2EE Container ( Tomcat 之類 ) 一起使用。

Apache Derby 10.14 API Documentation
https://db.apache.org/derby/docs/10.14/publishedapi/index.html

Getting a DataSource
https://db.apache.org/derby/docs/10.14/devguide/cdevresman89722.html

Derby Network Client
https://db.apache.org/derby/papers/DerbyClientSpec.html#-N100AC

ClientConnectionPoolDataSource
https://db.apache.org/derby/docs/10.14/publishedapi/org/apache/derby/jdbc/ClientConnectionPoolDataSource.html

Data Access Object ( DAO )︰


簡單來說,做 ORM ( Object Relational Mapping )。把所有 SQL 包裝成 DAO 尾的 Class,把 SQL 細節偽裝成為普通的 getter / setter method 使用,實際怎做,看參考。

當要轉換 Database 時 (例如 Derby 轉去 Mysql ),把所有 DAO 尾的 Class 檢視及修改 ( search 檔案含 DAO 字眼,很簡單找齊 ),理論上可以無痛轉換。除非用了database specific 的 SQL 語法。

Object-relational mapping - Wikipedia
https://en.wikipedia.org/wiki/Object-relational_mapping

Data Access Object
http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/dao.html

SQL dialect 及 EJB-QL︰


都是避免 vendor lock-in 的技術,沒有深入研究,只是轉換 database 成本比想像中還要高,所以一開始,最好考慮一下將來會不會因為轉不到 vendor,被人開天殺價。


———————————簡易的分隔線————————————

 總結︰


學會 database 後,然後是怎應用 database,例如︰

  • 輸入資料︰制作 GUI 連接 Database ( 網頁連接資料庫、手機 Apps 連接資料庫、桌面應用程式連接資料庫等等 )

  • 輸出資料︰JasperReport 連接 Database 制作 Report