JDBC学习笔记——事务、存储过程以及批量处理
2019獨角獸企業重金招聘Python工程師標準>>>
1、事務 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
1.1、事務的基本概念和使用示例
? ? ??數據庫事務,是指作為單個邏輯工作單元執行的一系列操作,要么完整地執行,要么完全地不執行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。通過將一組相關操作組合為一個要么全部成功要么全部失敗的單元,可以簡化錯誤恢復并使應用程序更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。
? ? ? JDBC可以操作Connection的setAutoCommit()方法,給它false參數,提示數據庫啟動事務,在下達一連串的SQL命令后,自行調用Connection的commit()方法,提示數據庫確認(Commit)操作。如果中間發生錯誤,則調用rollback(),提示數據庫撤銷(ROLLBACK)所有執行。同時,如果僅想要撤回某個SQL執行點,則可以設置存儲點(SAVEPOINT)。一個示范的事務流程如下:
Connection conn = ...; Savepoint point = null; try {conn.setAutoCommit(false);Statement stmt = conn.createStatement();stmt.executeUpdate("INSERT INTO ...");...point = conn.setSavepoint();stmt.executeUpdate("INSERT INTO ...");...conn.commit(); } catch (SQLException e) {e.printStackTrace();if (conn != null) {try {if (point == null) {conn.rollback();} else {conn.rollback(point);conn.releaseSavepoint(point);}} catch (SQLException ex) {ex.printStackTrace();}} } finally {...if (conn != null) {try {conn.setAutoCommit(true);conn.close();} catch (SQLException ex) {ex.printStackTrace();}} }? ? ? 需要說明的是,JDBC操作事務的前提條件是數據庫支持事務,如果數據庫本身不支持事務,那么我們即使調用setAutoCommit(false)也無法啟動事務。對于MYSQL來說,MyIsam數據庫引擎不支持事務操作,InnoDB數據庫引擎支持事務操作。
1.2、隔離級別
? ? ? 要理解隔離級別,首先要了解多個事務并行時,可能引發的數據不一致問題有哪些,常見的事務并行引發問題有以下幾類:
更新遺失
? ? ? 更新遺失是指某個事務對字段進行更新的信息,因另一個事務的介入而遺失更新的效力,一個簡單的示例如下:
? ? ? 這個序列就是典型的更新丟失,因為第三步所做的所有修改全部會丟失。如果要避免更新遺失問題,可以設置隔離級別為"read uncommitted",這樣A事務已更新但未確認的數據,B事務僅可做讀取操作,但不可做更新操作。這樣上面的四個步驟就是不合法的,必須A事務完全提交,B事務才能做更新操作。
臟讀
? ? ??"read uncommitted"隔離級別保證了在A事務提交之前,B事務不能做更改操作,但是沒有阻止B事務做讀取操作,但是這個其實是有問題的:如果A事務更新字段為"AAA",B事務讀取值為"AAA"并使用,然后A事務回滾事務,那么B事務讀取的"AAA"就屬于臟數據。如果要避免臟讀問題,可以設置隔離級別為"Read Commited",也就是事務讀取的數據必須是其他事務已經確認的數據。
不可重復讀
? ? ? 不可重復度是指兩次讀取同一字段的數據不一致,例如:事務A讀取字段為"AAA",事務B更新數據為"BBB",事務B提交,事務A讀取字段為"BBB",事務A連續的兩次讀取,字段值不一樣。要避免這種問題,可以設置數據庫隔離級別為"Repeatable Read",對于這種隔離界別,事務A讀取字段為"AAA"后,其他事務在事務A提交前只可讀取該字段,不可更新該字段。
幻讀
? ? ??幻讀是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,比如這種修改涉及到表中的"全部數據行"。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入"一行新數據"。那么,以后就會發生操作第一個事務的用戶發現表中還是有沒有修改的數據行,就好象發生了幻覺一樣。要解決幻讀問題,必須設置隔離級別為Serializable,Serializable是數據庫隔離級別的最高級別,串行化讀,事務只能一個一個執行,避免了臟讀、不可重復讀、幻讀,但是執行效率慢,需要謹慎使用。
? ? ? 可以使用以下命令查詢mysql的隔離級別:
?
select @@global.tx_isolation,@@tx_isolation;?
1.3、悲觀鎖、樂觀鎖
? ? ? 悲觀鎖是采用一種悲觀的態度來對待事務并發問題,認為系統中的并發更新會非常頻繁,并且事務失敗了以后重來的開銷很大,這樣一來,我們就需要采用真正意義上的鎖來進行實現。悲觀鎖的實現,往往依靠數據庫提供的鎖機制。悲觀鎖的基本思想就是每次一個事務讀取某一條記錄后,就會把這條記錄鎖住,這樣其它的事務要想更新,必須等以前的事務提交或者回滾解除鎖。
? ? ???樂觀鎖,顧名思義就是保持一種樂觀的態度,我們認為系統中的事務并發更新不會很頻繁,即使沖突了也沒事,大不了重新再來一次。它的基本思想就是每次提交一個事務更新時,我們先看看要修改的東西從上次讀取以后有沒有被其它事務修改過,如果修改過,那么更新就會失敗。樂觀鎖的實現方式大多是基于數據版本 ( Version )記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。 讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提 交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。
? ? ? 總的來說,悲觀鎖的機制依賴數據庫的鎖機制,較安全,而樂觀鎖機制通過應用程序控制,性能較好。
1.4、分布式事務
?
? ? ? 分布式事務處理涉及多個分布在不同地方的數據庫,但對數據庫的操作必須全部被提交或者回滾。只要任一數據庫操作時失敗,所有參與事務的數據庫都需要回滾。
? ? ? Open 組織定義的分布式事務處理模型X/Open DTP 模型包括應用程序( AP )、事務管理器( TM )、資源管理器( RM ,即數據庫 )、通信資源管理器( CRM )四部分。而 XA 是 X/Open DTP 定義的事務管理器與數據庫之間的接口規范(即接口函數),事務管理器用它來通知數據庫事務的開始、結束以及提交、回滾等。
? ? ? XA 接口規范使用兩階段提交協議來完成一個全局事務,保證同一事務中所有數據庫同時成功或者回滾。
? ? ? 兩階段提交協議假設每個數據庫點都存在一個write-ahead log,每一次的write請求都是先記log后才真正執行寫入。
第一階段為提交請求階段(Commit-request phase):
? ? ? 1. 事務管理器給所有數據庫發query to commit消息請求,然后開始等待回應;
? ? ? 2. 數據庫如果可以提交屬于自己的事務分支,則將自己在該事務分支中所做的操作固定記錄下來(在undo log和redo log中各記一項);
? ? ? 3. 數據庫都回應是否同意提交的應答。
第二階段為提交階段(Commit phase):
? ? 如果事務管理器收到的所有回應都是agreement,
? ? ? 1. 事務管理器記日志并給所有數據庫發commit消息請求;
? ? ? 2. 各個數據庫執行操作,釋放所有該事務相關的鎖和資源;
? ? ? 3. 各個數據庫給事務管理器回復;
? ? ? 4.當收到所有回復,事務管理器結束當前事務
?
? ? 如果事務管理器收到的任一回應是abort,
? ? ? 1. 事務管理器記日志并給所有數據庫發rollback消息請求;
? ? ? 2. 各個數據庫執行undo操作,釋放所有該事務相關的鎖和資源;
? ? ? 3. 各個數據庫給事務管理器回復;
? ? ? 4.當收到所有回復,事務管理器結束當前事務
?
? ? ? 兩階段提交協議的問題在于數據庫在提交請求階段應答后對很多資源處于鎖定狀態,要等到事務管理器收集齊所有數據庫的應答后,才能發commit或者rollback消息結束這種鎖定。鎖定時間的長度是由最慢的一個數據庫制約,如果數據庫一直沒有應答,所有其他庫也需要無休止的鎖并等待。并且,如果事務管理器出現故障,被鎖定的資源將長時間處于鎖定狀態。無論是任一數據庫或者事務管理器故障,其他數據庫都需要永久鎖定或者至少長時間鎖定。并且,分布式系統中節點越多,存在緩慢網絡或者故障節點的概率也就越大,資源被長時間鎖定的概率指數上升。
? ? ? 兩階段提交協議的另一個問題是只要有任意一個數據庫不可用都會導致事務失敗,這導致事務更傾向于失敗。對于多個副本的備份系統,很多時候我們希望部分副本點失效時系統仍然可用,使用該協議則不能實現。并且,分布式系統中節點越多,存在故障節點的概率也就越大,系統的可用性指數下降。
另外,如果數據庫在第一階段應答后到第二階段正式提交前的某個階段網絡故障或者節點故障,該協議無法提交或回滾,數據不一致不能絕對避免。
?
2、存儲過程 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? JDBC可以調用存儲過程,要調用存儲過程,首先我們應該創建存儲過程:
//創建表,并插入數據 create table g(num int,value varchar(10)); insert into g values(1, '1'),(10, '10'),(60, '60'),(100, '100');//創建存儲過程 DELIMITER $ CREATE PROCEDURE p1(IN n int, OUT avg double, OUT min int) BEGINselect avg(num) from g where num > n INTO avg;select min(num) from g where num > n INTO min; END$ DELIMITER ;JDBC調用存儲過程的接口與增刪改查的不同,JDBC調用存儲過程應該使用CallableStatement,簡單示例如下:
private static void ps() throws SQLException{Connection conn = null;CallableStatement cs = null;try{conn = JdbcUtils.getConnection();cs = conn.prepareCall("call p1(?,?,?)");cs.registerOutParameter(2, Types.DOUBLE);//設置out參數cs.registerOutParameter(3, Types.INTEGER);//設置out參數cs.setInt(1, 18);//設置in參數cs.executeUpdate();System.out.println(cs.getInt(2) + " " + cs.getInt(3));} finally{JdbcUtils.free(null, cs, conn);} }3、批次更新 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? 如果需要對對數據庫進行大量數據更新,使用循環多次操作更新是比較浪費性能的,對于這種場景,我們可以使用addBatch()方法來收集SQL,并使用executeBatch()方法將收集的SQL批次更新,例如:
Statement stmt = conn.createStatement(); while(someCondition) {stmt.addBatch("INSERT INTO ..."); } stmt.executeBatch();轉載于:https://my.oschina.net/u/197668/blog/361260
總結
以上是生活随笔為你收集整理的JDBC学习笔记——事务、存储过程以及批量处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dp之二维背包poj1837(天平问题
- 下一篇: uiimagepickerviewcon