• 事务属性:只读、超时时间、事务异常、事务隔离级别、事务传播行为


    1. 事务属性:只读

    1.1 只读介绍

    对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。

    1.2 设置方式

    // readOnly = true把当前事务设置为只读 默认是false!
    @Transactional(readOnly = true)
    
    • 1
    • 2

    1.3 针对DML动作设置只读模式

    会抛出下面异常:

    Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

    1.4 @Transactional注解放在类上

    1.4.1 生效原则

    如果一个类中每一个方法上都使用了 @Transactional 注解,那么就可以将 @Transactional 注解提取到类上。反过来说:@Transactional 注解在类级别标记,会影响到类中的每一个方法。 同时,类级别标记的 @Transactional 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 @Transactional 注解。

    对一个方法来说,离它最近的 @Transactional 注解中的事务属性设置生效。

    1.4.2 用法举例

    在类级别@Transactional注解中设置只读,这样类中所有的查询方法都不需要设置@Transactional注解了。因为对查询操作来说,其他属性通常不需要设置,所以使用公共设置即可。

    然后在这个基础上,对增删改方法设置@Transactional注解 readOnly 属性为 false。

    @Service
    @Transactional(readOnly = true)
    public class EmpService {
        
        // 为了便于核对数据库操作结果,不要修改同一条记录
        @Transactional(readOnly = false)
        public void updateTwice(……) {
        ……
        }
        
        // readOnly = true把当前事务设置为只读
        // @Transactional(readOnly = true)
        public String getEmpName(Integer empId) {
        ……
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2 事务属性:超时时间

    2.1 需求

    事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。

    此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。

    概括来说就是一句话:超时回滚,释放资源。

    2.2 设置超时时间

    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         */
        @Transactional(readOnly = false,timeout = 3)
        public void changeInfo(){
            studentDao.updateAgeById(100,1);
            //休眠4秒,等待方法超时!
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            studentDao.updateNameById("test1",1);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3. 事务属性:事务异常

    3.1 默认情况

    默认只针对运行时异常回滚,编译时异常不回滚。情景模拟代码如下:

    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         */
        @Transactional(readOnly = false,timeout = 3)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! 
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.2 设置回滚异常

    rollbackFor属性:指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚!

    /**
     * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
     * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
     * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
     */
    @Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class)
    public void changeInfo() throws FileNotFoundException {
        studentDao.updateAgeById(100,1);
        //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内! 
        new FileInputStream("xxxx");
        studentDao.updateNameById("test1",1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.3 设置不回滚的异常

    在默认设置和已有设置的基础上,再指定一个异常类型,碰到它不回滚。

    noRollbackFor属性:指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!

    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         */
        @Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4. 事务属性:事务隔离级别

    4.1 事务隔离级别

    数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:

    1. 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
    2. 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
    3. 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
    4. 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
      不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整。

    4.2 事务隔离级别设置

    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         * isolation = 设置事务的隔离级别,mysql默认是repeatable read!
         */
        @Transactional(readOnly = false,
                       timeout = 3,
                       rollbackFor = Exception.class,
                       noRollbackFor = FileNotFoundException.class,
                       isolation = Isolation.REPEATABLE_READ)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    
    • 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

    5. 事务属性:事务传播行为

    5.1 事务传播行为要研究的问题

    举例代码:

    @Transactional
    public void MethodA(){
        // ...
        MethodB();
        // ...
    }
    
    //在被调用的子方法中设置传播行为,代表如何处理调用的事务! 是加入,还是新事务等!
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void MethodB(){
        // ...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.2 propagation属性

    @Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是:

    Propagation propagation() default Propagation.REQUIRED;
    
    
    • 1
    • 2

    propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供:

    名称含义
    REQUIRED 默认值如果父方法有事务,就加入,如果没有就新建自己独立!
    REQUIRES_NEW不管父方法是否有事务,我都新建事务,都是独立的!
  • 相关阅读:
    MySQL数据库详解 一:安装MySQL数据库及基本管理
    Codeforces Round 897 (Div. 2)
    数据结构——堆排序
    kubeedge v1.17.0部署教程
    第三章 常用布局
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第3章 Vue.js生命周期函数 3.3 销毁期间生命周期函数 && 3.4 扩展
    Unity实现动态数字变化
    websocket逆向-protobuf序列化与反序列化
    理解Nginx反向代理详解
    Linux_应用篇(08) 信号-基础
  • 原文地址:https://blog.csdn.net/Kaiser__/article/details/136599401