一、事務(wù)的四個(gè)特性(ACID)
成都創(chuàng)新互聯(lián)公司公司2013年成立,先為昌平等服務(wù)建站,昌平等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為昌平企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
原子性(Atomicity):
事務(wù)是一個(gè)原子操作,由一系列動(dòng)作組成。事務(wù)的原子性確保動(dòng)作要么全部完成,要么完全不起作用。
一致性(Consistency):
一旦事務(wù)完成(不管成功還是失?。?,系統(tǒng)必須確保它所建模的業(yè)務(wù)處于一致的狀態(tài),而不會(huì)是部分完成部分失敗。在現(xiàn)實(shí)中的數(shù)據(jù)不應(yīng)該被破壞。
隔離性(Isolation):
可能有許多事務(wù)會(huì)同時(shí)處理相同的數(shù)據(jù),因此每個(gè)事務(wù)都應(yīng)該與其他事務(wù)隔離開來,防止數(shù)據(jù)損壞。
持久性(Durability):
一旦事務(wù)完成,無論發(fā)生什么系統(tǒng)錯(cuò)誤,它的結(jié)果都不應(yīng)該受到影響,這樣就能從任何系統(tǒng)崩潰中恢復(fù)過來。通常情況下,事務(wù)的結(jié)果被寫到持久化存儲(chǔ)器中。
二、傳播行為
當(dāng)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),必須指定事務(wù)應(yīng)該如何傳播。
例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行。
Spring 定義了七種傳播行為。
2.1、PROPAGATION_REQUIRED
表示當(dāng)前方法必須運(yùn)行在事務(wù)中。如果當(dāng)前事務(wù)存在,方法將會(huì)在該事務(wù)中運(yùn)行。否則,會(huì)啟動(dòng)一個(gè)新的事務(wù), Spring 默認(rèn)使用
2.2、PROPAGATION_SUPPORTS
表示當(dāng)前方法不需要事務(wù)上下文,但是如果存在當(dāng)前事務(wù)的話,那么該方法會(huì)在這個(gè)事務(wù)中運(yùn)行
2.3、PROPAGATION_MANDATORY
表示該方法必須在事務(wù)中運(yùn)行,如果當(dāng)前事務(wù)不存在,則會(huì)拋出一個(gè)異常
2.4、PROPAGATION_REQUIRED_NEW
表示當(dāng)前方法必須運(yùn)行在它自己的事務(wù)中。一個(gè)新的事務(wù)將被啟動(dòng)。如果存在當(dāng)前事務(wù),在該方法執(zhí)行期間,當(dāng)前事務(wù)會(huì)被掛起。如果使用 JTATransactionManager 的話,則需要訪問TransactionManager
2.5、PROPAGATION_NOT_SUPPORTED
表示該方法不應(yīng)該運(yùn)行在事務(wù)中。如果存在當(dāng)前事務(wù),在該方法運(yùn)行期間,當(dāng)前事務(wù)將被掛起。如果使用 JTATransactionManager 的話,則需要訪問 TransactionManager
2.6、PROPAGATION_NEVER
表示當(dāng)前方法不應(yīng)該運(yùn)行在事務(wù)上下文中。如果當(dāng)前正有一個(gè)事務(wù)在運(yùn)行,則會(huì)拋出異常
2.7、PROPAGATION_NESTED
表示如果當(dāng)前已經(jīng)存在一個(gè)事務(wù),那么該方法將會(huì)在嵌套事務(wù)中運(yùn)行。嵌套的事務(wù)可以獨(dú)立于當(dāng)前事務(wù)進(jìn)行單獨(dú)地提交或回滾。如果當(dāng)前事務(wù)不存在,那么其行為與PROPAGATION_REQUIRED 一樣。注意各廠商對(duì)這種傳播行為的支持是有所差異的。可以參考資源管理器的文檔來確認(rèn)它們是否支持嵌套事務(wù)
三、隔離級(jí)別
隔離級(jí)別定義了一個(gè)事務(wù)可能受其他并發(fā)事務(wù)影響的程度。
3.1、ISOLATION_DEFAULT
使用后端數(shù)據(jù)庫默認(rèn)的隔離級(jí)別,Spring默認(rèn)使用,MySQL默認(rèn)的隔離級(jí)別為:Repeatable Read( ( 可重復(fù)讀) )
3.2、ISOLATION_READ_UNCOMMITTED
讀未提交,最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀
3.3、ISOLATION_READ_COMMITTED
讀已提交,允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生
3.4、ISOLATION_REPEATABLE_READ
可重復(fù)讀,對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生
3.5、ISOLATION_SERIALIZABLE
可串行化,最高的隔離級(jí)別,完全服從 ACID 的隔離級(jí)別,確保阻止臟讀、不可重復(fù)讀以及幻讀,也是最慢的事務(wù)隔離級(jí)別,因?yàn)樗ǔJ峭ㄟ^完全鎖定事務(wù)相關(guān)的數(shù)據(jù)庫表來實(shí)現(xiàn)的
臟讀(Dirty reads):
臟讀發(fā)生在一個(gè)事務(wù)讀取了另一個(gè)事務(wù)改寫但尚未提交的數(shù)據(jù)時(shí)。如果改寫再稍后被回滾了,那么第一個(gè)事務(wù)獲取的數(shù)據(jù)就是無效的。
不可重復(fù)讀(Nonrepeatable read):
不可重復(fù)讀發(fā)生在一個(gè)事務(wù)執(zhí)行相同的查詢兩次或兩次以上,但是每次都得到不同的數(shù)據(jù)時(shí)。這通常是因?yàn)榱硪粋€(gè)并發(fā)事務(wù)在兩次查詢期間進(jìn)行了更新。
幻讀(Phantom read):
幻讀與不可重復(fù)讀類似。它發(fā)生在一個(gè)事務(wù)(T1)讀取了幾行數(shù)據(jù),接著另一個(gè)并發(fā)事務(wù)(T2)插入了一些數(shù)據(jù)時(shí)。在隨后的查詢中,第一個(gè)事務(wù)(T1)就會(huì)發(fā)現(xiàn)多了一些原本不存在的記錄。
四、操作
屬性說明 @Transactional
a、isolation:用于指定事務(wù)的隔離級(jí)別。默認(rèn)為底層事務(wù)的隔離級(jí)別。
b、noRollbackFor:指定遇到指定異常時(shí)強(qiáng)制不回滾事務(wù)。
c、noRollbackForClassName:指定遇到指定多個(gè)異常時(shí)強(qiáng)制不回滾事務(wù)。該屬性可以指定多個(gè)異常類名。
d、propagation:指定事務(wù)的傳播屬性。
e、readOnly:指定事務(wù)是否只讀。表示這個(gè)事務(wù)只讀取數(shù)據(jù)但不更新數(shù)據(jù),這樣可以幫助數(shù)據(jù)庫引擎優(yōu)化事務(wù)。若真的是一個(gè)只讀取的數(shù)據(jù)庫應(yīng)設(shè)置 readOnly=true
f、rollbackFor:指定遇到指定異常時(shí)強(qiáng)制回滾事務(wù)。
g、rollbackForClassName:指定遇到指定多個(gè)異常時(shí)強(qiáng)制回滾事務(wù)。該屬性可以指定多個(gè)異常類名。
h、timeout:指定事務(wù)的超時(shí)時(shí)長。
注 意:
mysql為例,存儲(chǔ)引擎不能使用MyISAM,應(yīng)該使用InnoDB
package com.example.demo.service; import com.example.demo.mapper.UserLogMapper; import com.example.demo.mapper.UserMapper; import com.example.demo.pojo.User; import com.example.demo.pojo.UserLog; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; /** * 描述 * * @Author: 我愛大金子 * @Description: 描述 * @Date: Create in 10:18 2017/6/22 */ @Service public class UserService { @Autowired private UserMapper userMapper; @Autowired private UserLogMapper userLogMapper; /** * 用戶注冊(cè) * * @return */ @Transactional public String register(String name, String ip) { // 1.添加用戶 User user = new User(); user.setName(name); user.setCreateTime(new Date()); userMapper.insert(user); // 測(cè)試使用 boolean flag = true; if (flag) { throw new RuntimeException(); } // 2.添加注冊(cè)日志 UserLog userLog = new UserLog(); userLog.setUserName(name); userLog.setUserIp(ip); userLog.setCreateTime(new Date()); userLogMapper.insert(userLog); return "success"; } }
測(cè)試:
package com.example.demo; import com.example.demo.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Date; @RunWith(SpringRunner.class) @SpringBootTest public class Springboot3ApplicationTests { @Autowired private UserService userService; @Test public void register() { String result = userService.register("張三", "192.168.1.1"); System.out.println(result); } }
效果: