1. 程式人生 > >JDBC處理事務

JDBC處理事務

防止 run enc innodb inno .... 不變 用戶 boolean

一、什麽是事務?

  在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!

二、事務是必須滿足4個條件(ACID)

  • 事務的原子性( Atomicity):一組事務,要麽成功;要麽撤回。
  • 一致性 (Consistency):事務執行後,數據庫狀態與其他業務規則保持一致。如轉賬業務,無論事務執行成功否,參與轉賬的兩個賬號余額之和應該是不變的。
  • 隔離性(Isolation):事務獨立運行。一個事務處理後的結果,影響了其他事務,那麽其他事務會撤回。事務的100%隔離,需要犧牲速度。
  • 持久性(Durability):
    軟、硬件崩潰後,InnoDB數據表驅動會利用日誌文件重構修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 選項 決定什麽時候吧事務保存到日誌裏。

三、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處理事務