zoukankan      html  css  js  c++  java
  • SpringBoot事务简单操作及手动回滚

    一、引入依赖

    <!-- 核心启动器, 包括auto-configuration、logging and YAML -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!-- using Spring Data JDBC, JdbcTemplate或NamedParameterJdbcTemplate都是由spring jdbc提供的 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    <!-- 数据库操作需要的mysql 驱动包 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.48</version>
    </dependency>
    
    <!-- testing Spring Boot applications with libraries including JUnit, Hamcrest and Mockito -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    二、application.properties

    spring.datasource.url=jdbc:mysql://192.168.178.5:12345/mydb?characterEncoding=UTF-8
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.type=com.zaxxer.hikari.HikariDataSource

    三、dao和service代码

    1. dao

    student接口

    public interface StudentDao {
        void saveStudent();
    }

    student实现类

    @Repository
    public class StudentDaoImpl implements StudentDao{
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void saveStudent(){
            String sql = "insert into student(name, s_class) values(?,?"; //这里会出现一个错误,少了右括号
            List<Object> sqlParamList = new ArrayList<Object>();
            sqlParamList.add("stn-"+Math.random());
            sqlParamList.add(20);
            jdbcTemplate.update(sql, sqlParamList.toArray(new Object[sqlParamList.size()]));
        }
    }

    user接口

    public interface UserDao {
        void saveUser();
    }

    user实现类

    @Repository
    public class UserDaoImpl implements UserDao{
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void saveUser(){
            String sql = "insert into user(name, age) values(?,?)";
            List<Object> sqlParamList = new ArrayList<Object>();
            sqlParamList.add("sn-"+Math.random());
            sqlParamList.add(20);
            jdbcTemplate.update(sql, sqlParamList.toArray(new Object[sqlParamList.size()]));
        }
    }

    2. service

    接口:

    public interface OperatorService {
        void saveEntity() throws Exception;
    }

    接口实现类

    @Service
    public class OperatorServiceImpl implements OperatorService{
    
        @Autowired
        private StudentDao studentDao;
    
        @Autowired
        private UserDao userDao;
    
        @Override
        public void saveEntity() throws Exception{
            userDao.saveUser(); 
            studentDao.saveStudent();
        }
    }

    当执行以上saveEntity()代码时,因StudentDaoImpl 插入语句的一个错误,会导致事务不一致,user表成功插入一条记录,student表没有。

    为了使事务一致,在SpringBoot项目中,我们只需要在saveEntity()上添加@Transactional注解,,对@Transactional的注解可以查看 对注解@Transactional的解读 一节。

    这里为了适应更多的异常,我们提升了事务捕获异常的范围:@Transactional(rollbackFor = Exception.class)

    四、手动回滚事务

    有时我们需要捕获一些错误信息,又需要进行事务回滚,这时我们就需要用到Spring提供的事务切面支持类TransactionAspectSupport。

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveEntity() throws Exception{
        try {
            userDao.saveUser();
            studentDao.saveStudent();
        }catch (Exception e){
            System.out.println("异常了=====" + e);
            //手动强制回滚事务,这里一定要第一时间处理
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    手动回滚事务一定要加上@Transactional,不然会报以下错误:

    org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope

    想想也是,不开启事务,何来手动回滚,所以@Transactional必不可少。

    五、回滚部分异常

    使用Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 设置回滚点。
    使用TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint); 回滚到savePoint。

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveEntity() throws Exception{
        Object savePoint = null;
        try {
            userDao.saveUser();
            //设置回滚点
            savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
            studentDao.saveStudent(); //执行成功
            int a = 10/0; //这里因为除数0会报异常,进入catch块
        }catch (Exception e){
            System.out.println("异常了=====" + e);
            //手工回滚异常
            TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
        }
    }

    六、使用DataSourceTransactionManager

    springboot 开启事务以及手动提交事务,可以在服务类上加上两个注解。

    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;

    手动开启事务
    TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    手动提交事务
    dataSourceTransactionManager.commit(transactionStatus);//提交
    手动回滚事务
    dataSourceTransactionManager.rollback(transactionStatus);//最好是放在catch 里面,防止程序异常而事务一直卡在哪里未提交

    七、spring boot controller设置 @Transactional 不回滚的解决办法

    默认spring事务只在发生未被捕获的 RuntimeException 时才回滚。  
    spring aop  异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获 RuntimeException 的异常,但可以通过配置来捕获特定的异常并回滚。
    换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new RuntimeException (),这样程序异常时才能被aop捕获进而回滚。

    解决方案:

    方案1:例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理。
    方案2:在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常。

    注意:

    1. 默认地,如果使用的数据源不是SpringBoot的默认配置(即是由自己定义的配置信息,自己解析创建的数据源),则需要手动创建事务管理器,因为SpringBoot无法识别配置信息,无法完成自动注入。

        //DynamicDataSource 是自定义的数据源 
        @Bean
        public PlatformTransactionManager transactionManager(DynamicDataSource dataSource){
            return  new DataSourceTransactionManager(dataSource);
        }

    2. SpringBoot1.x需要在启动类上添加@EnableTransactionManagement,SpringBoot2.x则不需要。

  • 相关阅读:
    boost::asio在VS2008下的编译错误
    Java集合框架——接口
    ACM POJ 3981 字符串替换(简单题)
    ACM HDU 1042 N!(高精度计算阶乘)
    OneTwoThree (Uva)
    ACM POJ 3979 分数加减法(水题)
    ACM HDU 4004 The Frog's Games(2011ACM大连赛区第四题)
    Hexadecimal View (2011ACM亚洲大连赛区现场赛D题)
    ACM HDU 4002 Find the maximum(2011年大连赛区网络赛第二题)
    ACM HDU 4001 To Miss Our Children Time (2011ACM大连赛区网络赛)
  • 原文地址:https://www.cnblogs.com/smallfa/p/13683332.html
Copyright © 2011-2022 走看看