• Spring事务


    Spring事务

    事务特性

    事务有四大特性(ACID),原子性、持久性、一致性和隔离性

    • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成。不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态
    • 一致性(Consistency):在事务开始之前和事务事务结束之后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精准度、串联性以及后续数据库可以自发性地完成预定的工作
    • 持久性(Isolation):事务处理结束后,对数据的修改就是永久的。即使系统故障也不会丢失
    • 隔离性(Durability):数据库允许多个并发事务同时对其数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由交叉执行而导致数据的不一致。

    spring事务隔离级别

    Java Spring框架提供了一种方便的方式来管理数据库事务,它支持多种事务隔离级别。事务隔离级别决定了事务在并发执行时的隔离程度,包括对其他事务的可见性和可能出现的并发问题。

    以下是Spring框架支持的事务隔离级别及其详细说明:

    1. ISOLATION_DEFAULT(默认):这是系统的默认隔离级别。根据具体数据库来定义,大多数数据库默认级别是可重复读。
    2. ISOLATION_READ_UNCOMMITTED(读未提交):在这个级别,一个事务可以看到其他未提交事务的变动。这种级别可以导致脏读、不可重复读和幻读的问题。
    3. ISOLATION_READ_COMMITTED(读提交):在这个级别,一个事务只能看到其他事务已经提交的变动。这种级别可以避免脏读问题,但可能会出现不可重复读和幻读的问题。
    4. ISOLATION_REPEATABLE_READ(可重复读):在这个级别,同一事务中多次读取的数据是一致的。这种级别可以避免脏读和不可重复读的问题,但可能会出现幻读的问题。
    5. ISOLATION_SERIALIZABLE(可串行化):这是最高的事务隔离级别。在这个级别,事务串行化顺序执行,可以避免脏读、不可重复读和幻读的问题。但是这种隔离级别效率低下,因为事务通常需要等待前一个事务完成,才能继续执行。

    Spring事务的默认隔离级别与数据库一致

    MySQL的事务隔离级别默认是可重复读(REPEATABLE READ),这是大多数数据库系统的默认设置。这个隔离级别可以避免脏读和不可重复读的问题,但可能会出现幻读的问题。

    spring事务传播行为

    Java Spring框架中的事务传播行为是指在一个事务方法被另一个事务方法调用时,如何处理事务的传播。事务传播行为定义了在一个方法中调用另一个方法时,事务应该如何启动、提交或回滚。

    以下是Spring框架支持的事务传播行为及其详细说明:

    1. PROPAGATION_REQUIRED(必需):如果当前存在一个事务,那么就加入这个事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
    2. PROPAGATION_SUPPORTS(支持):支持当前事务,如果当前没有事务,就以非事务方式执行。
    3. PROPAGATION_MANDATORY(强制):使用当前的事务,如果当前没有事务,就抛出异常。
    4. PROPAGATION_REQUIRES_NEW(新建):新建事务,如果当前存在事务,把当前事务挂起。
    5. PROPAGATION_NOT_SUPPORTED(不支持):以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    6. PROPAGATION_NEVER(从不):以非事务方式执行,如果当前存在事务,抛出异常。
    7. PROPAGATION_NESTED(嵌套):如果当前存在一个事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

    Spring的事务传播行为默认是PROPAGATION_REQUIRED,也就是如果当前存在一个事务,就加入该事务;如果当前没有事务,就新建一个事务。

    Spring中事务的实现

    在Spring中,事务有两种实现方式:

    • 编程式事务(手动操作事务)
    • 声明式事务(利用注解自动控制事务的开启和提交)

    编程式事务

    Spring 中手动操作事务和 MySQL操作事务类似,也是有 3 个重要操作

    开启事务(获取事务)
    提交事务
    回滚事务
    Spring Boot 内置了两个对象,DataSourceTransactionManager (事务管理器)用来获取事务(开启事务)、提交或回滚事务的,而 TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得一个事务 TransactionStatus 对象

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring.xml"})//加载配置文件
    public class UserTest {
        @Autowired
        private UserService userService;
        @Autowired
        private TransactionDefinition transactionDefinition;
        @Autowired
        private DataSourceTransactionManager transactionManager;
    
        @Test
        public void test() {
            final TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
            try {
                final User user = new User() {{
                    this.setUsername("test" + System.currentTimeMillis());
                    this.setPassword("123456");
                    this.setCreateTime(new Date());
                }};
                userService.addUser(user);
                System.out.println(user);
                // User(id=13, username=test1694675733277, password=123456, salt=null, token=null, isEnabled=null, createTime=Thu Sep 14 15:15:33 CST 2023, modifiedTime=null)
                transactionManager.commit(transaction);
            }catch (Exception e){
                e.printStackTrace();
                transactionManager.rollback(transaction);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    声明式事务

    声明式事务的实现,只需要在方法上添加 @Transactional 注解就可以实现,无序手动开启事务和提交事务,进入方法时自动开启事务,方法执行完全会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务

    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
    
        @Transactional(rollbackFor = Exception.class)
        @Override
        public void addUser(User user) {
            userMapper.insertSelective(user);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @Transactional 参数设置
    参数作用
    value当你配置多个事务管理器时,可以使用该属性指定选择用哪个事务管理器
    transactionManager同上
    propagation事务的传播行为,默认值为 Propagation.REQUIRED
    isolation事务的隔离级别,默认值为 Isolation.DEFAULT
    timeout事务的超时时间,默认值为-1,如果超过该时间限制但事务还没完成,则自动回滚事务
    readOnly指定事务是否为只读事务,默认值为 false,为了忽略那些不需要事务的方法,比如读取数据可以设置 read-only 为 true
    rolibackFor用于指定能够触发事务回滚的异常类型,可以指定多个异常类型
    rolibackForClassName同上
    noRolibackFor抛出指定的异常类型,不回滚事务,也可以指定多个异常类型
    noRollbackForClassName同上
    @Transactional的失效场景
    1. @Transactional标记的方法,非public修饰
    2. @Transactional标记的方法所在内,非Spring容器管理的bean
    3. @Transactional标记的方法,被类内部方法调用
    4. @Transactional 声明式事务默认只处理运行时异常,非运行时异常需要指定异常类型,例如@Transactional(rollbackFor = Exception.class)
    5. 异常被try catch语句块捕获,未抛出异常(最难被排查到问题且容易忽略)
    6. @Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)
    7. 数据库存储引擎不支持事务,如Mysql关系型数据库,如果选择的存储引擎是MyISAM而非InnoDB,则事务会不起作用(基本开发中不会遇到);
    1 @Transactional标记的方法,非public修饰
    @Component
    public class TestServiceImpl {
        @Resource
        TestMapper testMapper;
        
        @Transactional
        void insert() {
            int re = testMapper.insert(new Test(10,20,30));
            if (re > 0) {
                throw new Exception("need intercept");
            }        
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2 @Transactional标记的方法,被类内部方法调用
    @Component
    public class TestServiceImpl {
        @Resource
        TestMapper testMapper;
        
        @Transactional
        public void insert() {
            int re = testMapper.insert(new Test(10,20,30));
            if (re > 0) {
                throw new Exception("need intercept");
            }        
        }
        
        @Transactional
        public void update() {
            testMapper.update(new Test(10,20,30));    
        }
        
        public void handle(){
            update();
            insert();
        }
    }
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class DemoApplicationTests {
       @Resource
       TestServiceImpl testServiceImpl;
    
       @Test
       public void  testInvoke(){
          testServiceImpl.handle();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    3 异常被try catch语句块捕获,未抛出异常
    @Component
    public class TestServiceImpl {
        @Resource
        TestMapper testMapper;
        
        @Transactional
        void insert() {
            try{
                int re = testMapper.insert(new Test(10,20,30));
                if (re > 0) {
                    throw new Exception("need intercept");
                }    
            }catch(Exception e){
                // 打印 不抛出新异常
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    Linux 多线程编程详解
    vuepress(六)阿里云二级域名配置与添加SSL证书
    java 工程管理系统源码+项目说明+功能描述+前后端分离 + 二次开发
    章鱼网络 Community Call #19|​开启与 Eigenlayer 的合作
    主辅助服务市场出清模型研究【旋转备用】(Matlab代码实现)
    webpack实战:某网站RSA登录加密逆向分析
    redis大key优化
    红队|域渗透重要漏洞总结
    Math 对象+JS数组求和的常用方法+ indexOf+js计算数组中每个元素出现的次数+while循环语句死循环
    CFD模拟笔记
  • 原文地址:https://blog.csdn.net/qq_35987023/article/details/132908571