美文网首页程序员
Spring声明式事务

Spring声明式事务

作者: 小杨小杨神采飞杨 | 来源:发表于2020-04-22 23:10 被阅读0次

    模拟一个买书结账的环境
    环境搭建:
    bookDao类,与数据库交互

    @Repository
    public class bookDao {
        @Autowired
        private JdbcTemplate template;
    
        //根据isbn获取价格
        public int getPrice(String isbn) {
            String sql = "select price from book where isbn = ?";
            Integer price = template.queryForObject(sql, Integer.class, isbn);
            return price;
        }
    
        //根据价格减少钱
        public void reduceMoney(int price,String username) {
            String sql = "update account set balance = balance - ? where username = ?";
            template.update(sql,price,username);
        }
    
        //根据isbn减少库存
        public void reduceStock(String isbn,int num) {
            String sql = "update book_stock set stock = stock - ? where isbn = ?";
            template.update(sql,num,isbn);
        }
    }
    

    bookService类,逻辑操作

    @Service
    public class bookService {
        @Autowired
        private bookDao dao;
    
        //实现结账操作
        public void pay() throws IOException {
            Scanner s = new Scanner(System.in);
            System.out.print("请输入您的姓名:");
            String username = s.next();
            System.out.print("请输入购买的书籍编号:");
            String isbn = s.next();
            System.out.print("请输入购买数量:");
            int num = s.nextInt();
    
            System.out.println("正在结账...");
    
            int price = dao.getPrice(isbn);
            dao.reduceMoney((price*num),username);
            dao.reduceStock(isbn,num);
    
            System.out.println("购买成功");
        }
    }
    

    ioc容器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:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--扫描指定包下配置了注解的bean-->
        <context:component-scan base-package="cn.yzx.dao"/>
        <context:component-scan base-package="cn.yzx.service"/>
        <!--开启注解配置-->
        <context:annotation-config/>
        <!--引入外部配置文件-->
        <context:property-placeholder location="classpath:db.properties"/>
        
        <!--获取druid数据库连接池-->
        <bean id="DataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${driverClassName}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${password}"/>
        </bean>
    
        <!--注册JdbcTemplate-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <constructor-arg name="dataSource" ref="DataSource"/>
        </bean>
    </beans>
    

    测试类

    public class mytest {
        public static void main(String[] args) throws IOException {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            bookService bean = context.getBean(bookService.class);
            bean.pay();
        }
    }
    

    实现需求,用户买了书,根据买书的数量以及书的价格,减少用户的钱,以及书的库存

    • 无事务
      显而易见一个很严重的问题,如果在减少用户钱、库存减少两次操作中,后执行的一个除了错误,则前一个执行的结果无法撤回,比如,我这里是用户先付钱再减少库存,在减少库存的sql语句中人为的制造错误

      制造错误
      此时,钱已经减少了,但是库存因为错误,并没有减少
      运行结果
      用户的钱
      书的库存
      此时,我们就需要使用事务,事务可以保证所有操作都正常执行数据库数据才会发生改变,之前我们使用的是手动的事务,较为麻烦,且在本案例中有多次数据库连接,要甄别他们也十分麻烦,而spring的AOP功能与事务的步骤基本吻合,所以可以使用AOP来实现事务,但是自己写AOP也很麻烦,所以spring为我们写好的基于AOP的声明式事务
    • 使用声明式事务
      首先修改IOC容器的配置文件,添加一下几行


      xml

      注意,使用tx需要导入tx的名称空间依赖

    然后在进行操作的方法(Service层)上,使用@Transactional开启声明式事务


    @Transactional

    再次执行,依然报错


    结果
    但此时用户的钱没有减少
    用户的钱

    书的库存也没有减少


    书的库存

    声明式事务的一些细节

    声明式事务的注解中,有许多参数可以填入

    1. isolation:事务隔离级别 (重要)
    isolation

    首先了解概念

    为什么需要设置隔离级别?

    为什么需要设置隔离级别 脏读演示

    设置为读已提交隔离级别,虽然解决了读脏数据的问题,但是没有解决一次失误事务读取到的数据不同的问题

    不可重复读演示

    设置为可重复读级别,mysql在此级别下,已经解决了所有问题

    事务2的操作不影响事务1的读取结果
    所以一般都设置为可重复读级别,默认也是该级别

    隔离级别分为?


    隔离级别 隔离级别支持

    其中mysql在可重复读隔离级别下就可以防止所有问题了,表中给出的信息是错误的

    设置隔离级别
    1. propagation:事务传播行为 (重要)
    propagation

    解释:当有多个事务嵌套运行时,若子事务出现异常,父事务是否要回滚?其他子事务是否要回滚?这就是事务传播,可以通过propagation属性进行设置

    7种传播行为
    挂起:暂停
    一二对应(常用),三四五六一一对应,七独立
    设置传播行为
    图示
    注意:子事务如果是REQUIRED传播方式,那么子事务的所有配置,例如超时、设置回滚不回滚之类的全都不生效,只继承上一级事务的配置
    1. readOnly:设置事务为只读事务,不能增删改,可以加快查询速度,因为不涉及增删改,则无关事务,就不用管事务的操作了


      readOnly
    测试 测试结果
    1. timeout:事务超出指定时间后,自动终止并回滚


      timeout
    测试 测试结果

    异常相关

    异常:
    运行时异常:声明式事务默认在发生运行时异常时回滚
    编译时异常:需要try抓取或throws抛出的异常,声明式事务默认在发生运行时异常时不回滚
    所以就需要设置以下四个属性,来设置那些属性需要回滚,那些不需要

    1. rollbackFor:指定那些异常事务需要回滚 (常用)
    rollbackFor

    可以让原本不回滚的异常回滚
    例如:IO流读取文件不存在异常,不会触发事务的回滚,但设置后就会触发


    手动添加文件读取异常
    测试结果

    rollbackFor指定该异常回滚


    指定回滚
    测试结果
    1. noRollbackFor:指定那些异常事务不回滚 (常用)
    noRollbackFor

    可以让原本回滚的异常不回滚
    例如数学异常,会触发事务的回滚,指定后就不会触发
    手动设置数学异常


    设置异常

    测试


    测试结果
    设置不回滚
    设置数学异常不回滚
    测试
    运行前库存
    运行后库存

    可见未回滚
    此处要注意一个点:运行时异常默认是回滚的,设置不回滚后,若异常发生在修改sql语句之前,依然不会执行sql语句,因为运行时异常发生后,java程序会停止运行,所以要测试此异常,需要将异常放到所有sql语句之后


    放到sql语句之后
    1. rollbackForClassName:指定那些异常事务需要回滚by异常全类名


      rollbackForClassName

    效果与rollbackFor一致,只是参数是全类名,更麻烦,不常用

    1. noRollbackForClassName:指定那些异常事务不回滚by异常全类名


      noRollbackForClassName

    效果与noRollbackFor一致,只是参数是全类名,更麻烦,不常用

    使用xml配置声明式事务

    xml

    还是一个原则:重要的使用xml配置,不重要的使用注解配置

    相关文章

      网友评论

        本文标题:Spring声明式事务

        本文链接:https://www.haomeiwen.com/subject/nnfwihtx.html