Spring事务回滚在系统中的应用
以文章发布为例,介绍Spring事务回滚在系统中的应用
事务回滚的核心概念
事务回滚是数据库管理系统中的关键机制,它确保数据库操作要么全部成功,要么全部失败。在Spring框架中,我们可以通过@Transactional注解轻松实现事务管理。
事务的ACID特性
- 原子性(Atomicity):操作不可分割,要么全部完成,要么全部不执行
- 一致性(Consistency):事务前后数据库状态保持一致
- 隔离性(Isolation):并发事务互不干扰
- 持久性(Durability):事务完成后结果永久保存
文章发布系统的事务应用
在我们的文章发布系统中,存在两种业务场景:
- 正式发布:所有操作(主表、文件、白名单)必须全部成功
- 草稿保存:主表必须成功,其他操作尽量完成但不强制
事务控制实现代码
@Transactional(rollbackFor = Exception.class)
public RestResponse publishArticle(PublishArticleRequest request) {boolean isDraft = request.getIsDraft() == 2;try {// 设置默认值request.setIsDelete(1);// ...其他默认值设置// 保存文章主表int articleId = userArticlesMapper.publishArticle(request);// 尝试保存附加数据saveAdditionalData(request, articleId, isDraft);return RestResponse.success(isDraft ? "草稿保存成功" : "发布成功");} catch (Exception e) {// 手动标记回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return RestResponse.fail(isDraft ? "草稿保存失败" : "发布失败");}
}private void saveAdditionalData(PublishArticleRequest request, int articleId, boolean isDraft) {try {// 1. 保存可见白名单if (request.getWatchPermission() == 4) {// ...构建白名单数据userArticlesMapper.publishWatchPermissionnList(watchList);}// 2. 保存评价白名单if (request.getEvaluatePermission() == 4) {// ...构建评价白名单数据userArticlesMapper.publishEvaluatePermissionnList(evaluateList);}// 3. 批量保存文件(性能优化关键)if (request.getFiles() != null && !request.getFiles().isEmpty()) {List<BaseUserArticlesFiles> fileList = request.getFiles().stream().map(fileUrl -> new BaseUserArticlesFiles(articleId, fileUrl)).collect(Collectors.toList());userArticlesMapper.batchInsertFiles(fileList);}} catch (Exception e) {if (isDraft) {// 草稿模式:只记录日志,不抛出异常log.warn("草稿附加数据保存失败(不影响主表)", e);} else {// 发布模式:抛出异常触发事务回滚throw e;}}
}
实体类优化支持批量操作
@Data
public class BaseUserArticlesFiles {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;private Integer userArticlesId;private Integer isDelete = 1; // 默认值private String fileUrl;// 支持批量插入的构造方法public BaseUserArticlesFiles(Integer userArticlesId, String fileUrl) {this.userArticlesId = userArticlesId;this.fileUrl = fileUrl;}
}
MyBatis批量插入实现
<!-- 批量插入文件 -->
<insert id="batchInsertFiles" parameterType="list">INSERT INTO base_user_articles_files (user_articles_id, file_url, is_delete)VALUES<foreach collection="fileList" item="file" separator=",">(#{file.userArticlesId}, #{file.fileUrl}, #{file.isDelete})</foreach>
</insert>
事务回滚的工作原理
性能优化:批量操作
在文件保存场景中,使用批量插入显著提升性能:
// 普通单条插入(N+1问题)
for (String file : files) {mapper.insertFile(new File(articleId, file));
}// 批量插入(高效)
List<File> fileList = files.stream().map(file -> new File(articleId, file)).collect(Collectors.toList());
mapper.batchInsertFiles(fileList);