Spring事务个人知识点总结

一. Spring事务简介

  • 事务作用: 在数据层保障一系列的数据库操作同成功同失败
  • Spring事务作用: 在数据层或业务保障一些列的数据库操作同成功同失败

二. 案例: 银行账户转账

  • 需求: 实现任意两个账户间的转账操作
  • 需求微缩: A账户减钱, B账户加钱
idnamemoney
1Tom1000
2Jerry1000

程序正常执行时, 账户金额A减B加, 没有问题

程序出现异常后, 转账失败, 但是异常之前操作成功, 异常之后操作失败, 整体业务失败

(1). 数据层提供基础操作: 指定账户减钱(outMoney), 指定账户加钱(inMoney)

@Mapper
public interface AccountDao {
    @Update("update account set account_money = account_money + #{money} where account_name = #{name}")
    void inMoney(@Param("name")String name, @Param("money") Double money);

    @Update("update account set account_money = account_money - #{money} where account_name = #{name}")
    void outMoney(@Param("name")String name, @Param("money") Double money);

}

(2). 业务层提供转账操作(transfer): 调用减钱与加钱的操作

public interface AccountService {
  /*
   * 转账操作
   * @param out 传出方
   * @param in 转入方
   * @param money 金额
   * */
  @Transactional
  void transfer(String out, String in, Double money);
}
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String out, String in, Double money) {
        accountDao.outMoney(out, money);
        //int i = 1/0; //开启异常
        accountDao.inMoney(in, money);
    }
}

(3). 提供两个账号和操作金额执行转账操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
        accountService.transfer("Tom", "Jerry", 100.0);
    }
}

(4). 基于Spring整合MyBatis环境搭建上述操作

1. 开启事务之添加Spring事务管理

一般添加事务不直接添加在实现类的方法上, 而是直接添加在实现类的接口上,
在需要添加事务的方法所对应的接口方法上打上注解@Transactional, 好处是降低耦合,
写在接口上就会为接口里的所有方法开启事务

public interface AccountService {
    /*
    * 对指定方法的接口方法添加事务@Transactional
    *
    * */
    @Transactional
    void transfer(String out, String in, Double money);
}

2. 开启事务之设置事务管理器

因为是JDBC的事务, 所以需要在JDBC的配置文件中设置事务管理器, 同时还需要注入DataSource

public class JdbcConfig {
/*
    * 定义事务管理器
    * 添加数据库资源
    * */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

3. 开启事务之设注解事务驱动@EnableTransactionManagement

@Configuration
@ComponentScan("com.itstudy")
@PropertySource({"classpath:jdbc.properties"})
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}

三. Spring事务原理 — 事务角色

1. 事务管理员

  • 发起事务方, 在Spring中通常指代业务层开启事务的方法
    Spring开启第三个事务, 让另外两个执行事务的操作全部加入这个第三方事务

2. 事务协调员

  • 加入事务方, 在Spring中通常指代数据层方法, 也可以是业务层方法
    即为AccountDao里的两条Sql语句的执行方法, 这两个方法, 分别有自己对应的事务, 所以当一个事务有异常时另一个事务并不会回滚操作,
    当这两个事务都加入第三方同一个事务时 ,有一个除了异常, 另外一个方法也可以回滚了
@Mapper
public interface AccountDao {
    //事务方1
    @Update("update account set account_money = account_money + #{money} where account_name = #{name}")
    void inMoney(@Param("name")String name, @Param("money") Double money);

    //事务方2
    @Update("update account set account_money = account_money - #{money} where account_name = #{name}")
    void outMoney(@Param("name")String name, @Param("money") Double money);

}

四. Spring事务属性

1. 事务配置

在这里插入图片描述

图片来自B站黑马程序员—Spring事务属性

均可以采用注解的形式来开启

2. 案例: 转账业务追加日志

  • 基于转账操作案例添加日志模块, 实现数据库中记录日志
  • 业务层转账操作(transfer), 调用减钱, 加钱与记录日志功能
    实现效果预期: 无论转账操作是否成功, 均进行转账操作的日志留痕

准备好记录日志的表, 和实现方法

idaccount_infocreate_date
1转账操作有Tom到Jerry,金额:100.02023-06-07 21:05:49

Dao层下执行sql语句的接口方法

@Mapper
public interface LogDao {

    @Insert("insert into account_logs(account_info, crate_date) values (#{info}, now())")
    void log(String info);
}

新建一个日志业务层调用该接口方法, 并开启事务

public interface LogService {
  @Transactional
  void log(String out, String in, Double money);
}
@Service
public class LogServiceImpl implements LogService {
    @Autowired
    private LogDao logDao;
    @Override
    public void log(String out, String in, Double money) {
        logDao.log("转账操作由" + out + "到" + in + ",金额:" + money);
    }
}

在执行操作的业务层调用LogService日志业务接口, 并自动注入接口LogService的实现类, 为保证记入日志的方法一定执行,
采取try…finally的方式来执行

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    @Override
    public void transfer(String out, String in, Double money) {
        try {
            accountDao.outMoney(out, money);
            //int i = 1/0; //开启异常
            accountDao.inMoney(in, money);
        } finally {
            logService.log(out, in, money);
        }
    }
}

运行测试程序:
在这里插入图片描述

日志添加成功

但是开启异常后开启运行测试程序发现日志记录失败, 因为同一个事务内同成功同失败, 现在加以改进实现日志必须保留,
请转看下一条目, 事务传播行为

3. 事务传播行为

事务协调员对事物管理员所携带事务的处理态度

在这里插入图片描述

图片来自B站黑马程序员—Spring事务属性

为解决上述日志记录失败问题, 需要记录日志的事务不执行事务管理员所携带的事务,在日志接口上注解@Transactionalho加上条件变成@Transactional(propagation = Propagation.REQUIRES_NEW),
让日志不参加到同一个事务中, 自己新开一个事务

public interface LogService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}

再来开启异常后开启运行测试程序

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    @Override
    public void transfer(String out, String in, Double money) {
        try {
            accountDao.outMoney(out, money);
            int i = 1/0; //开启异常
            accountDao.inMoney(in, money);
        } finally {
            logService.log(out, in, money);
        }
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

  @Autowired
  private AccountService accountService;

  @Test
  public void testTransfer() {
    accountService.transfer("Tom", "Jerry", 150.0);
  }
}

账户金额表:

在这里插入图片描述

没有发生变动

日志记录表:

在这里插入图片描述

成功记录一条转账日志

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值