文章目录
1 概述
- propagation是指事务的传播行为
- 我们通过一个例子来讲解。例子:A向B转账,无论转账成功与否都需要记录日志
2 环境准备
2.1 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
- 添加对应坐标
<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>
- 创建测试类
- 设定类运行器
- 指定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);
}
}
- 运行测试,能够成功转账并记录日志
- 将数据库数据恢复
3 BUG
- 我们在
transfer
方法中模拟异常,如下图
- 再次运行测试,观察结果,发现account表和log数据都没有变化,不符合我们的预期。
- account表和log表数据都没有发生变化的原因:updateMoney方法、addLog方法和transfer方法的事务都是同一个,我们需要使用
propagation
来给addlog方法开启单独的事务
4 propagation
- 在addLog方法上添加如下代码,给addLog方法开启单独的事务
- 再次运行测试,观察结果,发现达到我们预期效果