SpringBoot聚合项目:达内知道(七)-声明式事务、统一异常处理、文件上传

1.声明式事务

1.1 新增问题的程序漏洞

  如果在新增问题的业务逻辑层运行过程中发生了异常,就会导致问题可能新增完毕,但是问题和关系(标签、讲师)新增失败的情况,这样就可能出现一个问题没有关联任何标签,或者没有关联任何讲师的情况。这种情况是不好的,在企业开发过程中,必须避免这种情况的发生。因为这样会引发数据安全性问题,可能导致程序运行过程中发生异常,要想避免这个问题就需要使用到事务。

1.2 什么是事务?

事务一般指:"数据库事务"

定义:是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

常见面试题

数据库事务的四个特性,简称ACID特性

  • 原子性(Atomicity):事务作为一个整体被执行,事务中的操作要么都成功,要么都失败(要执行都执行,要不执行都不执行);

  • 一致性(Consistency):事务运行前后,一致状态保持不变,例如:转账前后银行中的总金额不变;

  • 隔离性(Isolation):数据库支持多个事务并发执行,互不干扰

  • 持久性(Durability):已经提交的事务对数据库的影响是持久的,不可临时退回

1.3 Spring Boot 声明式事务

  Spring Boot框架中整合了Spring事务的管理功能,允许在业务逻辑层方法上添加事务管理的注解,一旦添加该注解,此方法中的所有数据库操作被管理为一个事务,即要么都执行,要么都不执行,就能解决上面的问题了。在这个方法运行过程中,如果没有发生异常,事务中数据库的操作就会正常提交生效;如果发生了异常,该方法中所有数据库操作全部取消,回滚(恢复)为运行之前的状态。

在实现类QuestionServiceImpl的saveQuestion方法上添加@Transactional注解:

 //新增用户发布的问题
 @Override
 //开启这个方法的事务管理,效果时该方法中所有数据库操作管理为一个事务
 // 如果运行正常,则所有操作生效;如果发生异常;则回滚为运行前状态
 @Transactional
 public void saveQuestion(QuestionVo questionVo, String username) {
  //代码略....
 }

为了安全起见,可以在前面接收注册表单数据的vo类RegisterVo上也加上事务管理的注解。

 

2.统一异常处理

2.1 为什么需要统一异常处理?

  在注册和发布问题的控制层代码中,需要对业务逻辑层进行异常处理,这样的异常处理在以后的功能中会不断出现,这样异常处理的代码就会形成冗余,代码显得臃肿,不方便维护。

2.2 Spring Mvc的统一异常处理

  由于开发过程中大多是由控制器承担异常处理的,所以SpringMvc框架提供了一套异常处理的机制,能够自动处理控制层中发生的各种异常,在控制层代码中就不需要编写try-catch了。我们只需要根据SpringMvc提供的格式编写一个类即可,在controller包中创建类ExceptionControllerAdvice,代码如下:

 package cn.tedu.knows.portal.controller;
 ?
 import cn.tedu.knows.portal.exception.ServiceException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 //@RestControllerAdvice:表示当前类是给其他控制器类新增功能的
 //我们这里的功能指异常处理
 @RestControllerAdvice
 @Slf4j //输出日志显示异常信息
 public class ExceptionControllerAdvice {
     //当前类的目标是统一处理控制器中的各种异常
     //控制器中无需再编写任何try-catch结构,减少代码冗余,专心处理业务
     @ExceptionHandler //表示是处理异常的方法
     public String handlerServiceException(ServiceException e){
         //这个方法来处理控制器方法发生ServiceException类型异常(自动运行)
         log.error("业务异常",e);//不需要写{}
         return e.getMessage();
    }
 ?
     @ExceptionHandler //此处相当于多重catch捕获异常
     public String handlerException(Exception e){
         log.error("其它异常",e);
         return e.getMessage();
    }
 }
 ?

统一异常处理类运行流程:

SpringBoot聚合项目:达内知道(七)-声明式事务、统一异常处理、文件上传

 

3.文件上传

3.1 什么是文件上传?

  即客户端将文件复制到服务器的过程,我们在static文件夹下创建一个可以上传文件的页面如下:upload.html

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>文件上载演示</title>
 </head>
 <body>
     <form id="demoForm" method="post"
           enctype="multipart/form-data"
           action="/upload/file" >
         <div>
             <label>上传文件
                 <input id="imageFile" type="file" name="imageFile">
             </label>
         </div>
         <button type="submit">上传文件</button>
     </form>
     <img id="image" src=""  alt="">
 </body>
 </html>

3.2 实现简单的同步上传

在SystemController类中添加如下方法:

 @PostMapping("/upload/file")
 //MultipartFile表示要上传文件
 public String uploadFile(MultipartFile imageFile)
                                     throws IOException {
     //确定上传的文件夹,该路径在电脑中一定要存在
     File folder=new File("F:/upload");
     //创建这个目录
     folder.mkdirs();//mkdirsssssssss!!!!!可以创建多级目录
     //获得用户上传的文件名(原始)
     String fileName=imageFile.getOriginalFilename();
     log.debug("获得的文件名为:{}",fileName);//类上添加了@Slf4j注解
     //创建文件,代表要保存的文件路径和名称
     //F:/upload/a.jpg
     File file=new File(folder,fileName);
     log.debug("复制到路径:{}",file.getAbsolutePath());//file类型:文件路径,String类型:文件名,联合起来就是一个完整路径
     //执行上传
     imageFile.transferTo(file);
     return "upload success!";
 }

测试效果:

(1)浏览器

SpringBoot聚合项目:达内知道(七)-声明式事务、统一异常处理、文件上传

(2)控制台

SpringBoot聚合项目:达内知道(七)-声明式事务、统一异常处理、文件上传

3.3 实现异步上传

修改upload.html:

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>文件上载演示</title>
     <!--添加引用-->
     <script src="bower_components/jquery/dist/jquery.min.js"></script>
     <!--引入axios框架-->
     <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js">
     </script>
 </head>
 <body>
 <form id="demoForm"  ><!--删除demoForm后面的代码-->
     <div>
         <label>上传文件
             <input id="imageFile" type="file" name="imageFile">
         </label>
     </div>
     <button type="submit">上传文件</button>
 </form>
 <img id="image" src=""  alt="">
 <!--引入js文件-->    
 <script src="js/utils.js"></script>
 <!--编写提交方法-->
 <script>
 $("#demoForm").submit(function(){
     console.log("上传文件方法运行");
     // 获得用户选中的文件(数组)
     let files=document.getElementById("imageFile").files;
     // 判断用户是否选择了文件
     if(files.length>0){
         //用户选中的文件执行上传
         //我们单独编写一个方法执行上传
         uploadImage(files[0]);//数组中的第一张图片
    }else{
         alert("请选择文件");
    }
     // 阻止表单提交
     return false;
 })
     
 //上传文件的方法
 function uploadImage(file){
     // 构建表单
     let form=new FormData();
     form.append("imageFile",file);
     // axios提交数据
 }
 ?
 </script>
 </body>
 </html>

 

SpringBoot聚合项目:达内知道(七)-声明式事务、统一异常处理、文件上传

上一篇:Java学习路线


下一篇:SpringCloud学习(1)