SpringBoot 快速開啟事務(wù)(附常見坑點(diǎn))

序言:此前,我們主要通過XML配置Spring來托管事務(wù)。在SpringBoot則非常簡(jiǎn)單,只需在業(yè)務(wù)層添加事務(wù)注解(@Transactional )即可快速開啟事務(wù)。雖然事務(wù)很簡(jiǎn)單,但對(duì)于數(shù)據(jù)方面是需要謹(jǐn)慎對(duì)待的,識(shí)別常見坑點(diǎn)對(duì)我們開發(fā)有幫助。

1. 引入依賴

    <!--依賴管理 -->
    <dependencies>
        <dependency> <!--添加Web依賴 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency> <!--添加Mybatis依賴 -->
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency><!--添加MySQL驅(qū)動(dòng)依賴 -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency><!--添加Test依賴 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2. 添加配置

主要是配置數(shù)據(jù)源和開啟Mybatis的自動(dòng)駝峰映射

@SpringBootApplication
public class MybatisTransactionApplication {

    public static void main(String[] args) {
        //1.初始化
        SpringApplication application=  new SpringApplication(MybatisTransactionApplication.class);

        //2.添加數(shù)據(jù)源
        Map<String,Object> map = new HashMap<>();
        map.put("spring.datasource.url","jdbc:mysql://localhost:3306/socks?useSSL=false");
        map.put("spring.datasource.username","root");
        map.put("spring.datasource.password","root");

        //3.開啟駝峰映射 (Such as account_id ==> accountId)
        map.put("mybatis.configuration.map-underscore-to-camel-case",true);
        application.setDefaultProperties(map);

        //4.啟動(dòng)應(yīng)用
        application.run(args);
    }
}

3. 添加數(shù)據(jù)庫記錄

打開 Navicat 的查詢窗口,然后執(zhí)行以下SQL:

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `account_id` varchar(30) ,
  `account_name` varchar(30),
  `balance` decimal(20,2),
  PRIMARY KEY (`account_id`)
);

insert into account values ('1','admin','1000.25');

執(zhí)行完畢后,可以查詢到賬戶數(shù)據(jù),如圖:

4. 編寫代碼

以操作賬戶金額為例,模擬正常操作金額提交事務(wù),以及發(fā)生異常回滾事務(wù)。
其中控制層代碼如下:

package com.hehe.controller;

@RestController
public class AccountController {

    @SuppressWarnings("all")
    @Autowired
    AccountService accountService;

    @GetMapping("/")
    public Account getAccount() {
        //查詢賬戶
        return accountService.getAccount();
    }

    @GetMapping("/add")
    public Object addMoney() {
        try {
            accountService.addMoney();
        } catch (Exception e) {
            return "發(fā)生異常了:" + accountService.getAccount();
        }
        return getAccount();
    }
}

在業(yè)務(wù)層使用 @Transactional 開啟事務(wù),執(zhí)行數(shù)據(jù)庫操作后拋出異常。具體代碼如下:

package com.hehe.service;

@Service
public class AccountService {

    @SuppressWarnings("all")
    @Autowired
    AccountMapper accountMapper;

    public Account getAccount() {
        return accountMapper.getAccount();
    }

    @Transactional
    public void addMoney() throws Exception {
        //先增加余額
        accountMapper.addMoney();
        //然后遇到故障
        throw new RuntimeException("發(fā)生異常了..");
    }
}

數(shù)據(jù)庫層就很簡(jiǎn)單了,我們通過注解來實(shí)現(xiàn)賬戶數(shù)據(jù)的查詢,具體如下:

package com.hehe.mapper;

@Mapper
public interface AccountMapper {

    @Select("select * from account where account_id=1")
    Account getAccount();

    @Update("update account set balance = balance+100 where account_id=1")
    void addMoney();
}

其中 Account 實(shí)體對(duì)象如下:

package com.hehe.pojo;

public class Account {

    private String accountId;
    private String accountName;
    private BigDecimal balance;

    // Override toString Method ..
    // Getter & Setters  ..
}

5. 測(cè)試事務(wù)

啟動(dòng)應(yīng)用,訪問 http://localhost:8080 ,可以看到賬戶數(shù)據(jù),如下:

然后訪問 http://localhost:8080/add ,可以看到賬戶余額并沒有增加,如下: 也就是說事務(wù)開啟成功,數(shù)據(jù)得到回滾。

6. 常見坑點(diǎn)

使用事務(wù)注解@Transactional 之前,應(yīng)該先了解它的相關(guān)屬性,避免在實(shí)際項(xiàng)目中踩中各種各樣的坑點(diǎn)。

常見坑點(diǎn)1:遇到檢測(cè)異常時(shí),事務(wù)默認(rèn)不回滾。

例如下面這段代碼,賬戶余額依舊增加成功,并沒有因?yàn)楹竺嬗龅絊QLException(檢測(cè)異常)而進(jìn)行事務(wù)回滾!!

   @Transactional
    public void addMoney() throws Exception {
        //先增加余額
        accountMapper.addMoney();
        //然后遇到故障
        throw new SQLException("發(fā)生異常了..");
    }

原因分析:因?yàn)镾pring的默認(rèn)的事務(wù)規(guī)則是遇到運(yùn)行異常(RuntimeException及其子類)和程序錯(cuò)誤(Error)才會(huì)進(jìn)行事務(wù)回滾,顯然SQLException并不屬于這個(gè)范圍。如果想針對(duì)檢測(cè)異常進(jìn)行事務(wù)回滾,可以在@Transactional 注解里使用
rollbackFor 屬性明確指定異常。例如下面這樣,就可以正常回滾:

  @Transactional(rollbackFor = Exception.class)
    public void addMoney() throws Exception {
        //先增加余額
        accountMapper.addMoney();
        //然后遇到故障
        throw new SQLException("發(fā)生異常了..");
    }

常見坑點(diǎn)2: 在業(yè)務(wù)層捕捉異常后,發(fā)現(xiàn)事務(wù)不生效。

這是許多新手都會(huì)犯的一個(gè)錯(cuò)誤,在業(yè)務(wù)層手工捕捉并處理了異常,你都把異常“吃”掉了,Spring自然不知道這里有錯(cuò),更不會(huì)主動(dòng)去回滾數(shù)據(jù)。例如:下面這段代碼直接導(dǎo)致增加余額的事務(wù)回滾沒有生效。

    @Transactional
    public void addMoney() throws Exception {
        //先增加余額
        accountMapper.addMoney();
        //謹(jǐn)慎:盡量不要在業(yè)務(wù)層捕捉異常并處理
        try {
            throw new SQLException("發(fā)生異常了..");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

不要小瞧了這些細(xì)節(jié),往前暴露異常很大程度上很能夠幫我們快速定位問題,而不是經(jīng)常在項(xiàng)目上線后出現(xiàn)問題,卻無法刨根知道哪里報(bào)錯(cuò)。

推薦做法:若非實(shí)際業(yè)務(wù)要求,則在業(yè)務(wù)層統(tǒng)一拋出異常,然后在控制層統(tǒng)一處理。

    @Transactional
    public void addMoney() throws Exception {
        //先增加余額
        accountMapper.addMoney();
        //推薦:在業(yè)務(wù)層將異常拋出
        throw new RuntimeException("發(fā)生異常了..");
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379

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