当前位置: 首页 > news >正文

事务的优化

 例子:

举例:假设我们有一个文件上传的uploadFile方法,在这个方法中我们会先执行上传一个文件到分布式文件系统中的方法addMediaFilesToMinIO( ),上传成功后执行文件资源数据入库的addMediaFilesToDb( ),那么这个时候事务应该加在哪个方法上? 是否会有事务失效的问题?

 问题引出

先说结论:我们只将addMediaFilesToDb方法添加事务控制即可,uploadFile方法上的不需要加@Transactional注解,只给与本地数据库进行网络传输的方法加相应的注解,在开发时,不同的网络传输方法要抽离成单独的方法,但是要注意事务生效的条件。

正常情况下我们在外部uploadFile方法上加会生效,但是直接将@Transactional加在addMediaFilesToDb( )事务会失效,问题出在哪里呢?

原因分析

如果在uploadFile方法上添加@Transactional注解,代理对象执行此方法前会开启事务,如下图:

如果在uploadFile方法上没有@Transactional注解,代理对象执行此方法前不进行事务控制,如下图:

所以判断该方法是否可以事务控制必须保证是通过代理对象调用此方法,且此方法上添加了@Transactional注解。

现在在addMediaFilesToDb方法上添加@Transactional注解,也不会进行事务控制是因为并不是通过代理对象执行的addMediaFilesToDb方法。为了判断在uploadFile方法中去调用addMediaFilesToDb方法是否是通过代理对象去调用,我们可以打断点跟踪。

我们发现在uploadFile方法中去调用addMediaFilesToDb方法不是通过代理对象去调用。

如何解决呢?通过代理对象去调用addMediaFilesToDb方法即可解决。

先将addMediaFilesToDb方法提成接口

 /*** @description 将文件信息添加到文件表* @param companyId  机构id* @param fileMd5  文件md5值* @param uploadFileParamsDto  上传文件的信息* @param bucket  桶* @param objectName 对象名称* @return com.xuecheng.media.model.po.MediaFiles* @author Mr.M* @date 2022/10/12 21:22*/public MediaFiles addMediaFilesToDb(Long companyId,String fileMd5,UploadFileParamsDto uploadFileParamsDto,String bucket,String objectName);

在MediaFileService的实现类中注入MediaFileService的代理对象(注入自己交托spring管理成为代理对象),如下:

@Autowired
MediaFileService currentProxy;

在uploadFile方法中将调用addMediaFilesToDb方法处代码改为

MediaFiles mediaFiles = currentProxy.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_files, objectName);

再次debug可以看到对象为代理对象,并且可以通过加入int i = 1/0,观察十五是否回滚测试addMediaFilesToDb上加事务是否生效。

 

完整代码

package com.xuecheng.media.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.model.PageParams;
import com.xuecheng.base.model.PageResult;
import com.xuecheng.media.mapper.MediaFilesMapper;
import com.xuecheng.media.model.dto.QueryMediaParamsDto;
import com.xuecheng.media.model.dto.UploadFileParamsDto;
import com.xuecheng.media.model.dto.UploadFileResultDto;
import com.xuecheng.media.model.po.MediaFiles;
import com.xuecheng.media.service.MediaFileService;
import io.minio.MinioClient;
import io.minio.UploadObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;/*** @author Mr.M* @version 1.0* @description TODO* @date 2022/9/10 8:58*/
@Slf4j
@Service
public class MediaFileServiceImpl implements MediaFileService {@AutowiredMediaFilesMapper mediaFilesMapper;@AutowiredMinioClient minioClient;@AutowiredMediaFileService currentProxy;
// 存储普通文件@Value("${minio.bucket.files}")private String bucket_files;
// 存储视频文件@Value("${minio.bucket.videofiles}")private String bucket_video;//普通文件桶@Value("${minio.bucket.files}")private String bucket_Files;@Overridepublic PageResult<MediaFiles> queryMediaFiels(Long companyId, PageParams pageParams, QueryMediaParamsDto queryMediaParamsDto) {//构建查询条件对象LambdaQueryWrapper<MediaFiles> queryWrapper = new LambdaQueryWrapper<>();//分页对象Page<MediaFiles> page = new Page<>(pageParams.getPageNo(), pageParams.getPageSize());// 查询数据内容获得结果Page<MediaFiles> pageResult = mediaFilesMapper.selectPage(page, queryWrapper);// 获取数据列表List<MediaFiles> list = pageResult.getRecords();// 获取数据总数long total = pageResult.getTotal();// 构建结果集PageResult<MediaFiles> mediaListResult = new PageResult<>(list, total, pageParams.getPageNo(), pageParams.getPageSize());return mediaListResult;}//获取文件默认存储目录路径 年/月/日private String getDefaultFolderPath() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String folder = sdf.format(new Date()).replace("-", "/") + "/";return folder;}//获取文件的md5private String getFileMd5(File file) {try (FileInputStream fileInputStream = new FileInputStream(file)) {String fileMd5 = DigestUtils.md5Hex(fileInputStream);return fileMd5;} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据扩展名获取MimeType* @param extension* @return*/private String getMimeType(String extension) {if (extension == null) {extension = "";}//根据扩展名取出mimeTypeContentInfo extensionMatch = ContentInfoUtil.findExtensionMatch(extension);//通用mimeType,字节流String mimeType = MediaType.APPLICATION_OCTET_STREAM_VALUE;if (extensionMatch != null) {mimeType = extensionMatch.getMimeType();}return mimeType;}/*** 将文件上传到minio* @param localFilePath 文件地址* @param bucket        桶* @param objectName    对象名称* @return void* @description 将文件写入minIO* @author Mr.M* @date 2022/10/12 21:22*/public boolean addMediaFilesToMinIO(String localFilePath, String mimeType, String bucket, String objectName) {try {UploadObjectArgs testbucket = UploadObjectArgs.builder().bucket(bucket).object(objectName).filename(localFilePath).contentType(mimeType).build();minioClient.uploadObject(testbucket);log.debug("上传文件到minio成功,bucket:{},objectName:{}", bucket, objectName);System.out.println("上传成功");return true;} catch (Exception e) {e.printStackTrace();log.error("上传文件到minio出错,bucket:{},objectName:{},错误原因:{}", bucket, objectName, e.getMessage(), e);XueChengPlusException.cast("上传文件到文件系统失败");}return false;}/*** @param companyId           机构id* @param fileMd5             文件md5值* @param uploadFileParamsDto 上传文件的信息* @param bucket              桶* @param objectName          对象名称* @return com.xuecheng.media.model.po.MediaFiles* @description 将文件信息添加到文件表* @author Mr.M* @date 2022/10/12 21:22*/@Override@Transactional  // 在这里加注解public MediaFiles addMediaFilesToDb(Long companyId, String fileMd5, UploadFileParamsDto uploadFileParamsDto, String bucket, String objectName) {//从数据库查询文件MediaFiles mediaFiles = mediaFilesMapper.selectById(fileMd5);if (mediaFiles == null) {mediaFiles = new MediaFiles();//拷贝基本信息BeanUtils.copyProperties(uploadFileParamsDto, mediaFiles);mediaFiles.setId(fileMd5);mediaFiles.setFileId(fileMd5);mediaFiles.setCompanyId(companyId);mediaFiles.setUrl("/" + bucket + "/" + objectName);mediaFiles.setBucket(bucket);mediaFiles.setFilePath(objectName);mediaFiles.setCreateDate(LocalDateTime.now());mediaFiles.setAuditStatus("002003");mediaFiles.setStatus("1");//保存文件信息到文件表int insert = mediaFilesMapper.insert(mediaFiles);if (insert < 0) {log.error("保存文件信息到数据库失败,{}", mediaFiles.toString());XueChengPlusException.cast("保存文件信息失败");return null;}log.debug("保存文件信息到数据库成功,{}", mediaFiles.toString());}return mediaFiles;}/*** 上传文件** @param companyId           机构id* @param uploadFileParamsDto 上传文件信息* @param localFilePath       文件磁盘路径* @return*/
//    @Transactional 不要给这个方法加事务注解,细化到数据库传输方法加@Overridepublic UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, String localFilePath) {File file = new File(localFilePath);if (!file.exists()) {XueChengPlusException.cast("文件不存在");}//文件名称String filename = uploadFileParamsDto.getFilename();//文件扩展名String extension = filename.substring(filename.lastIndexOf("."));//文件mimeTypeString mimeType = getMimeType(extension);//文件的md5值String fileMd5 = getFileMd5(file);//文件的默认目录String defaultFolderPath = getDefaultFolderPath();//存储到minio中的对象名(带目录)String objectName = defaultFolderPath + fileMd5 + extension;//将文件上传到minioboolean result = addMediaFilesToMinIO(localFilePath, mimeType, bucket_files, objectName);if (!result){XueChengPlusException.cast("上传文件失败!");}//文件大小uploadFileParamsDto.setFileSize(file.length());//将文件信息存储到数据库MediaFiles mediaFiles = currentProxy.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_files, objectName);if (null==mediaFiles){XueChengPlusException.cast("文件上传后保存信息失败");}//准备返回数据UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();BeanUtils.copyProperties(mediaFiles, uploadFileResultDto);return uploadFileResultDto;}
}

结论

       在使用事务注解的时候要考虑与三方等网络传输的效率等问题,正确的使用事务。

相关文章:

事务的优化

例子&#xff1a; 举例&#xff1a;假设我们有一个文件上传的uploadFile方法&#xff0c;在这个方法中我们会先执行上传一个文件到分布式文件系统中的方法addMediaFilesToMinIO( )&#xff0c;上传成功后执行文件资源数据入库的addMediaFilesToDb( ),那么这个时候事务应该加在哪…...

VMware虚拟机安装_新虚拟机创建_CentOS镜像导入_linux指令基本操作

文章目录 1 VMware下载安装1.1 下载网址1.2 安装步骤 2 创建虚拟机与CentOS镜像导入2.1 创建新虚拟机2.2 导入CentOS镜像 3 获取ip与连接Xshell3.1 查看虚拟机ip地址3.2 Xshell使用 1 VMware下载安装 1.1 下载网址 https://www.vmware.com/cn/products/workstation-pro/works…...

Git常用命令用法

参考视频&#xff1a;真的是全能保姆 git、github 保姆级教程入门&#xff0c;工作和协作必备技术&#xff0c;github提交pr - pull request_哔哩哔哩_bilibili 1.Git初始化 首先设置名称和邮箱。然后初始化一下&#xff0c;然后就创建了一个空的Git仓库。 PS D:\golang\oth…...

电子元器件采购的数字化转型:智能采购工具的应用

电子元器件采购的数字化转型是采购领域的一项重要趋势&#xff0c;智能采购工具的应用在此过程中发挥了关键作用。以下是智能采购工具在电子元器件采购数字化转型中的应用方面的一些关键点&#xff1a; 供应链可见性&#xff1a; 智能采购工具可以提供对供应链的实时可见性。通…...

【RuoYi移动端】uni-app中通过vuex的store来实现全局变量的修改和读取

一、在store文件中新建csjVar.js文件 const csjVar {csjMess: [{aaa:"ok"},{bbb:"no"}] } export default csjVar 二、修改store文件中新建index.js文件 import Vue from vue import Vuex from vuex import user from /store/modules/user import gette…...

IPv6改造深化之路

01 IPv6改造问题及整体改造思路 随着“十四五”期间国家政策对IPv6深化改造及规模部署的推动&#xff0c;在IPv6改造过程中出现了越来越多的系统性问题&#xff0c;如图1所示。 图1 关于IPv6改造的各种疑问所有跨设备通信的IT软硬件系统均需要处理IP地址&#xff0c;各领域均需…...

atoi(),isdigit(),isspace(),round()源码

atoi()是一个C标准库函数&#xff0c;用于将字符串转换为对应的整数。 以下是atoi()函数的一种简化版本的示例实现&#xff1a; int atoi(const char* str) {int result 0;int sign 1;int i 0;// 处理空格while (isspace(str[i])) {i;}// 处理正负号if (str[i] - || str[…...

C# 播放音频文件(播放提示音)

使用SoundPlayer播放声音 System.Media名称空间下的类SoundPlayer 可以让我们很方便的播放wav波形声音文件。SoundPlayer类其实就是对winmm.dll文件中API函数的封装。 首先引入命名空间&#xff1a; using System.Media; SoundPlayer player new SoundPlayer(); player.Sou…...

一种编程语言,

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…...

云原生Kubernetes:K8S常用服务端口

目录 一、理论 1.K8S常用服务端口号 一、理论 1.K8S常用服务端口号 &#xff08;1&#xff09;K8S集群 表1 K8S集群端口 协议端口号K8S集群TCP22使用主机驱动通过SSH进行节点配置TCP53集群DNS服务UDP53集群DNS服务TCP2376主机驱动与Docker守护进程通信的TLS端口TCP2379et…...

clickhouse调优配置

一、官方文档地址 clickhouse的配置项主要在 config.xml 或 users.xml 中&#xff0c; 基本上都在 users.xml 里 config.xml https://clickhouse.tech/docs/en/operations/server-configuration-parameters/settings/ users.xml https://clickhouse.tech/docs/en/operatio…...

pdf文件打开后部分文字无法显示

场景&#xff1a;pdf文件在系统内预览正常&#xff0c;但是下载到本地电脑上&#xff0c;使用wps查看&#xff0c;部分标题会消失&#xff0c;只有标题里面的数字还能显示出来 经过一系列排查&#xff0c;发现查看的电脑上缺失了字体&#xff0c;使用wps查看时&#xff0c;缺失…...

MCS-51单片机温度控制系统的设计

一、项目介绍 注塑机是一种常用的制造设备&#xff0c;用于生产塑料制品。在注塑机的工作过程中&#xff0c;溶胶必须达到一定的温度才能被注入模具中进行成型。因此&#xff0c;在注塑机的生产过程中&#xff0c;温度控制是非常重要的一环。 本项目基于MCS-51单片机设计了一…...

Xcode,swift:Error Domain=kCLErrorDomain Code=1 (null)问题解决

问题描述: iOS开发时,当使用用户的位置权限时,获取用户经纬度报错:Error DomainkCLErrorDomain Code1 "(null)",错误域kCLError域代码1“(null)” 解决方法: 打开模拟机的设置-通用-语言与地区 将地区设置为中国(如果你的开发位置在中国的话) 点击左上方Features,选择…...

0013Java程序设计-springboot教材图文内容审核系统

摘 要目 录第1章 绪论1.1 研究背景与意义1.2 研究内容1.3 论文组成结构 系统实现用户登录模块的实现后台管理系统登录模块的实现投稿信息的实现 开发环境 摘 要 《教材图文内容审核系统》课程案例库研究系统系统主要功能模块包括投稿信息、打卡记录、新闻资讯等&#xff0c;采…...

Unable to remove Temporary User Data

错误截图 原因 项目的临时数据目录是存在了未授权的盘符&#xff0c;当删除它的时候&#xff0c;遇到了权限问题&#xff0c;没有权限没法删除。 解决方法 增加字段&#xff1a;userDataDir 解决...

Rocky(Centos)安装中文字体(防止中文乱码)

1、查看字体列表 运行下列命令 fc-list 若出现&#xff0c;下面截图&#xff0c;则需要安装字体管理软件 安装字体库&#xff0c;运行&#xff1a; yum -y install fontconfig 当看到下图的提示信息时说明已安装成功&#xff1a; 二、添加中文字体 1&#xff09;window…...

O2OA(翱途)开发平台 V8.1正式发布

尊敬的O2OA(翱途)平台合作伙伴、用户以及亲爱的开发小伙伴们&#xff0c;平台 V8.1版本已正式发布。正值8月的最后一周&#xff0c;我们以更安全、更高效、更好用的崭新面貌迎接9月的到来。 O2OA开发平台v8.1版本更注重于对系统级别的安全防护。其中重大的更新&#xff0c;是对…...

差异化竞争阵地的所在【周技术进阶】-从BS 项目C#最基础截取字符串方法开始

效果 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleAppNumberOneHelloWorld {class Program{static void Main(string[] args){Console.WriteLine("hello world&#xf…...

docker安装在linux下的docker安装操作步骤完整版

参考文档&#xff1a;http://wed.xjx100.cn/news/151901.html?actiononClick 第一步&#xff0c;卸载历史版本。这一步是可选的&#xff0c;如果之前安装过旧版本的Docker&#xff0c;可以使用如下命令进行卸载&#xff1a; yum remove docker \docker-client \docker-client…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...