java事务处理
有個同學給我的文檔,直接發上來了,
在數據庫操作中,一項事務是指由一條或多條對數據庫更新的sql語句所組成的一個不可分割的工作單元。只有當事務中的所有操作都正常完成了,整個事務才能被提交到數據庫,如果有一項操作沒有完成,就必須撤消整個事務。?
例如在銀行的轉帳事務中,假定張三從自己的帳號上把1000元轉到李四的帳號上,相關的sql語句如下:?
update account set monery=monery-1000 where name='zhangsan'?
update account set monery=monery+1000 where name='lisi'?
這個兩條語句必須作為一個完成的事務來處理。只有當兩條都成功執行了,才能提交這個事務。如果有一句失敗,整個事務必須撤消。?
在connection類中提供了3個控制事務的方法:?
(1) setAutoCommit(Boolean autoCommit):設置是否自動提交事務;?
(2) commit();提交事務;?
(3) rollback();撤消事務;?
在jdbc api中,默認的情況為自動提交事務,也就是說,每一條對數據庫的更新的sql語句代表一項事務,操作成功后,系統自動調用commit()來提交,否則將調用rollback()來撤消事務。?
在jdbc api中,可以通過調用setAutoCommit(false) 來禁止自動提交事務。然后就可以把多條更新數據庫的sql語句做為一個事務,在所有操作完成之后,調用commit()來進行整體提交。倘若其中一項 sql操作失敗,就不會執行commit()方法,而是產生相應的sqlexception,此時就可以捕獲異常代碼塊中調用rollback()方法撤消事務。
? 示例:
? 有這樣一張名叫test_user的表:uid為1的money為0元,uid為2的money為300元,uid為3的money為800元。現在uid為3的人向uid為1的人轉賬,每次50元。當uid為3的人的賬戶中的money少于或等于700的元的時候,則停止轉賬。也就是說,最后的uid為1的人賬戶為100元,uid為3的賬戶700元。
事務操作的基本流程是:當達到某個條件時拋出一個異常,在這個異常處理中回滾操作。事務最大的一個特點是,要么全部執行,要么不執行。但是,對于使用JAVA來操作數據庫事務來說,并沒用我們想的那么簡單:下面這段代碼故意將某個字段寫錯,來檢驗java是如何操作數據庫的事務的:
public class TransactionTest { public static void main(String[] args) throws Exception { Connection conn = ConnectionFactory.getInstance().getLocalConnection(); //換成自己的連接代碼int money=0; ResultSet rt=null; try { conn.setAutoCommit(false); //修改提交方式,關閉自動提交模式Statement st =conn.createStatement(); String addMoneySql = "update test_user set money=money+50 where uid=1;"; int flag0= st.executeUpdate(addMoneySql); System.out.println(flag0); String reduceMoneySql = "update test_user set money=money-50 where uid=3"; int flag1 =st.executeUpdate(reduceMoneySql); System.out.println(flag1); String queryMoneySql="select money from test_user where uid=3"; rt=st.executeQuery(queryMoneySql); if(rt.next()){ money=rt.getInt("mone");//這里故意將字段打錯,檢驗java如何執行事務的,原字段為money System.out.println(money); } if(money<=700){ throw new RuntimeException("3號的錢已經不夠了,不要匯款了");//拋出一個異常,在異常處理中回滾操作 } conn.commit(); } catch (Exception e) { e.printStackTrace();//打印出上面拋出來的異常,否則是不會顯示的。 if(conn!=null){ conn.rollback(); } } } }
執行這段代碼。發現結果為:
?
1
1
java.sql.SQLException: Column 'mone' not found.
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910)
at com.mysql.jdbc.ResultSet.findColumn(ResultSet.java:987)
at com.mysql.jdbc.ResultSet.getInt(ResultSet.java:2749)
at TransactionTest.main(TransactionTest.java:26)
在異常發生后,catch住異常,執行了回滾操作,查看數據庫,數據庫數據并沒有發生變化。但是用update執行的結果來看,update是執行了的。這就說明java處理數據庫的事務的執行策略:先挨句執行(下一句的基本數據是上一句執行完畢的數據,而不是數據庫真實的數據),當出現回滾時候,直接通知數據庫回滾。數據庫回滾以后并不會通知java,所以就會出現打印執行結果與數據庫數據不一致的情況。
那么,這一段代碼中money(假設字段已經修改正常)的值會是多少呢?
首先 uid 1 加50,uid 3 減50,成功執行,打印出750
然后 uid 1加 50 uid 3減 50,此時 uid 3中的money為 700(發現異常,回滾)結果數據庫中的uid還是750,而打印出來的值卻是 700.
再執行。發現依舊會拋出3號錢已經不夠的異常。
當然,我們會思考,我們重新執行這段代碼的話,java是不會從數據庫中間取得數據,而是直接使用臟數據。而且關閉連接等資源,關閉eclispe都不起作用,只有重新啟動數據庫,才能使得java從數據庫讀數據。
當然了,將事務按照合理的方式編排就不會出現這種錯誤。比如,將select語句放在uid 3之前,就不會出錯了。 。(其實對于事務操作,依舊讀的是臟數據,只不過這個臟數據與實際數據一致罷了。)
總結
- 上一篇: vc给exe更改图标
- 下一篇: 几款浏览器JavaScript调试工具