# JDBC 小吳 20210503
###### tags: `JDBC`
# 模組16 - 交易(Transaction)
## ==交易要求原則==
## 交易ACID
- 交易的四個基本要求,也常簡稱為==ACID==:
1. ==原子性(Atomicity)==
2. ==一致性(Consistency)==
3. ==隔離行為(Isolation behavior)==
4. ==持續性(Durability)==
- 一個交易即為一個單元工作(Unit of work),裡面所有包含的步驟需全部執行成功,若有一個步驟失敗,則視為此交易失敗,需撤消先前所有執行成功的動作,回到初始狀態
- 交易作用的資料集合在交易前後必須一致,若交易成功,則整個資料集合都必須是交易後狀態,若交易失敗,整個資料集合都必須為開始交易前的狀態
- 多人使用環境下,每個使用者都能進行自己的交易,交易與交易之間彼此不相關,互不干擾
- 交易一旦成功,所有變更都必須保存下來,即使系統出了問題,交易結果仍不會遺失或改變
<br>
---
## ==JDBC交易處理==
## JDBC與Transaction
- 為維護資料庫的整合性,將一組SQL指令組成一個交易,
1. 若所有敘述++運作正常++,則==提交(commit)資料庫一次處理==
2. 若其中有++錯誤發生++,則==回復(rollback)交易前的狀態==
<br>
<br>
- ++Connection物件預設是++ ==自動提交(auto-commit)模式==,即以個別的交易為單位自動commit資料庫,完成資料庫更新
<br>
<br>
- Connection介面有關交易的三大方法:
1. ==setAutoCommit(boolean autoCommit)==:設定自動提交模式 ==(傳入false代表手動commit)==
2. ==commit( )==:結束目前的交易並將所有暫存的資料永久變更至資料庫中
3. ==rollback( )==:結束目前的交易並將所有暫存的資料遺棄
-> 一般會在交易被中斷時呼叫,即發生SQLException時呼叫
<br>
---
# 模組17 - 進階交易
## ==儲存點與交易相關==
## 其它交易相關事項
- BatchUpdate若是不必每一筆SQL指令都要確認的話,也可以搭配交易管理進行操作,使用方式同上頁說明
- 如果在交易管理時,僅想要撒回某個SQL執行點,可以利用設定儲存點(Save Point)的方式進行設計
- 上述交易相關功能,資料庫表格++必須有支援交易為前提++才能使用。例如在MySQL裡建立為InnoDB類型的表格
- ==進行交易時,除了使用同個連線物件完成之外,連線物件不可與其他使用者共用!== -> ++為了符合隔離行為要求++
參考訂單與訂單明細實作範例:TestTranscationWithOrder.java
<br>
---
## ==隔離行為層級==
## 隔離行為
- Connection物件可使用下列方法進行隔離行為等級的取得或設定:
1. `int getTransactionIsolation()`:取得目前的 transaction-isolation-level 值
2. ==Connection介面定義了5個常數:==
- `int TRANSACTION_NONE`
- `int TRANSACTION_READ_COMMITTED`
- `int TRANSACTION_READ_UNCOMMITTED`
- `int TRANSCATION_REPEATABLE_READ`
- `int TRANSCATION_SERIALIZABLE`
<br>
3. void setTransactionIsolation(int level)
- 此方法不得於執行交易區間內呼叫
- 使用此方法務必小心,勿亂設定!
<br>
---
## ==隔離行為與資料庫同步==
## 隔離行為與同步
- TRANSACTION_NONE僅適用==沒有交易功能或唯讀功能==的資料庫表格;若是表格是有支援交易功能,則會無視此設定
> [備註]
若是想查詢++資料庫是否支援某隔離行為++可利用
DatabaseMetaData的方法:
`boolean supportsTransactionIsolationLevel(int level)`
<br>
- 交易問題與可預防隔離行為關係 (補充):

<br>
1. TRANSCATION_NONE :
- 可解決 ==Lost Update(更新遺失)==
2. READ_UNCOMMITTED :
- 可解決 ==Dirty Read(髒讀)==
- 其他交易等到第一個交易解決後才能讀取或寫入
3. REPEATABLE_READ :
- 可解決 ==Unrepeatable read(無法重複讀取)==
4. SERIALIZABLE :
- 可解決 ==Phantom Read(幻讀)==
- Table前後兩者的資料筆數不一致
<br>
---
# 模組18 - 自增主鍵值綁定
## ==自增主鍵值綁定目的==
## 自增主鍵值綁定
- 關聯式資料庫裡對於表格之間關係,建議會是一對多或是多對一的關係維護,像多對多的情況,以商品跟訂單為例,我們在表格設計上,就會採取以下的做法:

<br>
---
## 多人環境與主鍵值取得問題
- 編號在設計上常見使用流水號,也就是 ==SEQUENCE==(Oracle用語) 或是 ==AUTO INCREMENT==(MySQL用語),如ORD001、ORD002、ORD003…依此類推,但若是在多人環境,該如何拿到屬於該訂單的正確編號並新增至訂單明細表格呢?

<br>
<br>
- 編號在設計上常見使用流水號,也就是SEQUENCE或是AUTO INCREMENT,如ORD001、ORD002、ORD003…依此類推,但若是在多人環境,該如何拿到屬於該訂單的正確編號並新增至訂單明細表格呢?

<br>
---
## ==Statement相關綁定==
## Statement綁定主鍵值
- JDBC 3.0規範裡,當新增資料時,允許++資料庫自動產生的主鍵值++綁定到 ==Statement== 或 ==PreparedStatement== 中
- 使用方式 ==(Statement)==:
1. `int executeUpdate(String sql, int autoGeneratedKeys)`
2. `int executeUpdate(String sql, int[] columnIndexes)`
3. `int executeUpdate(String sql, String[] colunmNames)`
<br>
- 使用方式 ==(PreparedStatement)==:
1. `PreparedStatement preparedStatement(String sql, int autoGeneratedKeys)`
2. `PreparedStatement preparedStatement (String sql, int[] columnIndexes)`
3. `PreparedStatement preparedStatement (String sql, String[] colunmNames)`
<br>
- 以 Statement 的 ++ResultSet++ ==getGeneratedKeys( )== 方法++可取出綁定的資料庫自增主鍵值++
<br>
---
# 模組21 - 連線管理
## ==連線管理與連線池==
## 連線管理議題
- 對於一個Web應用系統來說,連線有可能產生以下問題:
1. 資料庫++連結過於頻繁++,造成整個系統++執行效率低落++
2. 佔用連線後,卻又很少執行程式造成浪費 (如`servlet init()`方法使用)
3. 使用一個連線後,執行程式頻率極高,可能造成多個執行緒同時共用一個連線的不正常情況 (資料異動時不能共用同一個連線)
<br>
- ==連線池 (Connection Pool)==
1. 是一種對Connection物件的管理機制,為解決上述問題的方案之一
2. 系統啟動初期,Connection Pool就會預先建立幾個連線物件,並開始執掌分配連線物件的工作
<br>
---
## 連線池套件
- Connection Pool實作
1. 可由 Application Server(如Tomcat, WebSphere, WebLogic)提供
2. 驅動程式內也可以處理 Connection Pool
==(不是首選,因為會有相依性過高的問題,換驅動程式就得跟著改程式碼)==
3. 或是處理 Connection Pool 的商業產品套件
4. 免費的連線池:Apache DBCP, C3P0(ORM框架,Hibernate內附), Proxool 等
<br>

- 連線管理程式
範例: ==ConnPool.java== + ==SQLAgent.java== + ==TestThread.java==
<br>
---
## 連線池管理

<br>
---
# 模組22 - 連線池(Connection Pool)
## ==DataSource介面==
- 使用`javax.sql.DataSource`(資料來源介面)
- JDBC 2.0標準延伸API(Standard Extension API)裡引入的DataSource介面是目前連上資料庫的DriverManager類別的最佳替代方案
- ```
public interface DataSource {
Connection getConnection() throws SQLException;
…
}
```
#### 備註
getConnection()由連線池套件實作
<br>
- ==一個DataSource物件就代表一個資料庫,應用程式可以間接透過這個物件以取得資料庫Connection==
- 要配置DataSource物件來產生連線池,需先有ConnectionPoolDataSource介面的物件
<br>
---
## ==JNDI簡介==
- 實際上對於DataSource的取得,我們的應用程式更常藉由JNDI(Java Naming and Directory Interface),從Servlet Container取得已設定好的DataSource,再從DataSource取得資料庫連線物件
#### 備註
對資源先取好名字,在程式碼裡面就用名字來取得對應的資源
- 範例:
```
javax.naming.Context ctx = new javax.naming.InitialContext();
DataSource ds = (DataSource) ctx.lookup(“jdbc/XXX”);
Connection con = ds.getConnection();
```
<br>
<br>
- Java程式內無須再註冊驅動程式,也不必透過DriverManager提供URL, USER & PASSWORD,JDBC已經發現了命名與目錄服務
<br>
---
## ==連線池運作與優勢==
## 連線池優勢
- ==Java應用程式只要向DataSource交談即可==,DataSource會自行和ConnectionPoolDataSource交談
<br>
- ConnectionPoolDataSource儲存的就是放在連線池裡的資料庫連線。當應用程式呼叫DataSource實體的`getConnection()`方法時,`getConnection()`++方法即會從連線池裡取出一個連線物件++,應用程式就能利用此物件一直到工作完成
<br>
- 工作完成之後,應用程式正常關閉連線,但應用程式並不會知道實際上此連線與資料庫沒有中斷。`close()`++方法將連線歸還到連線池裡++,若是要再使用連線,需要再次取出
##### 備註
連線歸還: 照常close,不過連線池套件會對close()覆寫,所以會變成歸還連線的動作
<br>
<br>
- ==採用連線池的幾個好處==:
1. 開發快速
2. 好維護
3. 系統效能提升
4. 解決交易連線共用問題
<br>
---
# 模組23 - JDBC各版本比較
## ==JDBC各版本比較==
## JDBC各版本主要變更1
- JDBC 1.0 vs JDBC 2.0
1. ResultSet可以指定移動游標筆數
2. 可以使用Java指令更新資料,替代SQL指令,也可一次下達多個SQL指令
3. 支援SQL3的資料型態
- JDBC 3.0 (J2SE1.4 / 1.5)
1. 當新增資料時,允許將資料庫自動產生的主鍵值進行綁定到Statement或是PreparedStatement(參考先前範例),再透過ResultSet的getGeneratedKeys()方法取得綁定的主鍵值
2. 與各種Java應用程式結合,讓開發快速與應用程式效能都有所提昇
3. 規範與規定了下述類別與介面支援資料庫的連線池實作
```
javax.sql.ConnectionEvent
javax.sql.ConnectionPoolDataSource
javax.sql.PooledConnection
java.sql.ConnectionEventListener
```
<br>
---
## JDBC各版本主要變更2
- JDBC 4.0 (J2SE 6)
1. ==簡化資料庫驅動載入==
- JDBC 4.0後,可以不用再呼叫Class.forName方法載入驅動但在包裝JDBC驅動程式的JAR檔裡,必須有一個「META-INF/services/java.sql.Driver」的檔案,並在該檔案裡註明驅動程式的類別名稱
備註: 有版本支援跟廠商是否有提供來決定能否簡化
<br>
2. ==SQLException功能增強==
- SQLException新增了幾個以Throwable為建構子參數的建構子,就可以將任何其它類型的例外也包裝成SQLException再做後續處理。SQLException也同時實作了`Iterable<Throwable>`介面,可以搭配`for-each`取出
<br>
3. ==BLOB與CLOB的支援==
- PreparedStatement新增了setBlob()與setClob()方法,使用InputStream將位元資料送入資料庫使用Reader將文字資料送入資料庫