---
# System prepended metadata

title: JDBC 小吳 20210503
tags: [JDBC]

---

# 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>

- 交易問題與可預防隔離行為關係 (補充)：
  
  ![](https://i.imgur.com/VEhvUTi.png)

  <br>

  1. TRANSCATION_NONE : 
     - 可解決 ==Lost Update(更新遺失)==   

  2. READ_UNCOMMITTED : 
     - 可解決 ==Dirty Read(髒讀)== 
     - 其他交易等到第一個交易解決後才能讀取或寫入

  3. REPEATABLE_READ :
     - 可解決 ==Unrepeatable read(無法重複讀取)== 
  
  4. SERIALIZABLE : 
     - 可解決 ==Phantom Read(幻讀)== 
     - Table前後兩者的資料筆數不一致
     
<br>

---

# 模組18 - 自增主鍵值綁定

## ==自增主鍵值綁定目的==
## 自增主鍵值綁定

- 關聯式資料庫裡對於表格之間關係，建議會是一對多或是多對一的關係維護，像多對多的情況，以商品跟訂單為例，我們在表格設計上，就會採取以下的做法：

  ![](https://i.imgur.com/5hRPVJq.png)


<br>

---

## 多人環境與主鍵值取得問題

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

  ![](https://i.imgur.com/dB4QjmU.png)

<br>
<br>

- 編號在設計上常見使用流水號，也就是SEQUENCE或是AUTO INCREMENT，如ORD001、ORD002、ORD003…依此類推，但若是在多人環境，該如何拿到屬於該訂單的正確編號並新增至訂單明細表格呢？
  
  ![](https://i.imgur.com/jnM6xCP.png)

<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>

   ![](https://i.imgur.com/I18TyNC.png)



- 連線管理程式
  
  範例： ==ConnPool.java== + ==SQLAgent.java== + ==TestThread.java==


<br>

---

## 連線池管理

![](https://i.imgur.com/93Sl67e.png)



<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將文字資料送入資料庫

