數據庫事務、Spring開始事務、數據庫鏈接池

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:表示若是當前有事務,則開啓一個嵌套級別事務,若是當前沒有事務,則開啓一個新事務。

相關文章
相關標籤/搜索