尚硅谷 Spring视频学习笔记 spring-Transaction事务

读问题 案例截取
视频地址

五、事务概念

1、什么是事务

  • 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
  • 典型场景:银行转账(A钱少100,B多100, 但是A少100时断电,则需要使用事务机制!两者都失败)

2、事物的四个特性(ACID)

  • 原子性;要么都成功。要么都失败。
  • 一致性:操作之前和操作之后总量不变:A,B个有100;A转100给B;A0,B200;结果总量不变。
  • 隔离性:多事务操作之间不会相互影响。
  • 持久性:事务提交之后,表中数据才发生变化。

3、事务操作(搭建事务操作环境)

3.1 不开启事务,在转账业务出现问题

(1)创建数据表,添加记录

在这里插入图片描述
在这里插入图片描述

(2)创建Service,搭建Dao,完成对象创建和注入关系。

Service 注入 Dao,Dao注入JdbcTemplate,JdbcTemplate注入DataSource

@Service
public class UserService {
    // 注入dao
    @Autowired
    private UserDao userDao;
}

@Repository
public class UserDaoImpl implements UserDao{
    // 注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
(3)在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账方法)
@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addMoney() {
        String sql = "update account set money=money+? wherer username=?";
        jdbcTemplate.update(sql, 100, "mary");
    }

    @Override
    public void reduceMoney() {
        String sql = "update account set money=money-? wherer username=?";
        jdbcTemplate.update(sql, 100, "lucy");
    }
}

@Service
public class UserService {
    // 注入dao
    @Autowired
    private UserDao userDao;

    // 转账方法
    public void accountMoney(){
        userDao.reduceMoney();
        userDao.addMoney();
    }
}
(4)测试类
@Test
public void testAccount(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}
(5)上面代码,如果正常秩序没有问题的,但是如果代码执行过程中出现异常,有问题
public void accountMoney(){
    userDao.reduceMoney();

    // 模拟异常
    int i = 10/0;

    userDao.addMoney();
}

3.2 问题处理解决

  • 第一步 开启事务
  • 第二步 进行业务操作
  • 第三步 没有发生异常,事务提交
  • 第四步 出现异常,事件回滚

4、事务操作(Spring事务管理介绍)

(1)事务添加到javaEE三层结构里面Service层(业务逻辑层)

(2)Spring进行事务管理操作 两类

  • 编程式事务管理:按照3.2解决:第一步到第四步一步步执行
  • 声明式事务管理(使用):在开发一般使用声明式管理,通过配置/注解的方式完成。

(3)声明式事务管理

  • 基于注解方式(使用)
  • 基于xml配置文件方式

(4)在Spring进行声明式事务管理底层使用AOP原理

(5)Spring事务管理API

  • 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
    在这里插入图片描述

5、事务操作(注解声明式事务管理)

(1)在Spring配置文件,配置事务管理器,开启事务注解

  • 在Spring配置文件引入名称空间 xmlns:tx....

    <?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"
           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/context http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    ">
    
    <beans>    
        <!--数据源的配置-->
        ......
        
        <!--创建事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!--开启事务注解-->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    </beans>
    

(2)在Service类上面(获取service类里面方法上面)添加事务注解

  • @Transactional 这个注解添加到类上面,也可以添加方法上面

  • 如果把这个注解添加到类上面,这个类里面所有的方法都添加事务(一般使用)

  • 如果把这个注解添加到方法上面,为这个方法添加事务

    @Service
    @Transactional
    public class UserService {
    }
    

6、事务操作(声明式事务管理参数配置)

(1)在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数,参数如下:
在这里插入图片描述

6.1 propagation:事务传播行为**

传播行为:多事务方法之间进行调用,这个过程中事务是如何进行管理的

事务的7中传播行为:懂前俩个,后面的了解,默认的传播行为是REQUIRED

@Service
@Transactional(propagation = Propagation.REQUIRED)

在这里插入图片描述

当没有事务调用由事务方法时,有事务的方法执行自身的事务。

当有事务的调用其他方法,其他方法会加入到该事务方法中,无论是事务方法,还是调用方法出现异常,两者都要进行回滚。

7中事务传播属性:

在这里插入图片描述

6.2 ioslation:事务隔离级别**

解决事务间的隔离性(并发操作中产生的问题),而出现的隔离级别的概念

事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题

**(1)三个读问题:**脏读、不可重复读、虚(幻)读

1)脏读

一个未提交事务读取到另一个未提交事务的数据。(读取未提交数据)

在这里插入图片描述
在这里插入图片描述
案例截取:https://cloud.tencent.com/developer/article/1450773

2)不可以重复读:

一个未提交事务读取到另一提交事务中修改的数据 (数据内容不一致)

**例子:**事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。
在这里插入图片描述
https://cloud.tencent.com/developer/article/1450773

3)虚读:

一个未提交事务读取到另一个提交事务添加数据(数据总量不一致)

​ 事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

在这里插入图片描述
https://cloud.tencent.com/developer/article/1450773

不可重复读和幻读到底有什么区别呢?

(1) 不可重复读是读取了其他事务更改的数据,针对update操作

解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。

(2) 幻读是读取了其他事务新增的数据,针对insert和delete操作

解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。

这时候再理解事务隔离级别就简单多了呢。

(2)解决三个读问题:

通过设置事务隔离级别,解决读问题

(无:表示可以解决问题-网络截图)
image-20201015223909643

// 添加事务注解
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
// mysql默认的隔离级别为:REPEATABLE_READ

6.3 timeout:超时时间

  • 事务需要在一定时间内进行提交,如果不提交进行回滚
  • 默认值:-1(默认不超时),且可设置时间以秒单位进行计算
@Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

6.4 readOnly:是否只读

  • 读:查询操作,写:添加修改删除操作
  • readOnly默认值false,表示可以查询,可以添加修改删除操作,设置readOnly值为true后,只能查询
@Transactional(readOnly = true,timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

6.5 rollbackFor:回滚

  • 设置出现哪些异常进行事务回滚

6.6 noRollbackFor:不回滚

  • 设置出现哪些异常不进行事务回滚

7、事务操作(XML声明式事务管理 了解+)

(1)在Spring配置文件中进行配置

  • 第一步 配置事务管理器

    <!--开启组件扫描-->
    <!--Jdbc Template对象配置-->
    
    <!--1. 创建事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--注入数据源-->
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
  • 第二步 配置通知

        <!--2. 配置通知-->
        <tx:advice id="txadvice">
            <!--配置事务参数-->
            <tx:attributes>
                <!--指定那种规则的方法上面添加事务 accountMoney为方法名, 也可以写成account*-->
                <tx:method name="accountMoney" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
    
  • 第三步 配置切入点和切面

        <!--3. 配置切入点和切面-->
        <aop:config>
            <!--配置切入点-->
            <aop:pointcut id="pt" expression="execution(* cn.spring5.Aop.Service.UserService.*(..))"/>
            <!--配置切面-->
            <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
        </aop:config>
    

8、事务操作(完全注解声明式事务管理)

(1)创建配置类,使用配置类替代xml配置文件

@Configuration // 配置类
@ComponentScan(basePackages = "cn.spring5.packageName") // 组件扫描
@EnableTransactionManagement // 开启事务
public class TxConfig {
    // 创建数据库连接池
    @Bean //在配置类中创建Bean实例的注释@Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring5");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    //创建JdbcTemplate对象
    // 形参DataSource dataSource:因为getDruidDataSource()方法在IOC容器中已经创建了dataSource对象,所以形参这边是根据类型注入dataSource属性。
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 注入DataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    // 创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

(2)测试类

    @Test
    public void testAccountConfig(){
        ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值