ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【弄nèng - 化繁为简】@Transactional(propagation = Propagation.REQUIRES_NEW)失效

2021-11-02 19:58:50  阅读:249  来源: 互联网

标签:事务 get Transactional entity Propagation public testMapper 化繁为简


文章目录

参考
https://blog.csdn.net/hepei120/article/details/78058468
https://blog.csdn.net/yangquanwa/article/details/88578357

一. @Transactional失效

@Transactional失效的场景有很多种,感兴趣的研究下,文章很多,本文着重说明类内部调用Spring事务注解@Transactional失效的场景。

现象1

在一个事务中更新之后再查询能查询到最新的数据,毋庸置疑。
代码

    @Autowired
    private TestMapper testMapper;

    @Transactional
    public void testTransactional() {
        System.out.println("1.====:" + testMapper.selectById(1).toString());
        updateTestById("司马缸5");
        System.out.println("2.====:" + testMapper.selectById(1).toString());
    }
    
	public void updateTestById(String name) {
        TestEntity entity = new TestEntity();
        entity.setId(1);
        entity.setName(name);
        testMapper.updateById(entity);
    }

执行testTransactional()
输出
在这里插入图片描述

现象2

在一个事务中更新,在查询方法中添加事务传播行为Propagation.REQUIRES_NEW,意味着不管事务存不存在都新启用一个事务运行。

代码

    @Autowired
    private TestMapper testMapper;

    @Transactional
    public void testTransactional() {
        System.out.println("1.====:" + testMapper.selectById(1).toString());
        updateTestById("司马缸5");
        System.out.println("2.====:" + get().toString());
    }

    public void updateTestById(String name) {
        TestEntity entity = new TestEntity();
        entity.setId(1);
        entity.setName(name);
        testMapper.updateById(entity);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public TestEntity get() {
        return testMapper.selectById(1);
    }

执行testTransactional()
输出
在这里插入图片描述

分析

事务隔离级别

事务隔离级别由弱到强分别是:READ_UNCOMMITTED(未提交读)、READ_COMMITTED(提交读)、REPEATABLE_READ(重复读)和SERIALIZABLE(串行读)。

数据问题

  • 脏读:
    脏读指的是一个事务允许读取其他正在运行的事务还没有提交的数据,这种情况的发生主要因为没有加锁。

  • 不可重复读:
    是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)
    例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。
    要达到允许可重复读,必须让当前事务保持一个读共享锁。

  • 幻读:
    幻读指的是事务不是串行发生时发生的一种现象,是事务A读取了事务B已提交的新增数据。例如第一个事务对一个表的所有数据进行修改,同时第二个事务向表中插入一条新数据。那么操作第一个事务的用户就发现表中还有没有修改的数据行,就像发生了幻觉一样。解决幻读的方法是增加范围锁或者表锁。

不同的隔离级别会出现的问题

隔离界别脏读不可重复读幻读
READ_UNCOMMITTED允许允许允许
READ_COMMITTED不允允许允许
REPEATABLE_READ不允不允允许
SERIALIZABLE不允不允不允许

MySQL的默认事务隔离级别是REPEATABLE_READ,ORACLE、SQL Server、DB2和PostgreSQL的默认事务隔离级别是READ_COMMITED

按照MySQL的默认事务隔离级别REPEATABLE_READ按理来说不应该出现脏读,也就是不应该读取到其他事务没有提交的数据,但是现在读取到了,WHY???
继续看现象3

现象3

在一个事务中更新,在查询方法中添加事务传播行为Propagation.REQUIRES_NEW,意味着不管事务存不存在都新启用一个事务运行。

代码

@Service
public class TestServiceImpl extends ServiceImpl<TestMapper, TestEntity> implements ITestService {

    @Autowired
    private TestMapper testMapper;

    @Autowired
    private TestServiceImpl2 testServiceImpl2;

    @Transactional
    public void testTransactional() {
        System.out.println("1.====:" + testMapper.selectById(1).toString());
        updateTestById("司马缸5");
        System.out.println("2.====:" + testServiceImpl2.get().toString());
    }

    public void updateTestById(String name) {
        TestEntity entity = new TestEntity();
        entity.setId(1);
        entity.setName(name);
        testMapper.updateById(entity);
    }
}

@Service
public class TestServiceImpl2 extends ServiceImpl<TestMapper, TestEntity> implements ITestService {

    @Autowired
    private TestMapper testMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public TestEntity get() {
        return testMapper.selectById(1);
    }
}

执行testTransactional()
输出
在这里插入图片描述

分析

看出现象2和现象3的代码有什么不同了吗?调用get()不同,2是this.get(),3是testServiceImpl2.get(),3把get()方法提取出来了。
都是查询数据库为什么2中@Transactional(propagation = Propagation.REQUIRES_NEW)失效了呢?

原因

Spring事务是基于AOP实现的,就是把@Transactional标识的类进行代理,在创建对象的时候创建代理类,在代理类中执行方法前后添加事务处理,相当于@Around。当我们执行需要事务的方法时是调用的代理类执行。但是在方法内部调用本类其他事务方法是通过this.get()的方式,得到的是目标类不是代理类,所以get()方法的Propagation.REQUIRES_NEW是失效的,并没有创建一个新的事务,还是在一个事务中执行。

结论

事务是基于代理类的

标签:事务,get,Transactional,entity,Propagation,public,testMapper,化繁为简
来源: https://blog.csdn.net/yy756127197/article/details/121105655

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有