• Java 事务


    大家好呀,我是小笙,本节我分享一些事务有关的知识点

    事务

    概述

    编程式事务

    Connection connection = JdbcUtils.getConnection(); 
    try {
        //1. 先设置事务不要自动提交 
        connection.setAutoCommint(false); 
        //2. 进行各种多个表的操作
        // ...
        //3. 提交 
        connection.commit(); 
    } catch (Exception e) { 
        //4. 回滚
    	conection.rollback(); 
    }
    

    优点:简单,好理解

    缺点::代码冗余,效率低,不利于扩展

    声明式事务

    需要配置声明式注解

    
    
        
    
    
    
    
    

    我们如果需要 service 中某个方法执行事务的语句加上注解即可

    @Transactional
    public void serviceMethod(){}
    

    底层依旧是走了编程式事务的那一套,只是用 AOP 的方式嵌入事务开启、事务提交以及事务回滚

    // dataSourceTransactionManager 底层源码
    protected void doBegin(Object transaction, TransactionDefinition definition) {
        // ......
        // 核心代码 不开启自动提交事务
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }
    
            con.setAutoCommit(false);
        }
        // ......
    }
    
    // 提交事务
    protected void doCommit(DefaultTransactionStatus status) {
        DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {
            this.logger.debug("Committing JDBC transaction on Connection [" + con + "]");
        }
    
        try {
            con.commit();
        } catch (SQLException var5) {
            throw this.translateException("JDBC commit", var5);
        }
    }
    
    // 回滚事务
    protected void doRollback(DefaultTransactionStatus status) {
        DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        if (status.isDebug()) {
            this.logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
        }
    
        try {
            con.rollback();
        } catch (SQLException var5) {
            throw this.translateException("JDBC rollback", var5);
        }
    }
    

    优点:无代码冗余,效率高,扩展方便

    缺点:理解较困难

    事务的传播的属性/种类

    最主要的是 REQUIRED 和 REQUIRES_NEW,主要区别就是在当前运行的事务包裹的情况下是在该事务内部运行还是创建独立的事务运行

    传播属性描述
    *REQUIRED(默认)如果有事务在运行,当前的方法就在这个事务内运行;否则,就启动一个新的事务,并在自己的事务内运行
    *REQUIRES_NEW当前的方法必须启动新事务,并在它自己的事务内运行;如果有事务正在运行,应该将它挂起
    SUPPORTS如果有事务在运行,当前的方法就在这个事务内运行;否则它可以不运行在事务中
    NOT_SUPPORTEI当前的方法不应该运行在事务中,如果有运行的事务,将它挂起
    MANDATORY当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
    NEVER当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
    NESTED如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运 行;否则,就启动一个新的事务,并在它自己的事务内运行

    操作

    // 注解分析
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Transactional {
        @AliasFor("transactionManager")
        String value() default "";
    
        @AliasFor("value")
        String transactionManager() default "";
    
        String[] label() default {};
    
        Propagation propagation() default Propagation.REQUIRED; // 来设置哪种传播属性
    
        Isolation isolation() default Isolation.DEFAULT;
    
        int timeout() default -1;
    
        String timeoutString() default "";
    
        boolean readOnly() default false;
    
        Class<? extends Throwable>[] rollbackFor() default {};
    
        String[] rollbackForClassName() default {};
    
        Class<? extends Throwable>[] noRollbackFor() default {};
    
        String[] noRollbackForClassName() default {};
    }
    
    // 实际代码
    @Transactional(propagation = Propagation.REQUIRES_NEW)
     public void serviceMethod(){}
    

    事务隔离级别

    Java 默认的隔离级别, 就是 mysql 数据库默认的隔离级别 一般为 REPEATABLE_READ

    隔离级别脏读不可重复读幻读加锁读
    读未提交会发生会发生会发生不加锁
    读已提交不会发生会发生会发生不加锁
    可重复读不会发生不会发生不会发生不加锁
    可串行化不会发生不会发生不会发生加锁
    1. 脏读:当一个事务正在访问数据并且对数据进行了修改,但是还没有提交时,另一个事务读取到了修改之后的数据,但是前面的事务进行回滚了,此时就出现了脏读
    2. 不可重复读:一个事务在查询数据时另一个修改数据的事务刚好提交,再次查询时,两次查询结果不一样。(即不能读到相同的数据内容)
    3. 幻读:在一个事务的两次查询中数据不一致,例如:一个事务查询一次数据,在此时另一个事务插入了几条数据,当再次查询时出现了几条之前没有的数据,产生幻读

    不可重复读和幻读的区别

    可以理解为:不可重复读重点在于update和delete (只用行锁),而幻读的重点在于insert (必须表级锁)

    查看数据库默认的隔离级别

    SELECT @@global.tx_isolation;
    

    格式

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void serviceMethod(){}
    

    事务的超时回滚

    如果一个事务执行的时间超过某个时间限制,就让该事务回滚(单位:秒)

    格式

    // 通过抛出异常的形式,超时发生事务回滚
    @Transactional(timeout = 2)
    public void serviceMethod(){}
    
  • 相关阅读:
    Java Hbase查询接口条件正确查询不到踩坑记录,查询大小写不敏感,存储必须小写
    Lumiprobe 脱氧核糖核酸丨炔烃dT亚磷酰胺
    LeetCode【11】 盛水最多的容器
    设计模式——模板方法模式
    安全授权:从 RBAC 到 OAuth2 的最佳实践
    redis(0)-安装实操
    使用RoboBrowser和Python下载音频
    QT+OSG/osgEarth编译之四十二:osgDB+Qt编译(一套代码、一套框架,跨平台编译,版本:OSG-3.6.5核心库osgDB)
    记录pytorch安装 windows10 64位--第1部分:cuda和cuddn
    Unity的IPreprocessComputeShaders:深入解析与实用案例
  • 原文地址:https://blog.csdn.net/Al_tair/article/details/127098304