Spring事务传播机制实战

一篇文章带你精通Spring事务传播机制,三十个测试覆盖你所有常用场景。

Talk is cheap. Show me the code.

源代码地址

/**
 * 启动参数加上  -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true -Dcglib.debugLocation=D:\cglib  以便查看动态代理生成的字节码文件
 */
@RunWith(SpringRunner.class)
@SpringBootTest
@FixMethodOrder()
public class TransactionPropagationTest {

    @Autowired
    private PhotoService photoService;

    @Autowired
    private PhotoMapperOne photoMapperOne;

    @Autowired
    private PhotoMapperTwo photoMapperTwo;

    @Autowired
    @Qualifier("jdbcTemplateOne")
    private JdbcTemplate jdbcTemplateOne;

    @Autowired
    @Qualifier("jdbcTemplateTwo")
    private JdbcTemplate jdbcTemplateTwo;

    private static PhotoBO photoBO = new PhotoBO();

    private String createTableSql = "" +
            "create table if not exists photo\n" +
            "(\n" +
            "  id bigint auto_increment,\n" +
            "  name varchar(64) default '' not null,\n" +
            "  url varchar(128) default '' not null\n" +
            ")";

    /**
     * 此处注意下 H2的truncate语句和MySQL的truncate语句有细微差别(H2 多了 table)
     */
    private String truncateTableSql = "truncate table photo";

    /**
     * 测试开始之前初始化需要的数据
     */
    @BeforeClass
    public static void testBeforeClass() {
        photoBO.setName("transaction");
        photoBO.setUrl("xxx/xxx/transaction");
    }

    /**
     * 每个测试方法运行之前都清空两个数据库中对应的表中的数据
     */
    @Before
    public void testBefore() {
        jdbcTemplateOne.execute(createTableSql);
        jdbcTemplateTwo.execute(createTableSql);
        jdbcTemplateOne.execute(truncateTableSql);
        jdbcTemplateTwo.execute(truncateTableSql);
    }

    /**
     * 接下来就开始进行单数据源,单事务管理器,多数据源,多事务管理器,以及不同数据源配合不同事务管理器的实验
     * <p>
     * 首先进行单数据源,单事务管理器情况下,Spring事务传播的特性测试
     *
     * @see org.springframework.transaction.annotation.Propagation#REQUIRED
     * <p>
     * 第一个测试 REQUIRED 机制下外方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test1() {
        try {
            photoService.baseTwo(photoBO, Propagation.REQUIRED, true, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#REQUIRED
     * <p>
     * 测试 REQUIRED 机制下内方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test2() {
        try {
            photoService.baseTwo(photoBO, Propagation.REQUIRED, false, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#REQUIRES_NEW
     * <p>
     * 测试 REQUIRES_NEW 机制下外方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test3() {
        try {
            photoService.baseTwo(photoBO, Propagation.REQUIRES_NEW, true, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#REQUIRES_NEW
     * <p>
     * 测试 REQUIRES_NEW 机制下内方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test4() {
        try {
            photoService.baseTwo(photoBO, Propagation.REQUIRES_NEW, false, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#SUPPORTS
     * <p>
     * 测试 SUPPORTS 机制下外方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test5() {
        try {
            photoService.baseTwo(photoBO, Propagation.SUPPORTS, true, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#SUPPORTS
     * <p>
     * 测试 SUPPORTS 机制下内方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test6() {
        try {
            photoService.baseTwo(photoBO, Propagation.SUPPORTS, false, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#NOT_SUPPORTED
     * <p>
     * 测试 NOT_SUPPORTED 机制下外方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test7() {
        try {
            photoService.baseTwo(photoBO, Propagation.NOT_SUPPORTED, true, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#NOT_SUPPORTED
     * <p>
     * 测试 NOT_SUPPORTED 机制下内方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test8() {
        try {
            photoService.baseTwo(photoBO, Propagation.NOT_SUPPORTED, false, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(2, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#NEVER
     * <p>
     * 测试 NEVER 机制下抛出异常之后事务的回滚情况
     * <p>
     * 注意当调用 NEVER 机制下的方法时有事务上下文将抛出异常
     */
    @Test()
    public void test9() {
        try {
            photoService.baseTwo(photoBO, Propagation.NEVER, false, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#NEVER
     * <p>
     * 测试 NEVER 机制下抛出异常之后事务的回滚情况
     * <p>
     * 注意当调用 NEVER 机制下的方法时有事务上下文将抛出异常
     */
    @Test()
    public void test10() {
        try {
            photoService.testNever(photoBO, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#MANDATORY
     * <p>
     * 测试 MANDATORY 机制下外方法抛出异常之后事务的回滚情况
     * <p>
     * 注意当调用 MANDATORY 机制下的方法时没有事务上下文将抛出异常
     */
    @Test()
    public void test11() {
        try {
            photoService.baseTwo(photoBO, Propagation.MANDATORY, true, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#MANDATORY
     * <p>
     * 测试 MANDATORY 机制下内方法抛出异常之后事务的回滚情况
     * <p>
     * 注意当调用 MANDATORY 机制下的方法时没有事务上下文将抛出异常
     */
    @Test()
    public void test12() {
        try {
            photoService.baseTwo(photoBO, Propagation.MANDATORY, false, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#MANDATORY
     * <p>
     * 测试 MANDATORY 机制下抛出异常之后事务的回滚情况
     * <p>
     * 注意当调用 MANDATORY 机制下的方法时没有事务上下文将抛出异常
     */
    @Test()
    public void test13() {
        try {
            photoService.testMandatory(photoBO, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#NESTED
     * <p>
     * 测试 NESTED 机制下外方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test14() {
        try {
            photoService.baseTwo(photoBO, Propagation.NESTED, true, false);
        } catch (Exception e) {
        }
        Assert.assertEquals(0, selectCountSameTable());
    }

    /**
     * @see org.springframework.transaction.annotation.Propagation#NESTED
     * <p>
     * 测试 NESTED 机制下内方法抛出异常之后事务的回滚情况
     */
    @Test()
    public void test15() {
        try {
            photoService.baseTwo(photoBO, Propagation.NESTED, false, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    /**
     * 有了上面的单数据源,单事务管理器的经验,我们再来看下多数据源,多事务管理器,不同数据源对应不同事务管理器情况下事务的机制
     * <p>
     * 首先看下多数据源,多事务管理器的情况
     *
     * @see Propagation#REQUIRED
     */
    @Test
    public void test16() {
        try {
            photoService.baseOne(photoBO, Propagation.REQUIRED, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#REQUIRED
     */
    @Test
    public void test17() {
        try {
            photoService.baseOne(photoBO, Propagation.REQUIRED, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 0};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#REQUIRES_NEW
     */
    @Test
    public void test18() {
        try {
            photoService.baseOne(photoBO, Propagation.REQUIRES_NEW, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#REQUIRES_NEW
     */
    @Test
    public void test19() {
        try {
            photoService.baseOne(photoBO, Propagation.REQUIRES_NEW, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 0};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#SUPPORTS
     */
    @Test
    public void test20() {
        try {
            photoService.baseOne(photoBO, Propagation.SUPPORTS, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#SUPPORTS
     */
    @Test
    public void test21() {
        try {
            photoService.baseOne(photoBO, Propagation.SUPPORTS, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#NOT_SUPPORTED
     */
    @Test
    public void test22() {
        try {
            photoService.baseOne(photoBO, Propagation.NOT_SUPPORTED, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#NOT_SUPPORTED
     */
    @Test
    public void test23() {
        try {
            photoService.baseOne(photoBO, Propagation.NOT_SUPPORTED, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#NEVER
     */
    @Test
    public void test24() {
        try {
            photoService.baseOne(photoBO, Propagation.NEVER, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#NEVER
     */
    @Test
    public void test25() {
        try {
            photoService.baseOne(photoBO, Propagation.NEVER, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#MANDATORY
     */
    @Test
    public void test26() {
        try {
            photoService.baseOne(photoBO, Propagation.MANDATORY, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 0};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#MANDATORY
     */
    @Test
    public void test27() {
        try {
            photoService.baseOne(photoBO, Propagation.MANDATORY, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 0};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#NESTED
     */
    @Test
    public void test28() {
        try {
            photoService.baseOne(photoBO, Propagation.NESTED, true, false);
        } catch (Exception e) {
        }
        int[] result = {0, 1};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * @see Propagation#NESTED
     */
    @Test
    public void test29() {
        try {
            photoService.baseOne(photoBO, Propagation.NESTED, false, true);
        } catch (Exception e) {
        }
        int[] result = {1, 0};
        Assert.assertArrayEquals(result, selectCountDifferentTable());
    }

    /**
     * 通过上面的测试,现在我们知道了Spring中不同的事务管理器之间不会遵循原本的事务传播机制
     * <p>
     * 下面我们再来看下不同数据源对应的不同事务管理器的情况
     * <p>
     * 通过下面的测试可以得出结论:不同数据源的事务管理器不会对当前数据源上的操作产生影响
     */
    @Test
    public void test30() {
        try {
            photoService.differentDataSourceDifferentTransactionManager(photoBO, true);
        } catch (Exception e) {
        }
        Assert.assertEquals(1, selectCountSameTable());
    }

    private int selectCountSameTable() {
        return photoMapperTwo.selectCount(null);
    }

    private int[] selectCountDifferentTable() {
        int[] result = new int[2];
        result[0] = photoMapperOne.selectCount(null);
        result[1] = photoMapperTwo.selectCount(null);
        return result;
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容