【Spring5】事务详解

文章目录

  • 事务环境配置
    • 配置文件
    • 创建表并添加数据
    • 创建service,搭建dao,完成对象创建和注入
    • 分析
    • 解决
  • Spring 事务管理介绍
    • 注解方式实现声明式事务管理
    • 声明式事务管理参数配置
      • 参数说明
    • XML配置文件方式实现声明式事务管理
    • 完全注解方式

银行转账大家都不陌生,银行转账就是一个典型的事务操作。转账要么成功,要么失败,中间有一个错误就会有导致转账失败。

事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败

事务四个特性(ACID)

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

就以上面的银行转账为例我们来解释Spring中的事务相关内容

事务环境配置

配置文件

事务是数据库操作最基本单元,因此首先肯定要连接数据库啦,修改配置文件

druidbean.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    
    <context:component-scan base-package="com.amhu.yx.spring">context:component-scan>

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3308/springdb" />
        <property name="username" value="root" />
        <property name="password" value="" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    bean>

    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        
        <property name="dataSource" ref="dataSource">property>
    bean>

beans>

创建表并添加数据

在这里插入图片描述

创建service,搭建dao,完成对象创建和注入

service 种注入dao
UserService

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

在dao注入JdbcTemplate
UserDaoImpl

@Repository
public class UserDaoImpl implements UserDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

在dao中创建数据库中增加和减少钱的方法,在service中创建转账的方法(调用dao中方法)
UserDao

public interface UserDao {
    void outCount();
    void entryCount();
}

UserDaoImpl

@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //从出账账户中扣钱
    @Override
    public void outCount() {
        String sql = "update user set money=money-? where username=?";
        jdbcTemplate.update(sql,100,"jack");
    }
    
    //从入账账户中加钱
    @Override
    public void entryCount() {
        String sql = "update user set money=money+? where username=?";
        jdbcTemplate.update(sql,100,"tom");
    }
}

``

@Service
public class UserService {

    //注入dao
    @Autowired
    private UserDao userDao;

    //转账的方法
    public void accountMoney() {
        userDao.outCount();//jack少了100块钱
        userDao.entryCount();//tom增加了100块钱
    }
}

测试方法:testAccountMoney

 @Test
    public void testAccountMoney() {
        ApplicationContext context = new ClassPathXmlApplicationContext("druidbean.xml");
        UserService userService= context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

结果:
在这里插入图片描述

分析

上面的代码看似没有问题,但是如果代码执行过程中出现异常,那么该怎么办呢?
我们在转账方法中模拟一个异常:
【Spring5】事务详解_第1张图片
这里出账方法是正常执行的,但是因为中间有一个异常,入账方法没有得到执行,运行测试代码:
在这里插入图片描述
可以看到jack少了100元,但是tom却没有增加钱,实际生活中,如果你转账的时候不小心出现异常,钱唰一下就没了,这谁顶得住?

解决

我们这里就用到了事务来解决这个问题:
将代码放在异常捕获中,如果出现异常,就进行事务回滚,比如上面将jack的钱退还给jack。

//转账的方法
public void accountMoney() {
    try {
        //第一步,开启事务

        //第二部 进行业务操作
        userDao.outCount();//jack少了100块钱

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

        userDao.entryCount();//tom增加了100块钱
        
        //如果无异常发生,提交事务
    }catch (Exception e){
        //出现异常,事务回滚
    }
}

Spring 事务管理介绍

1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2、在 Spring 进行事务管理操作有两种方式:

  • 编程式事务管理
  • 声明式事务管理(推荐使用)
    3、声明式事务管理
  • 基于注解方式(使用)
  • 基于 xml 配置文件方式
    4、在 Spring 进行声明式事务管理,底层使用 AOP 原理
    5、Spring 事务管理 API
    (1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
    在idea中任意一个地方输入
    在这里插入图片描述
    将鼠标放在中间,然后按CTRL + H 就可以查看相关类的继承结构图:
    【Spring5】事务详解_第2张图片

注解方式实现声明式事务管理

  1. 在Spring配置文件中配置事务管理器,就是创建DataSourceTransactionManager对象

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    
    <property name="dataSource " ref="dataSource">property>
bean>
  1. 在 spring 配置文件,开启事务注解
    (1)在 spring 配置文件引入名称空间 tx
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
                           
</beans>

(2)开启事务注解


<tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>

(3)在service类上(获取service类的方法上)添加事务注解@Transactional
在这里插入图片描述

  • @Transactional 既可以添加到类上,也可以添加到方法上
  • 如果注解添加到类上,表示该类中所有方法都添加事务
  • 如果注解添加到方法上,只是为这个方法添加事务

修改数据库中表:
在这里插入图片描述
重新开始运行测试代码,注意我们并没有修改代码,并且去掉刚刚加上的try-catch语句,恢复到之前的样子,与之前的区别只是添加了@Transactional 注解:
【Spring5】事务详解_第3张图片
再次运行,查看结果:
在这里插入图片描述
发现没有任何改变。

声明式事务管理参数配置

  1. 在service类上添加注解@Transactional,在这个注解里面可以配置事务相关参数
    【Spring5】事务详解_第4张图片

参数说明

  1. propagation : 事务传播行为
    多事务方法直接进行调用,这个过程中事务是如何进行管理的。
    【Spring5】事务详解_第5张图片
    事务方法:对数据库表数据进行变化的方法
    【Spring5】事务详解_第6张图片在这里插入图片描述

  2. ioslation : 事务隔离级别
    (1) 事务有特性称为隔离性,多十五操作之间不会产生影响,不考虑隔离性会产生多种问题
    (2) 有三个读问题

    • 脏读
      一个未提交事务读取到另一个未提交事务的数据。
    • 不可重复读
      一个未提交事务读取到已提交事务的数据
    • 虚(幻)读
      一个未提交的事务读取到另一提交事务添加的数据

(3)通过设置事务隔离级别,解决读问题
【Spring5】事务详解_第7张图片
mysql中默认级别是:REPEATABLE_READ
在这里插入图片描述

  1. timeout : 超时时间
    规定时间内事务未提交,进行回滚
    默认值是-1(不超时)
    自己设置以为单位

  2. readOnly : 是否只读
    读:查询操作
    写:添加修改删除操作
    readOnly默认值false
    若设置为true后,事务只能进行查询操作

  3. rollbackFor : 回滚
    设置出现中哪些异常进行事务回滚

  4. noRollbackFor : 不回滚
    设置出现中哪些异常不进行事务回滚

XML配置文件方式实现声明式事务管理

1、在 spring 配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置切入点和切面


<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

	<property name="dataSource" ref="dataSource">property>
bean>

<tx:advice id="txadvice">
	
	<tx:attributes>
	
	<tx:method name="accountMoney" propagation="REQUIRED"/>
	
	tx:attributes>
tx:advice>

<aop:config>
	
	<aop:pointcut id="pt" expression="execution(*
	com.atguigu.spring5.service.UserService.*(..))"/>
	
	<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
aop:config>

完全注解方式

不用配置文件,完全使用注解

  1. 创建配置类,替代xml文件
    TxConfig
package com.amhu.yx.spring.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration//配置类
@ComponentScan(basePackages = "com.amhu.yx.spring")//注解扫描
@EnableTransactionManagement //开启事务
public class TxConfig {

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3308/springdb");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("");

        return druidDataSource;
    }

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        //到ioc容器中根据类型注入datasSource
        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;
    }
}

测试方法 testConfig()

    @Test
    public void testConfig(){
        ApplicationContext context =
                new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();

    }

其它代码与之前相同:
UserDao

public interface UserDao {
    void outCount();
    void entryCount();
}

UserDaoImpl

@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //从出账账户中扣钱
    @Override
    public void outCount() {
        String sql = "update user set money=money-? where username=?";
        jdbcTemplate.update(sql,100,"jack");
    }

    //从入账账户中加钱
    @Override
    public void entryCount() {
        String sql = "update user set money=money+? where username=?";
        jdbcTemplate.update(sql,100,"tom");
    }
}

UserService

@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class UserService {

    //注入dao
    @Autowired
    private UserDao userDao;

    //转账的方法
    public void accountMoney() {
        userDao.outCount();//jack少了100块钱

        userDao.entryCount();//tom增加了100块钱

    }
}

你可能感兴趣的:(Spring)