程序员

Spring声明式事务

2020-04-22  本文已影响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();
    }
}

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

然后在进行操作的方法(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配置,不重要的使用注解配置

上一篇 下一篇

猜你喜欢

热点阅读