Mysql事務開啓方式(客戶端+java手動+Spring Boot)

一:概念

      做爲單個邏輯單元執行一系列操做,要麼徹底執行,要麼徹底不執行。舉例 咱們須要向數據庫插入3條數據(咱們但願這三條數據要麼所有插入成功,要麼所有失敗), 好比第一條數據插入成功,插入第二條數據失敗(顯然這已經不是一個完整的業務數據),那麼第三條數據也無需執行。那麼咱們就能夠用到事務了。java

二:事務的特性和隔離級別

      爲了不在事務期間發生衝突,DBMS使用鎖定機制來阻止其餘人訪問事務正在訪問的數據。(請注意,在自動提交模式下,每一個語句都是一個事務,只保留一個語句的鎖定。)設置鎖定後,它將一直有效,直到提交或回滾事務爲止。例如,DBMS能夠鎖定表的一行,直到對其進行更新爲止。此鎖定的做用是防止用戶獲取髒讀,即在永久化以前讀取值。(訪問還沒有提交的更新值被視爲髒讀由於該值能夠回滾到其先前的值。若是您讀取稍後回滾的值,則會讀取無效值。)mysql

隔離級別 事務 髒讀 不可重複讀 幻讀
TRANSACTION_NONE 不支持 不適用 不適用 不適用
TRANSACTION_SERIALIZABLE 支持 防止 防止 防止
TRANSACTION_READ_COMMITTED(默認 支持 防止 容許 容許
TRANSACTION_REPEATABLE_READ 支持 防止 防止 容許
TRANSACTION_READ_UNCOMMITTED 支持 容許 容許 容許

 

 

 

 

 

髒讀   :   讀取了另外一個事務沒有提交的數據,默認隔離級別,即防止了別人讀取到咱們沒有提交(commit)的數據git

重複讀:對一個開啓了事務鏈接,在第一次查詢一行數據(這次另外一個開啓的事務更新了這一條數據),與第二次查詢的數據不同。即兩次查詢同一條數據不同github

幻讀   :對一個開啓了事務鏈接,第一次查詢的數據行數(這次另外一個開啓的事務的鏈接新增了一條),與第二次查詢的行數不同(好像產生了幻覺)。即兩次查詢的數量不同spring

備註:我的理解髒讀,幻讀,重複讀能夠上咱們看到數據的整個變化過程,而不是隻注重結果,故我認爲這並不算是一種bug。打個比方說我種了了一畝地的西瓜,在我準備次日收穫以前我先去數了一下共有200個大西瓜。等到次日我去收穫的時候我只收穫了198個,還有兩個去哪裏了呢?我猜想多是被那個口渴的路人偷吃了吧,我不會糾結這兩個西瓜到哪裏去了。固然這也要根據業務場景分析了,若是說你第一天數的200個西瓜並興高采烈的告訴了你的老婆大人(多線程查詢同一條數據),然而收穫回去只有198個,那你就要和她解釋了。若是她是個通情達理的人這就不是bug,若是不是你就把它認爲bug吧。sql

備註1:客戶端設置隔離級別數據庫

mysql>  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; Query OK, 0 rows affected (0.00 sec)

備註2: Java設置隔離級別session

Connection.getTransactionIsolation//獲取當前隔離級別
Connection.setTransactionIsolation)//設置當前隔離級別

注意:一次事務只能設置一次隔離即使多線程

mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress

三:客戶端操做方法

  備註:默認數據庫事務是關閉的,即執行更新(修改)表的語句,MySQL就會將更新存儲在磁盤上以使其永久化。沒法回滾更改。框架

                  咱們能夠經過執行  SET autocommit=0; 命令設置事務開啓狀態,即每條更新語句都須要手動commit提交事務(只對當前session有效,即其餘客戶端更新操做是沒有事務)

  • START TRANSACTION或 BEGIN開始新的交易。

  • COMMIT 提交當前事務,使其更改永久化。

  • ROLLBACK 回滾當前事務,取消其更改。

  • SET autocommit 禁用或啓用當前會話的默認自動提交模式。

四:java手動控制

 即:手動控制事務的開啓和關閉

public class RawTransactions { private final JdbcTemplate jdbcTemplate; public RawTransactions(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } public void book(String... persons) { //開啓事務
        jdbcTemplate.execute("START TRANSACTION"); for (String person : persons) { try { jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person); } catch (RuntimeException e) { System.out.println("----發生異常數據回滾 -----"); jdbcTemplate.execute("ROLLBACK"); break; } } //提交事務
        jdbcTemplate.execute("COMMIT"); } public List<String> findAllBookings() { return jdbcTemplate.query("select FIRST_NAME from BOOKINGS", (rs, rowNum) -> rs.getString("FIRST_NAME")); } }

項目地址:https://github.com/374003909/JdbcTransactions/blob/master/src/main/java/hello/RawTransactions.java

五:spring boot開啓事務

即:在spring boot框架中經過註解@Transactional 實現

@Component public class BookingService { private final static Logger logger = LoggerFactory.getLogger(BookingService.class); private final JdbcTemplate jdbcTemplate; public BookingService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Transactional public void book(String... persons) { for (String person : persons) { logger.info("Booking " + person + " in a seat..."); jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person); } } public List<String> findAllBookings() { return jdbcTemplate.query("select FIRST_NAME from BOOKINGS", (rs, rowNum) -> rs.getString("FIRST_NAME")); } }

項目地址:https://github.com/374003909/JdbcTransactions/blob/master/src/main/java/hello/BookingService.java

測試入口:https://github.com/374003909/JdbcTransactions/blob/master/src/main/java/hello/AppRunner.java

項目地址:https://github.com/374003909/JdbcTransactions

相關文章
相關標籤/搜索