Spring事務(wù)失效場景原理及解決方案

1.事務(wù)失效-自身調(diào)用(通過REQUIRES梭伐、REQUIRES_NEW傳播屬性):自身調(diào)用即調(diào)該類自己的方法稻艰。
同類OrderServiceImpl 中 doSomeThing()方法 不存在事務(wù)呀酸,該方法去調(diào)用本類中的存在事務(wù)注解的 insertAndUpdateOrderInfo() 方法。但是insertAndUpdateOrderInfo() 其實是無法保證預(yù)想的事務(wù)性膜赃。
示列驗證:

OrderServiceImpl.insertAndUpdateOrderInfo方法中upateData(updateParam) 發(fā)生異常時,insertData(insertParam) 未發(fā)生回滾
說明:自身調(diào)用時候蝠嘉,無論是以下哪種傳播屬性均是無效的,因為自身調(diào)用時的子方法壓根就不會被AOP 代理攔截到以下的這兩種方式均經(jīng)過驗證杯巨,無法保證子方法事務(wù)的有效性

@Transactional(propagation = Propagation.REQUIRES)
@Transactional(propagation = Propagation.REQUIRES_NEW)

@Controller
@RequestMapping("/trans")
public class TransactionalController {

  @Autowired
  OrderService orderService;

  @RequestMapping("/test.do")
  @ResponseBody
  public void getIndex(HttpServletRequest request, HttpServletResponse response, Model model) {

    orderService.doSomeThing();

  }

}

@Service
public interface OrderService {

  /*
  *添加訂單和修改其他訂單信息
  * */
  public void doSomeThing();

}



@Service
public class OrderServiceImpl implements OrderService {
  @Autowired
  TransBusiness transBusiness;

  @Override
  public void doSomeThing() {

    insertAndUpdateOrderInfo();

  }

  @Transactional(propagation = Propagation.REQUIRED)
  public void insertAndUpdateOrderInfo(){
    Date date = new Date();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String updateTime = dateFormat.format(date);

    //步驟1:插入訂單記錄信息
    String[] insertParam = {"555555555", "977723233", updateTime, updateTime};
    transBusiness.insertData(insertParam);

    //步驟2:修改訂單記錄信息
    String[] updateParam = {"1111111111", updateTime, "1"};
    transBusiness.upateData(updateParam);
  }

}



@Service
public class TransBusiness {

  @Autowired
  JdbcTemplate dalClient;

  public void insertData(String[] param) {
    Map<String, Object> resultMap = new HashMap<>();
    String sql = "INSERT INTO test_order (`order_no`, `cust_no`,create_time,update_time) VALUES (?, ?,?,?)";

    int i = dalClient.update(sql, param);
    System.out.println("TransBusiness>>>insertData" + i);
    resultMap.put("插入的記錄數(shù)", i);
  }

  public void upateData(String[] param) {
    Map<String, Object> resultMap = new HashMap<>();
    String sql = "update test_order set order_no =?,update_time=? ? where id= ?";

    int i = dalClient.update(sql, param);
    System.out.println("TransBusiness>>>upateData" + i);
    resultMap.put("修改的記錄數(shù)", i);
  }

}

2.1自身調(diào)用事務(wù)失效解決方法1—在父方法中添加事務(wù)
通過doSomeThing()方法中添加事務(wù)性蚤告,可以解決1中事務(wù)自身調(diào)用失效的問題。

示列驗證:

OrderServiceImpl.insertAndUpdateOrderInfo方法中當步驟1執(zhí)行完成后服爷,數(shù)據(jù)庫中并不會存在該訂單記錄杜恰。當執(zhí)行步驟2時發(fā)生了異常,整個事務(wù)發(fā)生了回滾仍源。說明才方法解決了1自身調(diào)用事務(wù)失效的問題心褐。
說明:此處的@Transactional等同于 @Transactional(propagation = Propagation.REQUIRED) 表示支持當前事務(wù),如果沒有事務(wù)就新建一個事務(wù)笼踩,這是常見的選擇檬寂,也是spring默認的事務(wù)傳播

@Override
  @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
  public void doSomeThing1() {
    insertAndUpdateOrderInfo();
  }

  public void insertAndUpdateOrderInfo(){
    Date date = new Date();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String updateTime = dateFormat.format(date);

    //步驟1:插入訂單記錄信息
    String[] insertParam = {"8888888888", "977723233", updateTime, updateTime};
    transBusiness.insertData(insertParam);

    //步驟2:修改訂單記錄信息
    String[] updateParam = {"1111111112", updateTime, "1"};
    transBusiness.upateData(updateParam);
  }

2.2自身調(diào)用事務(wù)失效解決方法2—將事務(wù)方法拆分到另外一個類中

@Service
public class TransBusiness {

  @Autowired
  JdbcTemplate dalClient;

  @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
  public void insertAndUpdateOrderInfo(){
    Date date = new Date();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String updateTime = dateFormat.format(date);

    //步驟1:插入訂單記錄信息
    String[] insertParam = {"8888888888", "977723233", updateTime, updateTime};
    insertData(insertParam);

    //步驟2:修改訂單記錄信息
    String[] updateParam = {"1111111112", updateTime, "1"};
    upateData(updateParam);
  }

}

2.2自身調(diào)用事務(wù)失效解決方法2—將事務(wù)方法拆分到另外一個類中

@Service
public class TransBusiness {

  @Autowired
  JdbcTemplate dalClient;

  @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
  public void insertAndUpdateOrderInfo(){
    Date date = new Date();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String updateTime = dateFormat.format(date);

    //步驟1:插入訂單記錄信息
    String[] insertParam = {"8888888888", "977723233", updateTime, updateTime};
    insertData(insertParam);

    //步驟2:修改訂單記錄信息
    String[] updateParam = {"1111111112", updateTime, "1"};
    upateData(updateParam);
  }

}

3.SQL規(guī)范于1992年提出了數(shù)據(jù)庫事務(wù)隔離級別,以此用來保證并發(fā)操作數(shù)據(jù)的正確性及一致性戳表。Mysql的事務(wù)隔離級別由低往高可分為以下幾類:

  1. READ UNCOMMITTED(讀取未提交的數(shù)據(jù))

這是最不安全的一種級別桶至,查詢語句在無鎖的情況下運行,就讀取到別的未提交的數(shù)據(jù)匾旭,造成臟讀镣屹,如果未提交的那個事務(wù)數(shù)據(jù)全部回滾了,而之前讀取了這個事務(wù)的數(shù)據(jù)即是臟數(shù)據(jù)价涝,這種數(shù)據(jù)不一致性讀造成的危害是可想而知的女蜈。

  1. READ COMMITTED(讀取已提交的數(shù)據(jù))

一個事務(wù)只能讀取數(shù)據(jù)庫中已經(jīng)提交過的數(shù)據(jù),解決了臟讀問題色瘩,但不能重復(fù)讀伪窖,即一個事務(wù)內(nèi)的兩次查詢返回的數(shù)據(jù)是不一樣的。如第一次查詢金額是100居兆,第二次去查詢可能就是50了覆山,這就是不可重復(fù)讀取。

  1. REPEATABLE READ(可重復(fù)讀取數(shù)據(jù)泥栖,這也是Mysql默認的隔離級別)

一個事務(wù)內(nèi)的兩次無鎖查詢返回的數(shù)據(jù)都是一樣的簇宽,但別的事務(wù)的新增數(shù)據(jù)也能讀取到。比如另一個事務(wù)插入了一條數(shù)據(jù)并提交吧享,這個事務(wù)第二次去讀取的時候發(fā)現(xiàn)多了一條之前查詢數(shù)據(jù)列表里面不存在的數(shù)據(jù)魏割,這時候就是傳說的中幻讀了。這個級別避免了不可重復(fù)讀取钢颂,但不能避免幻讀的問題钞它。

  1. SERIALIZABLE(可串行化讀)

這是效率最低最耗費資源的一個事務(wù)級別,和可重復(fù)讀類似殊鞭,但在自動提交模式關(guān)閉情況下可串行化讀會給每個查詢加上共享鎖和排他鎖遭垛,意味著所有的讀操作之間不阻塞,但讀操作會阻塞別的事務(wù)的寫操作钱豁,寫操作也阻塞讀操作耻卡。

4.spring事務(wù)管理其實是對數(shù)據(jù)庫事務(wù)進行了封裝而已,并提了5種事務(wù)隔離級別和7種事務(wù)傳播機制牲尺。

4.1聲明式事務(wù)(declarative transaction management)是Spring提供的對程序事務(wù)管理的方式之一卵酪。Spring使用AOP來完成聲明式的事務(wù)管理,因而聲明式事務(wù)是以方法為單位谤碳,Spring的事務(wù)屬性自然就在于描述事務(wù)應(yīng)用至方法上的策略溃卡,在Spring中事務(wù)屬性有以下參數(shù):

image

readOnly屬性的詳細理解:

1)readonly并不是所有數(shù)據(jù)庫都支持的,不同的數(shù)據(jù)庫下會有不同的結(jié)果蜒简。

2)設(shè)置了readonly后瘸羡,connection都會被賦予readonly,效果取決于數(shù)據(jù)庫的實現(xiàn)搓茬。

a. 在oracle下測試犹赖,發(fā)現(xiàn)不支持readOnly队他,也就是不論Connection里的readOnly屬性是true還是false均不影響SQL的增刪改查;

b. 在mysql下測試,發(fā)現(xiàn)支持readOnly峻村,設(shè)置為true時麸折,只能查詢,若增刪改會發(fā)生如下異常:

Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:910)
at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:792)

3)在ORM中粘昨,設(shè)置了readonly會賦予一些額外的優(yōu)化垢啼,例如在Hibernate中,會被禁止flush等张肾。

4.2 spring 的 5種事務(wù)隔離級別

  1. ISOLATION_DEFAULT     (使用后端數(shù)據(jù)庫默認的隔離級別)

以下四個與JDBC的隔離級別相對應(yīng):

  1. ISOLATION_READ_UNCOMMITTED (允許讀取尚未提交的更改,可能導(dǎo)致臟讀芭析、幻影讀或不可重復(fù)讀)

  2. ISOLATION_READ_COMMITTED (允許從已經(jīng)提交的并發(fā)事務(wù)讀取,可防止臟讀,但幻影讀和不可重復(fù)讀仍可能會發(fā)生)

  3. ISOLATION_REPEATABLE_READ (對相同字段的多次讀取的結(jié)果是一致的,除非數(shù)據(jù)被當前事務(wù)本身改變⊥痰桑可防止臟讀和不可重復(fù)讀馁启,但幻影讀仍可能發(fā)生)

  4. ISOLATION_SERIALIZABLE (完全服從ACID的隔離級別,確保不發(fā)生臟讀尸饺、不可重復(fù)讀和幻影讀进统。這在所有隔離級別中也是最慢的,因為它通常是通過完全鎖定當前事務(wù)所涉及的數(shù)據(jù)表來完成的)

4.3 spring的7種事務(wù)傳播機制:

  1. REQUIRED(需要事務(wù)): 業(yè)務(wù)方法需要在一個事務(wù)中運行,如果方法運行時,已處在一個事務(wù)中,那么就加入該事務(wù),否則自己創(chuàng)建一個新的事務(wù).這是spring默認的傳播行為;

  2. NOT_SUPPORTED(不支持事務(wù)): 聲明方法需要事務(wù),如果方法沒有關(guān)聯(lián)到一個事務(wù),容器不會為它開啟事務(wù).如果方法在一個事務(wù)中被調(diào)用,該事務(wù)會被掛起,在方法調(diào)用結(jié)束后,原先的事務(wù)便會恢復(fù)執(zhí)行;

  3. REQUIREDS_NEW(需要新事務(wù)):業(yè)務(wù)方法總是會為自己發(fā)起一個新的事務(wù),如果方法已運行在一個事務(wù)中,則原有事務(wù)被掛起,新的事務(wù)被創(chuàng)建,直到方法結(jié)束,新事務(wù)才結(jié)束,原先的事務(wù)才會恢復(fù)執(zhí)行;
    備注:新建的事務(wù)如果沒有進行異常捕獲浪听,發(fā)生異常那么原事務(wù)方法也會發(fā)生回滾螟碎。(該結(jié)論經(jīng)過自測驗證)

  4. MANDATORY(強制性事務(wù)):只能在一個已存在事務(wù)中執(zhí)行。業(yè)務(wù)方法不能發(fā)起自己的事務(wù),如果業(yè)務(wù)方法在沒有事務(wù)的環(huán)境下調(diào)用,就拋異常

  5. NEVER(不能存在事務(wù)):聲明方法絕對不能在事務(wù)范圍內(nèi)執(zhí)行,如果方法在某個事務(wù)范圍內(nèi)執(zhí)行,容器就拋異常.只有沒關(guān)聯(lián)到事務(wù),才正常執(zhí)行.

  6. SUPPORTS(支持事務(wù)):如果業(yè)務(wù)方法在某個事務(wù)范圍內(nèi)被調(diào)用,則方法成為該事務(wù)的一部分,如果業(yè)務(wù)方法在事務(wù)范圍外被調(diào)用,則方法在沒有事務(wù)的環(huán)境下執(zhí)行.

  7. NESTED(嵌套事務(wù)):如果一個活動的事務(wù)存在,則運行在一個嵌套的事務(wù)中.如果沒有活動的事務(wù),則按REQUIRED屬性執(zhí)行.它使用了一個單獨的事務(wù),這個事務(wù)擁有多個可以回滾的保證點.內(nèi)部事務(wù)回滾不會對外部事務(wù)造成影響, 它只對DataSourceTransactionManager 事務(wù)管理器起效.

思考:Nested和RequiresNew的區(qū)別:

a. RequiresNew每次都創(chuàng)建新的獨立的物理事務(wù)迹栓,而Nested只有一個物理事務(wù)掉分;

b. Nested嵌套事務(wù)回滾或提交不會導(dǎo)致外部事務(wù)回滾或提交,但外部事務(wù)回滾將導(dǎo)致嵌套事務(wù)回滾克伊,而 RequiresNew由于都是全新的事務(wù)酥郭,所以之間是無關(guān)聯(lián)的;

c. Nested使用JDBC 3的保存點實現(xiàn)愿吹,即如果使用低版本驅(qū)動將導(dǎo)致不支持嵌套事務(wù)不从。

實際應(yīng)用中一般使用默認的事務(wù)傳播行為,偶爾會用到RequiresNew和Nested方式犁跪。


最新2020整理收集的一些高頻面試題(都整理成文檔)椿息,有很多干貨,包含mysql坷衍,netty寝优,spring,線程枫耳,spring cloud乏矾、jvm、源碼、算法等詳細講解钻心,也有詳細的學習規(guī)劃圖凄硼,面試題整理等,需要獲取這些內(nèi)容的朋友請加Q君樣:11604713672

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扔役,一起剝皮案震驚了整個濱河市帆喇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亿胸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件预皇,死亡現(xiàn)場離奇詭異侈玄,居然都是意外死亡,警方通過查閱死者的電腦和手機吟温,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門序仙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鲁豪,你說我怎么就攤上這事潘悼。” “怎么了爬橡?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵治唤,是天一觀的道長。 經(jīng)常有香客問我糙申,道長宾添,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任柜裸,我火速辦了婚禮缕陕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疙挺。我一直安慰自己扛邑,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布铐然。 她就那樣靜靜地躺著蔬崩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锦爵。 梳的紋絲不亂的頭發(fā)上舱殿,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音险掀,去河邊找鬼沪袭。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的冈绊。 我是一名探鬼主播侠鳄,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼死宣!你這毒婦竟也來了伟恶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤毅该,失蹤者是張志新(化名)和其女友劉穎博秫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眶掌,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡挡育,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了朴爬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片即寒。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖召噩,靈堂內(nèi)的尸體忽然破棺而出母赵,到底是詐尸還是另有隱情,我是刑警寧澤具滴,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布凹嘲,位于F島的核電站,受9級特大地震影響抵蚊,放射性物質(zhì)發(fā)生泄漏施绎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一贞绳、第九天 我趴在偏房一處隱蔽的房頂上張望谷醉。 院中可真熱鬧,春花似錦冈闭、人聲如沸俱尼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遇八。三九已至,卻和暖如春耍休,著一層夾襖步出監(jiān)牢的瞬間刃永,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工羊精, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斯够,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像读规,于是被迫代替她去往敵國和親抓督。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 眾所周知束亏,數(shù)據(jù)庫能實現(xiàn)本地事務(wù)铃在,也就是在同一個數(shù)據(jù)庫中,你可以允許一組操作要么全都正確執(zhí)行碍遍,要么全都不執(zhí)行定铜。這里特...
    martin4096閱讀 216評論 0 0
  • 什么是事務(wù)? 事務(wù)由一組操作構(gòu)成怕敬,我們希望這組操作能夠全部正確執(zhí)行宿稀,如果這一組操作中的任意一個步驟發(fā)生錯誤,那么就...
    愛情小傻蛋閱讀 1,174評論 1 1
  • 分布式事務(wù)解決方案 對于剛剛接觸分布式系統(tǒng)的伙伴來說赖捌,分布式看起來非常高大上、深不可測矮烹。目前已有Dubbo越庇、Spr...
    wxdfun閱讀 724評論 0 0
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友奉狈。感恩相遇卤唉!感恩不離不棄。 中午開了第一次的黨會仁期,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,562評論 0 11
  • 彩排完桑驱,天已黑
    劉凱書法閱讀 4,212評論 1 3