Java中事務的特性有四種,原子性、一致性、隔離性、持久性mysql
原子性:若是執行一條sql,底層是默認執行事務的,叫作隱形事務,當執行多條sql語句的時候,多條sql語句不能夠進行分割,必須所有一次性執行完成,就是要麼所有完成,要麼失敗;sql
一致性:事務完成後,數據狀態保持一致性,能量守恆定律,哈哈哈;數據庫
隔離性:多個事務併發訪問數據庫,事務對數據的修改,須要與其餘事務進行隔離;安全
持久性:數據修改完成,數據持久化存儲;併發
使用JDBC,須要先關閉主動提交事務,而後執行多條SQL語句,而後提交,若是報錯進行回滾事務,在最後啓動自動提交,由於執行1條sql語句的時候,也會開啓事務,叫作隱形事務,默認進行提交,若是關閉了不啓動,之後執行一條SQL也須要顯示顯示事務了;性能
Connection conn = openConnection(); try { // 關閉自動提交: conn.setAutoCommit(false); // 執行多條SQL語句: insert(); update(); delete(); // 提交事務: conn.commit(); } catch (SQLException e) { // 回滾事務: conn.rollback(); } finally { conn.setAutoCommit(true); conn.close(); }
Mysql隔離級別分爲4種:Read Uncommitted(讀取未提交的)、Read Committed(讀取提交的)、Repeatable Red(可重複讀)、Serializaable(串行化)spa
Isolation Level | 髒讀(Dirty Read) | 不可重複讀(Non Repeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
Read Uncommitted | Yes | Yes | Yes |
Read Committed | - | Yes | Yes |
Repeatable Read | - | - | Yes |
Serializable | - | - | - |
Read Uncommitted是隔離級別最低的一種事務級別。在這種隔離級別下,一個事務會讀到另外一個事務更新後但未提交的數據,若是另外一個事務回滾,那麼當前事務讀到的數據就是髒數據,這就是髒讀(Dirty Read)。code
mysql> select * from students; +----+-------+ | id | name | +----+-------+ | 1 | Alice | +----+-------+ 1 row in set (0.00 sec)
而後,分別開啓兩個MySQL客戶端鏈接,按順序依次執行事務A和事務B:事務
當事務A執行完第3步時,它更新了id=1
的記錄,但並未提交,而事務B在第4步讀取到的數據就是未提交的數據。ci
隨後,事務A在第5步進行了回滾,事務B再次讀取id=1
的記錄,發現和上一次讀取到的數據不一致,這就是髒讀。
可見,在Read Uncommitted隔離級別下,一個事務可能讀取到另外一個事務更新但未提交的數據,這個數據有多是髒數據。
時刻 | 事務A | 事務B |
---|---|---|
1 | SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; | SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; |
2 | BEGIN; | BEGIN; |
3 | UPDATE students SET name = 'Bob' WHERE id = 1; | |
4 | SELECT * FROM students WHERE id = 1; | |
5 | ROLLBACK; | |
6 | SELECT * FROM students WHERE id = 1; | |
7 | COMMIT; |
在Read Committed隔離級別下,一個事務可能會遇到不可重複讀(Non Repeatable Read)的問題。
不可重複讀是指,在一個事務內,屢次讀同一數據,在這個事務尚未結束時,若是另外一個事務剛好修改了這個數據,那麼,在第一個事務中,兩次讀取的數據就可能不一致。
咱們仍然先準備好students
表的數據:
mysql> select * from students; +----+-------+ | id | name | +----+-------+ | 1 | Alice | +----+-------+ 1 row in set (0.00 sec)
而後,分別開啓兩個MySQL客戶端鏈接,按順序依次執行事務A和事務B:
當事務B第一次執行第3步的查詢時,獲得的結果是Alice
,隨後,因爲事務A在第4步更新了這條記錄並提交,因此,事務B在第6步再次執行一樣的查詢時,獲得的結果就變成了Bob
,所以,在Read Committed隔離級別下,事務不可重複讀同一條記錄,由於極可能讀到的結果不一致。
時刻 | 事務A | 事務B |
---|---|---|
1 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED; | SET TRANSACTION ISOLATION LEVEL READ COMMITTED; |
2 | BEGIN; | BEGIN; |
3 | SELECT * FROM students WHERE id = 1; | |
4 | UPDATE students SET name = 'Bob' WHERE id = 1; | |
5 | COMMIT; | |
6 | SELECT * FROM students WHERE id = 1; | |
7 | COMMIT; |
在Repeatable Read隔離級別下,一個事務可能會遇到幻讀(Phantom Read)的問題。
幻讀是指,在一個事務中,第一次查詢某條記錄,發現沒有,可是,當試圖更新這條不存在的記錄時,居然能成功,而且,再次讀取同一條記錄,它就神奇地出現了。
咱們仍然先準備好students
表的數據:
mysql> select * from students; +----+-------+ | id | name | +----+-------+ | 1 | Alice | +----+-------+ 1 row in set (0.00 sec)
而後,分別開啓兩個MySQL客戶端鏈接,按順序依次執行事務A和事務B:
事務B在第3步第一次讀取id=99
的記錄時,讀到的記錄爲空,說明不存在id=99
的記錄。隨後,事務A在第4步插入了一條id=99
的記錄並提交。事務B在第6步再次讀取id=99
的記錄時,讀到的記錄仍然爲空,可是,事務B在第7步試圖更新這條不存在的記錄時,居然成功了,而且,事務B在第8步再次讀取id=99
的記錄時,記錄出現了。
可見,幻讀就是沒有讀到的記錄,覺得不存在,但實際上是能夠更新成功的,而且,更新成功後,再次讀取,就出現了。
時刻 | 事務A | 事務B |
---|---|---|
1 | SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; | SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; |
2 | BEGIN; | BEGIN; |
3 | SELECT * FROM students WHERE id = 99; | |
4 | INSERT INTO students (id, name) VALUES (99, 'Bob'); | |
5 | COMMIT; | |
6 | SELECT * FROM students WHERE id = 99; | |
7 | UPDATE students SET name = 'Alice' WHERE id = 99; | |
8 | SELECT * FROM students WHERE id = 99; | |
9 | COMMIT; |
Serializable是最嚴格的隔離級別。在Serializable隔離級別下,全部事務按照次序依次執行,所以,髒讀、不可重複讀、幻讀都不會出現。
雖然Serializable隔離級別下的事務具備最高的安全性,可是,因爲事務是串行執行,因此效率會大大降低,應用程序的性能會急劇下降。若是沒有特別重要的情景,通常都不會使用Serializable隔離級別。
默認隔離級別
若是沒有指定隔離級別,數據庫就會使用默認的隔離級別。在MySQL中,若是使用InnoDB,默認的隔離級別是Repeatable Read。
啓動事務
在啓動類上添加註解 @EnableTransactionManagement ,
在執行事務的方法上面使用 @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)設置隔離界別與事務傳播。默認就是REQUIRED;
事務傳播
Spring的聲明式事務爲事務傳播定義了幾個級別,默認傳播級別就是REQUIRED,它的意思是,若是當前沒有事務,就建立一個新事務,若是當前有事務,就加入到當前事務中執行。
SUPPORTS
:表示若是有事務,就加入到當前事務,若是沒有,那也不開啓事務執行。這種傳播級別可用於查詢方法,由於SELECT語句既能夠在事務內執行,也能夠不須要事務;
MANDATORY
:表示必需要存在當前事務並加入執行,不然將拋出異常。這種傳播級別可用於核心更新邏輯,好比用戶餘額變動,它老是被其餘事務方法調用,不能直接由非事務方法調用;
REQUIRES_NEW
:表示無論當前有沒有事務,都必須開啓一個新的事務執行。若是當前已經有事務,那麼當前事務會掛起,等新事務完成後,再恢復執行;
NOT_SUPPORTED
:表示不支持事務,若是當前有事務,那麼當前事務會掛起,等這個方法執行完成後,再恢復執行;
NEVER
:和NOT_SUPPORTED
相比,它不但不支持事務,並且在監測到當前有事務時,會拋出異常拒絕執行;
NESTED
:表示若是當前有事務,則開啓一個嵌套級別事務,若是當前沒有事務,則開啓一個新事務。