一、什么是事務?
在人員管理系統中,你刪除一個人員,你即需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!
二、事務是必須滿足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。