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