1.调用这个方法的对象是否是spring的代理对象($CGLIB结尾的)
2.这个方法是否是加了@Transactional注释
都符合才可以被事物控制
如果调用方法的对象没有被事物控制,那么被调用的方法即便是加了@Transactional也是没用的
一个非事务方法调同类一个事务方法,事务无法控制
举例如下:
在controller中,调用MediaFileServiceImpl 中的uploadFile方法,因为controller中注入了@Autowired MediaFileService mediaFileService;所以controller将被spring代理对象代理,此时如果MediaFileServiceImpl中的 uploadFile加了@Transactional注释,那么很自然的将受到事物控制。(验证是否是spring的代理对象,可以debug然后查看变量是否是$CGLIB结尾的)。

 如果在uploadFile方法上没有@Transactional注解,代理对象执行此方法前不进行事务控制,如下图:
 现在在addMediaFilesToDb方法上添加@Transactional注解,也不会进行事务是因为并不是通过代理对象执行的addMediaFilesToDb方法。为了判断在uploadFile方法中去调用addMediaFilesToDb方法是否是通过代理对象去调用,我们可以打断点跟踪。
controller代码如下,在try catch中看到用的是mediaFileService去调用的uploadFile方法,此时是可以控制事物的。
public class MediaFilesController {
    
@Autowired
MediaFileService mediaFileService;
@RequestMapping(value = "/upload/coursefile", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public UploadFileResultDto upload(@RequestPart("filedata") MultipartFile filedata,
                                      @RequestParam(value = "folder",required=false) String folder,
                                      @RequestParam(value= "objectName",required=false) String objectName) {
        Long companyId = 1232141425L;
        UploadFileParamsDto uploadFileParamsDto = new UploadFileParamsDto();
        String contentType = filedata.getContentType();
        uploadFileParamsDto.setContentType(contentType);
        uploadFileParamsDto.setFileSize(filedata.getSize());//文件大小
        if (contentType.indexOf("image") >= 0) {
            //是个图片
            uploadFileParamsDto.setFileType("001001");
        } else {
            uploadFileParamsDto.setFileType("001003");
        }
        uploadFileParamsDto.setFilename(filedata.getOriginalFilename());//文件名称
        UploadFileResultDto uploadFileResultDto = null;
        try {
            uploadFileResultDto = mediaFileService.uploadFile(companyId, uploadFileParamsDto, filedata.getBytes(), folder, objectName);
        } catch (Exception e) {
            XueChengPlusException.cast("上传文件过程中出错");
        }
        return uploadFileResultDto;
    }
如果在uploadFile方法中取调用另外一个方法,那么显然调用的对象默认是this,并不是受到spring代理的对象,所以即便被uploadFile方法调用的方法加了@Transactional注释,也是没用的。
解决非代理对象调用@Transactional方法的:
我们可以模仿@Controller中的写法,在被调用的方法中注入相应的service,然后用service在非事物方法中取调用加了@Transactional注释的方法,这样事物是会生效的。
下面举例:
在serviceimpl类中的uploadFile方法调用另一个@Transactional方法,形成事物
首先在serviceimpl中注入service对象:(对象名字无所谓,加了@Autowired注释的对象会被AOP拦截)
    @Autowired
    MediaFileService proxy;调用方法:(在try代码第2行,用proxy调用)
@Override
    public UploadFileResultDto uploadFile(Long companyId, UploadFileParamsDto uploadFileParamsDto, byte[] bytes, String folder, String objectName) {
       ....
        try {
            addMediaFilesToMinIO(bytes,bucket_files,objectName);
            MediaFiles mediaFiles = proxy.addMediaFilesToDb(companyId, fileMd5, uploadFileParamsDto, bucket_files, objectName);
            //准备返回数据
            UploadFileResultDto uploadFileResultDto = new UploadFileResultDto();
            BeanUtils.copyProperties(mediaFiles,uploadFileResultDto);
            return uploadFileResultDto;
        } catch (Exception e) {
            log.debug("上传文件失败:{}",e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
//        return null;
    }被调用的方法:
@Transactional
public MediaFiles addMediaFilesToDb(Long companyId, String fileId, UploadFileParamsDto uploadFileParamsDto, String bucket, String objectName) {
    //保存到数据库
    MediaFiles mediaFiles = mediaFilesMapper.selectById(fileId);
    if(mediaFiles == null){
        mediaFiles = new MediaFiles();
        //封装数据
        BeanUtils.copyProperties(uploadFileParamsDto,mediaFiles);
        mediaFiles.setId(fileId);
        mediaFiles.setFileId(fileId);
        mediaFiles.setCompanyId(companyId);
        mediaFiles.setBucket(bucket);
        mediaFiles.setFilePath(objectName);
        mediaFiles.setUrl("/"+bucket+"/"+objectName);
        mediaFiles.setCreateDate(LocalDateTime.now());
        mediaFiles.setStatus("1");
        mediaFiles.setAuditStatus("002003");
        //插入文件表
        mediaFilesMapper.insert(mediaFiles);
    }
    return mediaFiles;
}
注意:我们这里由于是用的service来调用,而service是一个接口,所以被proxy调用的方法必须也是以接口形式呈现出来,那么需要将addMediaFilesToDb写入到对应调用这个方法的service(此处用)MediaFileService proxy调用,所以在MediaFileService 中加上这个方法,写个接口即可。
接口如下:
public interface MediaFileService {
/***
* @description 上传文件到数据库,抽取为接口的形式,方便调用
* @param companyId
 * @param fileId
 * @param uploadFileParamsDto
 * @param bucket
 * @param objectName
* @return
* @author
* @date
*/
 public MediaFiles addMediaFilesToDb(Long companyId, String fileId, UploadFileParamsDto uploadFileParamsDto, String bucket, String objectName);
}



















