zoukankan      html  css  js  c++  java
  • Spring事务传播机制详解

    Spring的事务,也就是数据库的事务操作,符合ACID标准,也具有标准的事务隔离级别。

        但是Spring事务有自己的特点,也就是事务传播机制。

        所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的,是重新创建事务还是使用父方法的事务?父方法的回滚对子方法的事务是否有影响?这些都是可以通过事务传播机制来决定的。

        本文就测试一下这些事务传播机制的使用及异同

    1.准备测试方法

        主要是创建两个service接口(接口主要是对数据库表的操作),并创建其实现类

        1)创建beans.xml,开启事务

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <tx:annotation-driven transaction-manager="transactionManager"/>
    </beans>


       2)创建实体类和表(表创建读者可自定义创建)

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Blog {
        private int id;
        private String name;
        private String ur;
    }


        3)创建service接口(BlogService和BlogService2,主要是对Blog的不同操作)

    // BlogService
    package jdbc;
    // 主要负责Blog的添加和修改
    public interface BlogService {
        void save(Blog blog);
        void update(Blog blog);
    }
     
    // BlogService2
    package jdbc;
    // 主要负责Blog的删除
    public interface BlogService2 {
        void delete(int id);
    }


        4)创建其实现类(BlogServiceImpl,BlogService2)

    @Transactional(propagation=Propagation.REQUIRED)
    @Component
    public class BlogServiceImpl implements BlogService {
     
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Autowired
        private BlogService2 blogService2;
        
        @Override
        public void save(Blog blog) {
     
            String sql = "insert into blog values(?,?,?)";
            jdbcTemplate.update(sql,
                new Object[]{blog.getId(),blog.getName(),blog.getUr()},
                new int[]{java.sql.Types.INTEGER,java.sql.Types.VARCHAR,java.sql.Types.VARCHAR});
            
            blogService2.delete(16);
    //        update(blog);
    //        throw new RuntimeException("error");
            
        }
        
        @Override
        public void update(Blog blog){
            
            String sql = "update blog set name = ? where id=?";
            jdbcTemplate.update(sql, new Object[]{blog.getName(),blog.getId()},
                    new int[]{java.sql.Types.VARCHAR,java.sql.Types.INTEGER});
        }
    }

     BlogService2.java

    @Transactional(propagation=Propagation.REQUIRED)
    @Component
    public class BlogServiceImpl2 implements BlogService2 {
     
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Override
        public void delete(int id){
            
            String sql = "delete from blog where id=?";
            jdbcTemplate.update(sql, id);
        }
    }

        注意:既然要实现多事务的传播,就需要在一个方法里调用另一个类的方法,下面的测试就是基于这种方法,在BlogService的save()方法中调用BlogService2的delete()方法

        5)创建Configuration类,用于创建DataSource实现

    @Configuration
    @ComponentScan(basePackages={"jdbc"})// 扫描BlogService实现类所在的包路径
    @ImportResource(locations={"classpath:beans.xml"})// 添加事务管理
    public class JdbcConfig {
     
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
        
        @Bean
        public DataSourceTransactionManager transactionManager(DataSource dataSource){
            return new DataSourceTransactionManager(dataSource);
        }
        
        @Bean
        public DataSource dataSource(){
            try {
                return new SimpleDriverDataSource(new com.mysql.jdbc.Driver(), "jdbc:mysql://localhost:3306/test", "root", "root");
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    }

        6)测试

    public class Test {
     
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(JdbcConfig.class);
            BlogService service = ac.getBean(BlogService.class);
            
            Blog b = new Blog(18,"lili","url");
            service.save(b);
        }
    }


    总结:大体的测试框架就如上所示,下面的测试修改主要是修改BlogServiceImpl和BlogServiceImpl2的事务传播机制@Transactional(propagation=Propagation.REQUIRED)

    3.事务传播机制的测试

        1)REQUIRED

        定义:如果有事务则加入事务,如果没有事务,则创建一个新的(默认值)

        操作1:将BlogServiceImpl和BlogServiceImpl2的事务传播机制都修改为

    @Transactional(propagation=Propagation.REQUIRED)

        结果1:

     

        操作2:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.REQUIRED)

        结果2:

     

        总结:

            当BlogServiceImpl提供事务的时候,BlogServiceImpl2的方法执行使用当前已有事务,不再新建事务;

            当BlogServiceImpl不创建事务的时候,BlogServiceImpl2的方法执行发现没有事务可用,自己新建事务;

        

        2)NOT_SUPPORTED

        定义:Spring不为当前方法开启事务,相当于没有事务

        操作:将BlogServiceImpl和BlogServiceImpl2的事务传播机制都

    修改为@Transactional(propagation=Propagation.NOT_SUPPORTED)

        结果:

     

        总结:

            NOT_SUPPORTED相当于没有Spring事务,每条执行语句单独执行,单独提交

        3)REQUIRES_NEW

        定义:不管是否存在事务,都创建一个新的事务,原来的方法挂起,新的方法执行完毕后,继续执行老的事务

        操作:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.REQUIRES_NEW)

        结果:

     

        总结:

            REQUIRES_NEW为当前方法创建一个新的事务,并且当前事务先提交,然后再提交老的事务

        4)MANDATORY

        定义:必须在一个已有的事务中执行,否则报错

      操作:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.MANDATORY),查看是否报错

        结果:

     

        总结:    

            MANDATORY必须在已有事务下被调用,否则报错

            NOT_SUPPORTED执行数据库层面的事务操作,故当前测试中,insert方法成功执行,delete方法的抛错并不影响insert方法的执行

        5)NEVER

        定义:必须在一个没有的事务中执行,否则报错

        操作:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.MANDATORY),查看是否报错

        结果: 

     

        总结:

            NEVER必须在没有事务的方法中执行,否则报错;

            save方法开启一个事务,还没来及提交发现delete方法报错,只能回滚事务

        6)SUPPORTS

        定义:如果其他bean调用这个方法时,其他bean声明了事务,则就用这个事务,如果没有声明事务,那就不用事务

        操作1:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.SUPPORTS)

        结果1:

     

        操作2:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.SUPPORTS)

        结果2:

     

        总结:

            SUPPORTS类型的事务传播机制,是否使用事务取决于调用方法是否有事务,如果有则直接用,如果没有则不使用事务

        7)NESTED

        定义:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作

        操作1:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.REQUIRED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.NESTED)

        结果1:

     

        操作2:将BlogServiceImpl事务传播机制修改为@Transactional(propagation=Propagation.NOT_SUPPORTED),BlogServiceImpl2的仍为@Transactional(propagation=Propagation.NESTED)

        结果2:

     

        总结:

            save方法创建一个事务,则再调用delete方法时,直接在该事务的基础上创建一个嵌套事务,本质上还是同一个事务,做一次提交;

            save方法不创建事务,则调用delete方法时,直接创建一个新的事务,单独提交

    4.注意事项

        1)REQUIRED

            当两个方法的传播机制都是REQUIRED时,如果一旦发生回滚,两个方法都会回滚

        2)REQUIRES_NEW

            当delete方法传播机制为REQUIRES_NEW,会开启一个新的事务,并单独提交方法,所以save方法的回滚并不影响delete方法事务提交

        3)NESTED

            当save方法为REQUIRED,delete方法为NESTED时,delete方法开启一个嵌套事务;

            当save方法回滚时,delete方法也会回滚;反之,如果delete方法回滚,则并不影响save方法的提交

  • 相关阅读:
    WPF 关于拖拽打开文件的注意事项
    asp.net core 3.1中对Mongodb BsonDocument的序列化和反序列化支持
    用百度webuploader分片上传大文件
    多线程学习笔记
    web.config数据库连接字符串加密
    Visual Studio 2010 常用快捷方式
    Team Foundation Server 2013 日常使用使用手册(四)分支与合并
    Team Foundation Server 2013 日常使用使用手册(三)上传新工程、创建任务、创建bug、设置预警
    Team Foundation Server 2013 日常使用使用手册(二)修改、签入、撤销、回滚、对比代码变更
    Team Foundation Server 2013 日常使用使用手册(一)-本地连接TFS、查看任务
  • 原文地址:https://www.cnblogs.com/alenblue/p/12900911.html
Copyright © 2011-2022 走看看