学习之前建议先复习一下事务
目录
[MySql 实际开发需求](#MySql 实际开发需求)
[写 sql 事务](#写 sql 事务)
[运行 mysql 事务](#运行 mysql 事务)
[Springboot 的事务框架](#Springboot 的事务框架)
[Transactional 注解](#Transactional 注解)
[疑问 那我们是怎么校验传入标签有误的呢](#疑问 那我们是怎么校验传入标签有误的呢)
MySql 实际开发需求
需求分析
现在是有三张表
第一张表为 t_sentence 表 列有 id content create_time from hot other1 other2 other2
第二张表为 t_tags 表 列有 id name from hot other1 other2 other3 create_time
第三张表为t_sentence表和t_tags 表的中间表 t_sentence_tag 列有 id sentence_id tag_id create_time
现在我要往第一张表里插入数据 现有数据是 String 类型的 content
List<TagsReq>类型的 tags 的 name 的集合 还有 String 类型的 from
现在要求你写一个 mysql 事务
往数据库里插入数据 同时插入到 t_sentence 表和t_sentence_tag 这两张表里面
给你一个提示 要根据给出的集合中的 name 先去 t_tags 表里面把 id 查出来然后再操作
注意要保证数据一致性
现在确保的是标签一定存在 插入时间会自动赋值

因为涉及到多表操作
我们应该在操作第二张表失败后进行事务回滚
写 sql 事务
第一步是 首先插入 sentence 表 获取 sentence_id
第二步是 获取到刚刚得到的 再去 tags 表查询 tags _id
第三步是 获取到 现在有 sentence_id 和 tags_id 后 要对唯一的 sentence_id 对每一条 tags_id 的数据组合后写入中间表 sentence_tag
为保证原子性 我们需要写一个事务来处理 保证原子性

运行 mysql 事务
跑了 180ms

Springboot 的事务框架
我们将数据库操作上升到 OBM 框架
这边选择的是 mybatis
发起的请求
同样的
我们的传输层对象是这样的

请求体 JSON 格式
{
"addSentenceReq": {
"content": "这是一句新的句子内容",
"from": "出处信息"
},
"tagsList": [
{
"name": "标签1"
},
{
"name": "标签2"
}
]
}
在 XML 里写了两个操作

<!-- 添加句子主记录以及插入标签关联(带事务控制) -->
<!-- 添加句子主记录 -->
<insert id="addSentence" parameterType="work.dduo.ans.model.vo.request.AddSentenceReq"
useGeneratedKeys="true" keyProperty="sentenceId">
INSERT INTO t_sentences
(content, `from`, hot)
VALUES (#{content}, #{from}, 0)
</insert>
<!-- 批量插入标签关联 -->
<insert id="batchInsertTags" parameterType="work.dduo.ans.model.vo.request.AddSentenceTagReq">
INSERT INTO t_sentence_tag (sentence_id, tag_id)
SELECT #{sentenceId}, id
FROM t_tags
WHERE `name` IN
<foreach item="tag" collection="tagsList" open="(" separator="," close=")">
#{tag.name}
</foreach>
</insert>
配置事务配置类

package work.dduo.ans.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Autowired
private DataSource dataSource;
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource);
}
}
接下来我们只需要在 Springboot 框架上封装事务处理即可
Transactional 注解
@Transactional(rollbackFor = Exception.class, timeout = 5)
捕获的是异常
service 方法
@Transactional(rollbackFor = Exception.class, timeout = 5)
public boolean addSentenceWithTags(AddSentenceDTO addSentenceDTO) throws Exception {
try {
// 主记录插入
AddSentenceReq addSentenceReq = addSentenceDTO.getAddSentenceReq();
tSentencesMapper.addSentence(addSentenceReq);
Long sentenceId = addSentenceReq.getSentenceId();
// 关联标签插入
List<AddTagsReq> tagsList = addSentenceDTO.getTagsList();
AddSentenceTagReq addSentenceTagReq = new AddSentenceTagReq();
addSentenceTagReq.setSentenceId(sentenceId);
addSentenceTagReq.setTagsList(tagsList);
int size = tagsList.size();
if (!tagsList.isEmpty()) {
int i = tSentencesMapper.batchInsertTags(addSentenceTagReq); // 返回的是改变的标签数量
if (i != size) {
throw new Exception("传入无效标签");
}
return true;
}
} catch (Exception e) {
throw e;
}
return false;
}
捕获后回滚

同样超时回滚也是为了防止恶意占用 cpu mysql 出现卡死现象
疑问 那我们是怎么校验传入标签有误的呢

这边很巧妙的通过查询有用数据返回来判断的
传入 2 个标签 应该传出 2 个标签 id
如果不同的话则存在无效标签
抛出异常
被 catch 捕获
再(第二次抛出异常) 被事务注解捕获
spring 事务回滚