Spring事务管理

Spring事务管理

1、事务

  1. 什么是事务

    一组操作要么都成功,要么都失败

  2. 事务的特性

    ACID

    • 原子性,一组操作是一个整体,要么全成功,要么全失败,不可分隔
    • 一致性:事务前后数据保持一致
    • 隔离性:多个事务并发访问数据库,并发事务相互隔离
    • 持久性:事务一旦提交不能修改
  3. 隔离问题?

    • 脏读:一个事务读到另一个事务没有提交的数据
    • 不可重复读:一个事务读到另一个数为已经提交的数据(update)
    • 幻读:一个事务读到另一个数为已经提交的数据(insert)

    不可重复读指对数据进行了修改

    幻读指添加了一条数据

  4. 隔离级别

    • 读未提交(read uncommitted):一个事务读到另一个事务没有提交的数据
    • 读已提交(read committed):一个事务读到另一个事务已经提交的数据,解决了一个问题(脏读)
    • 可重复读(repeatable read):在一个事务中读到的数据重复的。解决2个问题(脏读、不可重复读)
    • 串行化(Serializable):单事务,解决3个问题(脏读、不可重复读、幻读)
  5. 对比

    • 性能:read uncommitted > read committed > repeatable read > Serializable
    • 安全:read uncommitted < read committed < repeatable read < Serializable

2、事务管理

2.1、事务的传播行为

事务的传播行为有7种

传播行为 描述
propagation_required 如果当前没有事务,就创建一个,如果有就使用当前的
propagation_supports 支持当前事务,如果没有事务,就以非事务执行
propagation_mandatory 使用当前事务,如果没有事务,就抛出异常
propagation_requires_new 新建事务,如果当前有事务,就挂起
propagation_not_supported 非事务执行,如果当前有事务,就挂起
propagation_never 以非事务执行,如果当前有,就抛出异常
propagation_nested 当前事务存在,在嵌套事务中执行,没有就用propagation_required

2.2、事务的隔离级别

隔离级别

隔离级别 描述
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 读未提交
ISOLATION_READ_COMMITTED 读已提交
ISOLATION_REPEATABLE_READ 读可重复
ISOLATION_SERIALIZABLE 穿行化

2.3、只读

表示只能对数据库进行读取操作,不能对数据库进行增删改操作

2.4、基本操作(注解)

2.4.1、需求

完成转账

2.4.2、实现

表结构

CREATE TABLE account(
  id INT PRIMARY KEY AUTO_INCREMENT,
  money FLOAT
);

表数据

Spring事务管理

配置文件

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db1
jdbc.username=root
jdbc.password=root

Configuration类

package com.czxy.demo11_transaction.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import tk.mybatis.spring.annotation.MapperScan;

import javax.sql.DataSource;

@Configuration      // 标明配置类
@ComponentScan("com.czxy.demo11_transaction")      // 设置组件扫描路径
@PropertySource("classpath:db.properties")      //加载配置文件
@MapperScan("com.czxy.demo11_transaction.mapper")       //mapper扫描路径 使用的通用mapper 需要导入的是tk.mybatis.包下的
@EnableTransactionManagement                    //开启事务
public class SpringConfig {

    @Value("${jdbc.driver}")
    private String driver;  //驱动

    @Value("${jdbc.url}")
    private String url;     //路径

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    //配置连接池
    @Bean
    public DataSource dataSource(){
        DruidDataSource source = new DruidDataSource();
        source.setDriverClassName(driver);
        source.setUrl(url);
        source.setUsername(username);
        source.setPassword(password);
        return source;
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        //1 创建工厂
        // 1.通过工厂bean创建对象,最后需要调用 getObject()获得具体的对象
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

        // 1.1 设置数据源
        factoryBean.setDataSource(dataSource);
        // 1.2 设置别名包扫描
        factoryBean.setTypeAliasesPackage("com.czxy.sm.domain");
        // 1.3 全局配置:驼峰映射
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        config.setMapUnderscoreToCamelCase(true);
        factoryBean.setConfiguration(config);

        // 返回SqlSessionFactory
        return factoryBean.getObject();
    }

}

service接口

public interface UserService {
    /**
     *
     * @param outId 输出账号
     * @param intId
     * @param money
     */
    void change(Integer outId, Integer intId, float money);
}

service实现类

@Service
@Transactional      //开启事务
public class UserServiceImpl implements UserService {

    @Resource
    private AccountMappper mapper;

    public void change(Integer outId, Integer intId, float money) {
        Account intAccount = mapper.selectByPrimaryKey(intId);
        intAccount.setMoney(intAccount.getMoney() + money);
        mapper.updateByPrimaryKey(intAccount);

        Account outAccount = mapper.selectByPrimaryKey(outId);
        outAccount.setMoney(outAccount.getMoney() - money);
        mapper.updateByPrimaryKey(outAccount);
    }
}

javaBean

//数据库表名未account
public class Account {
    @Id
    private Integer id;
    private Float money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }
}

测试类

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DemoTest {

    @Resource
    private UserService service;

    @Test
    public void test01(){
        service.change(1,2,100);
        System.out.println("成功");
   	}
}

运行后

Spring事务管理

错误的情况下

在service方法里面加上异常

public class UserServiceImpl implements UserService {

    @Resource
    private AccountMappper mapper;

    public void change(Integer outId, Integer intId, float money) {
        Account intAccount = mapper.selectByPrimaryKey(intId);
        intAccount.setMoney(intAccount.getMoney() + money);
        mapper.updateByPrimaryKey(intAccount);
        int i =1/0;
        Account outAccount = mapper.selectByPrimaryKey(outId);
        outAccount.setMoney(outAccount.getMoney() - money);
        mapper.updateByPrimaryKey(outAccount);
    }
}

运行后

Spring事务管理

数据库的数据没有改变。

2.5、小结

  • 在配置类上加上@EnableTransactionManagement

    • 并添加一个DataSourceTransactionManager的bean
  • 在需要事务的实现类上加上@Transactional

    • @Transactional的参数

    • @Transactional(
          readOnly = false,	//只读
          timeout = -1,		//超时
          isolation = Isolation.DEFAULT, 		//隔离级别
          propagation = Propagation.REQUIRED)	//传播行为
      

一个DataSourceTransactionManager的bean

  • 在需要事务的实现类上加上@Transactional

    • @Transactional的参数

    • @Transactional(
          readOnly = false,	//只读
          timeout = -1,		//超时
          isolation = Isolation.DEFAULT, 		//隔离级别
          propagation = Propagation.REQUIRED)	//传播行为
      
上一篇:线程并发,如何上下文切换


下一篇:C#学习笔记,2021/12/4