spring事务相关配置之propagation

1 概述

  1. propagation是指事务的传播行为
  2. 我们通过一个例子来讲解。例子:A向B转账,无论转账成功与否都需要记录日志

2 环境准备

2.1 spring整合mybatis

spring整合mybatis

2.2 数据库准备

以下表在test数据库下创建

2.2.1 account表

-- 创建account表
create table account (
    id int primary key,
    money int not null
);

--插入数据
insert into account(id, money) values(1, 1000), (2, 1000);

2.2.2 log表

create table log (
     remitterId int not null,			    -- 汇款人
     payeeId int not null,				    -- 收款人
     money int not null,					-- 汇款金额
     insertTime timestamp default now(),    -- 时间
     success bool default 1				    -- 是否成功
);

2.3 创建dao、service并开启spring事务

2.3.1 AccountDao

package cn.qiguai.dao;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

public interface AccountDao {
	@Update("update account set money = money + ${money} where id = ${id}")
	void updateMoney(@Param("id")int id, @Param("money")int money);
}

2.3.2 LogDao

package cn.qiguai.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

public interface LogDao {
	@Insert("insert into log(remitterId, payeeId, money, success) values(${remitterId}, ${payeeId}, ${money}, ${flag})")
	void addLog(@Param("remitterId")int remitterId, @Param("payeeId")int payeeId, @Param("money")int money, @Param("flag")boolean flag);
}

2.3.3 AccountService

package cn.qiguai.service;

import org.springframework.transaction.annotation.Transactional;

public interface AccountService {
	@Transactional
	void transfer(int remitterId, int payeeId, int money);
}

2.3.4 AccountServiceImpl

package cn.qiguai.service.impl;

import cn.qiguai.dao.AccountDao;
import cn.qiguai.dao.LogDao;
import cn.qiguai.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
	@Autowired
	private AccountDao accountDao;
	@Autowired
	private LogDao logDao;

	@Override
	public void transfer(int remitterId, int payeeId, int money) {
		boolean flag = true;

		try {
			accountDao.updateMoney(remitterId, -money);
			accountDao.updateMoney(payeeId, money);
		} catch (Exception e) {
			flag = false;
			throw e;
		} finally {
			logDao.addLog(remitterId, payeeId, money, flag);
		}
	}
}

2.3.5 设置事务管理器(在JdbcConfig中)

@Bean
public PlatformTransactionManager transactionManager(DataSource ds) {
	DataSourceTransactionManager dstm = new DataSourceTransactionManager();

	dstm.setDataSource(ds);

	return dstm;
}

2.3.6 告诉spring使用注解开启了事务

package cn.qiguai.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan("cn.qiguai")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
@EnableTransactionManagement //告诉spring使用注解开启了事务
public class SpringConfig {
}

2.4 spring整合Junit

  1. 添加对应坐标
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<!--spring整合junit-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
  1. 创建测试类
  2. 设定类运行器
  3. 指定spring配置
package cn.qiguai.service;

import cn.qiguai.config.SpringConfig;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class) //设定类运行器
@ContextConfiguration(classes = SpringConfig.class) //指定spring配置
public class AccountServiceTest {
}

2.5 测试

package cn.qiguai.service;

import cn.qiguai.config.SpringConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {
	@Autowired
	private AccountService accountService;

	@Test
	public void testTransfer() {
		//1向2转账500
		accountService.transfer(1, 2, 500);
	}
}
  1. 运行测试,能够成功转账并记录日志
  2. 将数据库数据恢复

3 BUG

  1. 我们在transfer方法中模拟异常,如下图
    回滚异常模拟
  2. 再次运行测试,观察结果,发现account表和log数据都没有变化,不符合我们的预期。
  3. account表和log表数据都没有发生变化的原因:updateMoney方法、addLog方法和transfer方法的事务都是同一个,我们需要使用propagation来给addlog方法开启单独的事务

4 propagation

  1. 在addLog方法上添加如下代码,给addLog方法开启单独的事务
    propagation开启单独的事务
  2. 再次运行测试,观察结果,发现达到我们预期效果
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值