SpringBoot整合Transaction——事務(wù)的傳播行為

前言

SpringBoot下想要使用事務(wù)非常簡單,只需要在Service的類或方法上面加上一個@Transactional注解即可實(shí)現(xiàn)失敗自動回滾。大部分情況下,默認(rèn)的@Transactional就能很好的滿足需求了,但是更加深入的了解@Transactional還是很有必要的。

傳播行為

在@Transactional注解中,可以propagation屬性用來配置事務(wù)傳播,支持7種不同的傳播機(jī)制

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

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

  • REQUIRES_NEW:不管是否存在事務(wù),該方法總會為自己發(fā)起一個新的事務(wù)。如果方法已經(jīng)運(yùn)行在一個事務(wù)中,則原有事務(wù)掛起,新的事務(wù)被創(chuàng)建。

  • MANDATORY:該方法只能在一個已經(jīng)存在的事務(wù)中執(zhí)行,業(yè)務(wù)方法不能發(fā)起自己的事務(wù)。如果在沒有事務(wù)的環(huán)境下被調(diào)用,容器拋出例外。

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

  • NEVER:該方法絕對不能在事務(wù)范圍內(nèi)執(zhí)行。如果在就拋異常。只有該方法沒有關(guān)聯(lián)到任何事務(wù),才正常執(zhí)行。

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

實(shí)驗(yàn)驗(yàn)證

我們定義了兩個函數(shù),一個父函數(shù),一個子函數(shù),它們都實(shí)現(xiàn)了事務(wù)機(jī)制,父函數(shù)中嵌套了子函數(shù)。這兩個函數(shù)的作用都是在同一張表中插入一條數(shù)據(jù),通過改變父子函數(shù)的事務(wù)傳播行為以及它們是否拋出異常來驗(yàn)證以上七種傳播行為的正確性。結(jié)果如圖所示(null表示插入失敗):


image.png

源代碼

實(shí)體類 Transaction

package org.jz.transaction.chapter1;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("transaction")
public class Transaction {

    @TableId(value = "`id`",type = IdType.AUTO)
    private Integer id;

    @TableField("`name`")
    private String name;

    @TableField("`group`")
    private Integer group;

}

Mapper接口 TransactionMapper

package org.jz.transaction.chapter1;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TransactionMapper extends BaseMapper<Transaction> {

}

主程序類 Chapter1Application

package org.jz.transaction.chapter1;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@SpringBootApplication
public class Chapter1Application {

    @Bean
    @Scope("prototype")
    public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
        return new TransactionTemplate(platformTransactionManager);
    }

    @Autowired
    TransactionTemplate father;

    @Autowired
    TransactionTemplate son;

    @Autowired
    TransactionMapper transactionMapper;

    public static void main(String[] args) {
        SpringApplication.run(Chapter1Application.class, args);
    }

    @Data
    private class Item {
        private Integer fatherPropagation;
        private Integer sonPropagation;
        private Integer fatherThrowError;
        private Integer sonThrowError;
        private String result;
        private String errorResult;
    }

    @PostConstruct
    public void testPropagation() {
        List<Item> items = new ArrayList<>();
        //遍歷所有可能的情況
        permute(items, new ArrayList<>(), Arrays.asList(7, 7, 2, 2), 0);
        //傳播行為列表
        List<String> propagation = Arrays.asList("required", "support", "mandatory", "requires_new", "not_support",
                "never", "nested");
        for (int i = 0; i < items.size(); i++) {
            final int group = i;
            Item item = items.get(i);
            father.setPropagationBehavior(item.getFatherPropagation());
            try {
                father.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus status) {
                        //先插入一條父數(shù)據(jù)
                        transactionMapper.insert(new Transaction(null,"father", group));
                        son.setPropagationBehavior(item.getSonPropagation());
                        son.execute(new TransactionCallbackWithoutResult() {
                            @Override
                            protected void doInTransactionWithoutResult(TransactionStatus status) {
                                //插入一條子數(shù)據(jù)
                                transactionMapper.insert(new Transaction(null,"son", group));
                                if (item.getSonThrowError() > 0) {
                                    throw new RuntimeException("子調(diào)用拋出異常");
                                }
                            }
                        });
                        if (item.getFatherThrowError() > 0) {
                            throw new RuntimeException("父調(diào)用拋出異常");
                        }
                    }
                });
            } catch (Exception e) {
                //捕獲父類異常,防止停止運(yùn)行
                log.info("捕獲 {}", e.getMessage());
                item.setErrorResult(e.getMessage());
            }
            List<Transaction> transactions = transactionMapper.selectList(new LambdaQueryWrapper<Transaction>()
                    .eq(Transaction::getGroup, group));
            boolean fatherRes = transactions.stream().anyMatch(a -> "father".equals(a.getName()));
            boolean sonRes = transactions.stream().anyMatch(a -> "son".equals(a.getName()));
            item.setResult(StrUtil.join(" | ", fatherRes ? "father" : null, sonRes ? "son" : null));
        }
        //清空表
        transactionMapper.delete(new QueryWrapper<>());

        //輸出為表格
        ExcelWriter writer = ExcelUtil.getWriter(FileUtil.getUserHomePath() + "/Desktop/res.xlsx");
        writer.addHeaderAlias("fatherPropagation", "父調(diào)用傳播行為");
        writer.addHeaderAlias("sonPropagation", "子調(diào)用傳播行為");
        writer.addHeaderAlias("fatherThrowError", "父調(diào)用拋出異常");
        writer.addHeaderAlias("sonThrowError", "子調(diào)用拋出異常");
        writer.addHeaderAlias("errorResult", "報錯輸出");
        writer.addHeaderAlias("result", "結(jié)果");
        List<Map<String, Object>> collect = items.stream().map(a -> {
            Map<String, Object> map = BeanUtil.beanToMap(a);
            map.put("fatherPropagation", propagation.get((Integer) map.get("fatherPropagation")));
            map.put("sonPropagation", propagation.get((Integer) map.get("sonPropagation")));
            return map;
        }).collect(Collectors.toList());
        writer.write(collect);
        writer.close();
    }

    private void permute(List<Item> items, List<Integer> list, List<Integer> asList, int i) {
        if (i == asList.size()) {
            Item item = new Item();
            List<Integer> copy = new ArrayList<>(list);
            item.setFatherPropagation(copy.get(0));
            item.setSonPropagation(copy.get(1));
            item.setFatherThrowError(copy.get(2));
            item.setSonThrowError(copy.get(3));
            items.add(item);
            return;
        }
        Integer loop = asList.get(i);
        for (int k = 0; k < loop; k++) {
            list.add(k);
            permute(items, list, asList, i + 1);
            list.remove(list.size() - 1);
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容