JDBC處理事務
一、什麽是事務?
在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!
二、事務是必須滿足4個條件(ACID)
- 事務的原子性( Atomicity):一組事務,要麽成功;要麽撤回。
- 一致性 (Consistency):事務執行後,數據庫狀態與其他業務規則保持一致。如轉賬業務,無論事務執行成功否,參與轉賬的兩個賬號余額之和應該是不變的。
- 隔離性(Isolation):事務獨立運行。一個事務處理後的結果,影響了其他事務,那麽其他事務會撤回。事務的100%隔離,需要犧牲速度。
- 持久性(Durability):
三、MySQL中的事務
在默認情況下,MySQL每執行一條SQL語句,都是一個單獨的事務。如果需要在一個事務中包含多條SQL語句,那麽需要開啟事務和結束事務。
- 開啟事務:start transaction
- 結束事務:commit或rollback
在執行SQL語句之前,先執行start transaction,這就開啟了一個事務(事務的起點),然後可以去執行多條SQL語句,最後要結束事務,commit表示提交,即事務中的多條SQL語句所作出的影響會持久到數據庫中,或者rollback,表示回滾到事務的起點,之前做的所有操作都被撤銷了。
1 mysql> SELECT * FROM account;
2 +----+------+---------+
3 | id | NAME | balance |
4 +----+------+---------+
5 | 1 | zs | 1000.00 |
6 | 2 | ls | 1000.00 |
7 | 3 | ww | 1000.00 |
8 +----+------+---------+
9 3 rows in set (0.00 sec)
10
11 mysql> START TRANSACTION;
12 Query OK, 0 rows affected (0.00 sec)
13
14 mysql> UPDATE account SET balance=900 WHERE name = ‘zs‘;
15 Query OK, 1 row affected (0.00 sec)
16 Rows matched: 1 Changed: 1 Warnings: 0
17
18 mysql> SELECT * FROM account;
19 +----+------+---------+
20 | id | NAME | balance |
21 +----+------+---------+
22 | 1 | zs | 900.00 |
23 | 2 | ls | 1000.00 |
24 | 3 | ww | 1000.00 |
25 +----+------+---------+
26 3 rows in set (0.00 sec)
27
28 mysql> UPDATE account SET balance=1100 WHERE name = ‘ls‘;
29 Query OK, 1 row affected (0.00 sec)
30 Rows matched: 1 Changed: 1 Warnings: 0
31
32 mysql> SELECT * FROM account;
33 +----+------+---------+
34 | id | NAME | balance |
35 +----+------+---------+
36 | 1 | zs | 900.00 |
37 | 2 | ls | 1100.00 |
38 | 3 | ww | 1000.00 |
39 +----+------+---------+
40 3 rows in set (0.00 sec)
41
42 mysql> ROLLBACK;
43 Query OK, 0 rows affected (0.00 sec)
44
45 mysql> SELECT * FROM account;
46 +----+------+---------+
47 | id | NAME | balance |
48 +----+------+---------+
49 | 1 | zs | 1000.00 |
50 | 2 | ls | 1000.00 |
51 | 3 | ww | 1000.00 |
52 +----+------+---------+
53 3 rows in set (0.00 sec)
54
55 mysql> START TRANSACTION;
56 Query OK, 0 rows affected (0.00 sec)
57
58 mysql> UPDATE account SET balance=balance-100 WHERE name = ‘zs‘;
59 Query OK, 1 row affected (0.00 sec)
60 Rows matched: 1 Changed: 1 Warnings: 0
61
62 mysql> SELECT * FROM account;
63 +----+------+---------+
64 | id | NAME | balance |
65 +----+------+---------+
66 | 1 | zs | 900.00 |
67 | 2 | ls | 1000.00 |
68 | 3 | ww | 1000.00 |
69 +----+------+---------+
70 3 rows in set (0.00 sec)
71
72 mysql> UPDATE account SET balance=balance+100 WHERE name = ‘ls‘;
73 Query OK, 1 row affected (0.00 sec)
74 Rows matched: 1 Changed: 1 Warnings: 0
75
76 mysql> SELECT * FROM account;
77 +----+------+---------+
78 | id | NAME | balance |
79 +----+------+---------+
80 | 1 | zs | 900.00 |
81 | 2 | ls | 1100.00 |
82 | 3 | ww | 1000.00 |
83 +----+------+---------+
84 3 rows in set (0.00 sec)
85
86 mysql> commit;
87 Query OK, 0 rows affected (0.02 sec)
88
89 mysql> SELECT * FROM account;
90 +----+------+---------+
91 | id | NAME | balance |
92 +----+------+---------+
93 | 1 | zs | 900.00 |
94 | 2 | ls | 1100.00 |
95 | 3 | ww | 1000.00 |
96 +----+------+---------+
97 3 rows in set (0.00 sec)
四、JDBC事務
在JDBC中處理事務,都是通過Connection完成的。
同一事務中所有的操作,都在使用同一個Connection對象。
①JDBC中的事務
Connection的三個方法與事務有關:
- setAutoCommit(boolean):設置是否為自動提交事務,如果true(默認值為true)表示自動提交,也就是每條執行的SQL語句都是一個單獨的事務,如果設置為false,那麽相當於開啟了事務了;con.setAutoCommit(false) 表示開啟事務。
- commit():提交結束事務。
- rollback():回滾結束事務。
JDBC處理事務的代碼格式:
try{
con.setAutoCommit(false);//開啟事務
......
con.commit();//try的最後提交事務
} catch() {
con.rollback();//回滾事務
}
示例:
1 public class AccountDao {
2 /*
3 * 修改指定用戶的余額
4 * */
5 public void updateBalance(Connection con, String name,double balance) {
6 try {
7 String sql = "UPDATE account SET balance=balance+? WHERE name=?";
8 PreparedStatement pstmt = con.prepareStatement(sql);
9 pstmt.setDouble(1,balance);
10 pstmt.setString(2,name);
11 pstmt.executeUpdate();
12 }catch (Exception e) {
13 throw new RuntimeException(e);
14 }
15 }
16 }
1 import cn.itcast.jdbc.JdbcUtils;
2 import org.junit.Test;
3 import java.sql.Connection;
4 import java.sql.SQLException;
5
6 public class Demo1 {
7 /*
8 * 演示轉賬方法
9 * 所有對Connect的操作都在Service層進行的處理
10 * 把所有connection的操作隱藏起來,這需要使用自定義的小工具(day19_1)
11 * */
12 public void transferAccounts(String from,String to,double money) {
13 //對事務的操作
14 Connection con = null;
15 try{
16 con = JdbcUtils.getConnection();
17 con.setAutoCommit(false);
18 AccountDao dao = new AccountDao();
19 dao.updateBalance(con,from,-money);//給from減去相應金額
20 if (true){
21 throw new RuntimeException("不好意思,轉賬失敗");
22 }
23 dao.updateBalance(con,to,+money);//給to加上相應金額
24 //提交事務
25 con.commit();
26
27 } catch (Exception e) {
28 try {
29 con.rollback();
30 } catch (SQLException e1) {
31 e.printStackTrace();
32 }
33 throw new RuntimeException(e);
34 }
35 }
36 @Test
37 public void fun1() {
38 transferAccounts("zs","ls",100);
39 }
40 }
五、事務隔離級別
1、事務的並發讀問題
- 臟讀:讀取到另外一個事務未提交數據(不允許出來的事);
- 不可重復讀:兩次讀取不一致;
- 幻讀(虛讀):讀到另一事務已提交數據。
2、並發事務問題
因為並發事務導致的問題大致有5類,其中兩類是更新問題三類是讀問題。
- 臟讀(dirty read):讀到另一個事務的未提交新數據,即讀取到了臟數據;
- 不可重復讀(unrepeatable):對同一記錄的兩次讀取不一致,因為另一事務對該記錄做了修改;
- 幻讀(虛讀)(phantom read):對同一張表的兩次查詢不一致,因為另一事務插入了一條記錄。
3、四大隔離級別
4個等級的事務隔離級別,在相同的數據環境下,使用相同的輸入,執行相同的工作,根據不同的隔離級別,可以導致不同的結果。不同事務隔離級別能夠解決的數據並發問題的能力是不同的。
1、SERIALIZABLE(串行化)
- 不會出現任何並發問題,因為它是對同一數據的訪問是串行的,非並發訪問的;
- 性能最差
2、REPEATABLE READ(可重復讀)(MySQL)
- 防止臟讀和不可重復讀,不能處理幻讀
- 性能比SERIALIZABLE好
3、READ COMMITTED(讀已提交數據)(Oracle)
- 防止臟讀,不能處理不可重復讀和幻讀;
- 性能比REPEATABLE READ好
4、READ UNCOMMITTED(讀未提交數據)
- 可能出現任何事物並發問題,什麽都不處理。
- 性能最好
六、MySQL隔離級別
MySQL的默認隔離級別為Repeatable read,可以通過下面語句查看:
SELECT @@`TX_ISOLATION`;
也可以通過下面語句來設置當前連接的隔離級別:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ;//[4選1]
七、JDBC設置隔離級別
con.setTransactionIsolation(int level) :參數可選值如下:
- Connection.TRANSACTION_READ_UNCOMMITTED;
- Connection.TRANSACTION_READ_COMMITTED;
- Connection.TRANSACTION_REPEATABLE_READ;
- Connection.TRANSACTION_READ_SERIALIZABLE。
JDBC處理事務