Application.yml 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| server: port: 8181 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8 username: root password: 421232 mybatis: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl type-aliases-package: online.zorange.springboot.entity
|
sql 语句写的位置
-
注解

- mybatis 的 xml 文件里面

跨域问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package online.zorange.springboot.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter;
@Configuration public class CorsConfig { private static final long MAX_AGE = 24 * 60 * 60;
@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOriginPattern("http://localhost:8080"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.setMaxAge(MAX_AGE); source.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(source); } }
|
忽略某个字段,不展示给前端
1 2
| @JsonIgnore private String password;
|

Mybatis plus 实体类的注解

后端分页(UserController.java)
使用 Mybatis-Plus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
@GetMapping("/page") public IPage<User> findByPage( @RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam(defaultValue = "") String userName, @RequestParam(defaultValue = "") String email, @RequestParam(defaultValue = "") String address){ IPage<User> page= new Page<>(pageNum,pageSize); QueryWrapper<User> wrapper=new QueryWrapper<>(); wrapper.like("username",userName);
wrapper.like("email",email); wrapper.like("address",address); return userService.page(page,wrapper);
}
|
Mapper.interface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
@GetMapping("/page") public Map<String, Object> findByPage( @RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam String userName, @RequestParam String email, @RequestParam String address){ Integer total=userMapper.selectTotal(userName,email,address); pageNum=(pageNum-1)*pageSize; List<User> data=userMapper.selectPage(pageNum,pageSize,userName,email,address);
Map<String, Object> res=new HashMap<>(); res.put("data",data); res.put("total",total); return res; }
|
mybatisPlus 配置
pom.xml 依赖
1 2 3 4 5 6
| <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency>
|
mybatisPlusConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @MapperScan("online.zorange.springboot.mapper") public class MybatisPlusConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
|
axios
1. (npm i axios -S)
封装代码
src/utils/request.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import axios from "axios";
const request = axios.create({ baseURL: "/api", timeout: 5000, });
request.interceptors.request.use( (config) => { config.headers["Content-Type"] = "application/json;charset=utf-8";
return config; }, (error) => { return Promise.reject(error); } );
request.interceptors.response.use( (response) => { let res = response.data; if (response.config.responseType === "blob") { return res; } if (typeof res === "string") { res = res ? JSON.parse(res) : res; } return res; }, (error) => { console.log("err" + error); return Promise.reject(error); } );
export default request;
|
main.js

使用:

2. vue add axios
使用:

返回的数据:

路由
子路由
1
| children: [ { 子路由一 }, { 子路由二 } ]
|

- 创建一个 components.vue
- 将需要的组件复制过来
- 定义自定义的值

- 导入 components

- 引用+传值

Excel 导出
hutool
安装依赖
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.20</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency>
|
别名
在导入和导出时,使用别名,
在entity
对象加个hutool
的Alias
注解
1 2 3 4 5 6 7 8 9 10
| import cn.hutool.core.annotation.Alias;
@Alias("用户名") @ApiModelProperty("用户名") private String username;
@Alias("密码") @ApiModelProperty("密码") private String password;
|
用了注解之后下面代码里面起别名就可以去掉了。
导出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @GetMapping("/export") public void export(HttpServletResponse response) throws Exception {
List<User> list = userService.list();
ExcelWriter writer = ExcelUtil.getWriter(true);
writer.addHeaderAlias("id","编号"); writer.addHeaderAlias("username","用户名"); writer.addHeaderAlias("password","密码"); writer.addHeaderAlias("nickname","昵称"); writer.addHeaderAlias("email","邮箱"); writer.addHeaderAlias("phone","电话"); writer.addHeaderAlias("address","地址"); writer.addHeaderAlias("creatTime","创建时间");
writer.write(list);
response.setContentType("application/vnd.ms-excel;charset=utf-8");
String fileName = URLEncoder.encode("用户列表", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename="+fileName+".xls");
ServletOutputStream out = response.getOutputStream(); writer.flush(out,true); out.close(); writer.close(); }
|
导入
后端
1 2 3 4 5 6 7 8 9 10 11 12 13
| @PostMapping("/import") public boolean importUser(MultipartFile file) throws Exception { InputStream in = file.getInputStream(); ExcelReader reader = ExcelUtil.getReader(in); List<User> list = reader.readAll(User.class); System.out.println(list); return userService.saveBatch(list); }
|
前端
element 的组件
1 2 3 4 5 6 7 8 9
| <el-upload action="http://localhost:8181/user/import" style="display: inline-block" :show-file-list="false" :accept="xlsx" :on-success="handleExcelSuccess" > <el-button style="margin: 5px" type="primary" >导入<i class="el-icon-download"></i></el-button> </el-upload>
|
日志打印:
Spring Boot 层

common 统一包装
config 过滤层
controller 控制
entity 实体类
Dao 数据包装类
exception 自定义异常
mapper 接口
service 服务
utils 工具
common 统一包装
constants.java
定义常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package online.zorange.springboot.common;
public interface Constants { String CODE_SUCCESS = "200"; String MSG_SUCCESS = "操作成功";
String CODE_ERROR = "500"; String MSG_ERROR = "系统错误"; String CODE_PARAM_ERROR = "400"; String MSG_PARAM_ERROR = "参数错误"; String CODE_OTHER_ERROR = "501"; String MSG_OTHER_ERROR = "其他业务异常";
String CODE_NOT_LOGIN = "401"; String MSG_NOT_LOGIN = "权限不足";
}
|
Result.java
接口,统一返回包装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package online.zorange.springboot.common;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor public class Result { private String code; private String msg; private Object data; public static Result success(){ return new Result(Constants.CODE_SUCCESS,Constants.MSG_SUCCESS,null); } public static Result success(Object data){ return new Result(Constants.CODE_SUCCESS,Constants.MSG_SUCCESS,data); } public static Result error(){ return new Result(Constants.CODE_ERROR,Constants.MSG_ERROR,null); } public static Result error(String msg){ return new Result(Constants.CODE_ERROR,msg,null); } public static Result error(String code,String msg){ return new Result(code,msg,null); } }
|
exception 自定义异常处理
GlobalExceptionHandler.java
全局异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package online.zorange.springboot.exception;
import online.zorange.springboot.common.Result; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice public class GlobalExceptionHandler {
@ExceptionHandler(ServiceException.class) @ResponseBody public Result handle(ServiceException e){ return Result.error(e.getCode(),e.getMessage()); } }
|
ServiceException.java
自定义服务类异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13
| package online.zorange.springboot.exception;
import lombok.Getter;
@Getter public class ServiceException extends RuntimeException{ private final String code; public ServiceException(String code, String msg){ super(msg); this.code=code; } }
|
JWT登录
依赖
1 2 3 4 5 6
| <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency>
|
生成token
utils.tokenUtil.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package online.zorange.springboot.utils;
import cn.hutool.core.date.DateUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import online.zorange.springboot.entity.User;
import java.util.Date;
public class TokenUtil { public static String genToken(User user){ return JWT.create().withAudience(String.valueOf(user.getId())) .withExpiresAt(DateUtil.offsetHour(new Date(),2)) .sign(Algorithm.HMAC256(user.getPassword())); } }
|
前端放开请求头
1 2 3 4
| let user=localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : ''; if(user){ config.headers['token'] = user.token; }
|
创建拦截器
config.interceptor.JWTinterceptor.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| package online.zorange.springboot.config.interceptor;
import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import online.zorange.springboot.common.Constants; import online.zorange.springboot.entity.User; import online.zorange.springboot.exception.ServiceException; import online.zorange.springboot.service.IUserService; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class JwtInterceptor implements HandlerInterceptor { private IUserService userService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token= request.getHeader("token"); if(!(handler instanceof HandlerMethod)){ return true; } if(token.equals("")){ throw new ServiceException(Constants.CODE_NOT_LOGIN,"无token"); } String userId; try{ userId=JWT.decode(token).getAudience().get(0); }catch (JWTDecodeException j){ throw new ServiceException(Constants.CODE_NOT_LOGIN,"token验证失败"); } User user=userService.getById(userId); if(user==null){ throw new ServiceException(Constants.CODE_NOT_LOGIN,"用户不存在,请重新登录"); } JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(user.getPassword())).build(); try{ jwtVerifier.verify(token); }catch (JWTVerificationException j){ throw new ServiceException(Constants.CODE_NOT_LOGIN,"token验证失败,请重新登录"); }
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
|
注册拦截器
config.interceptorCongif.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package online.zorange.springboot.config;
import online.zorange.springboot.config.interceptor.JwtInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration public class interceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { WebMvcConfigurer.super.addInterceptors(registry); registry.addInterceptor(jwtInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/user/login","user/register","**/export","**/import"); } @Bean public JwtInterceptor jwtInterceptor(){ return new JwtInterceptor(); } }
|
文件操作
创建数据库表
1 2 3 4 5 6 7 8 9 10 11
| CREATE TABLE `file` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名称', `type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件类型', `size` bigint(20) DEFAULT NULL COMMENT '文件大小', `url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '链接', `is_delete` tinyint(1) unsigned zerofill DEFAULT '0' COMMENT ' 是否删除', `enable` tinyint(1) unsigned zerofill DEFAULT '1' COMMENT '是否禁用', `md5` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件md5', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
实体类entity层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package online.zorange.springboot.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;
@Data @TableName("file") public class File { @TableId(value = "id",type = IdType.AUTO) private String id; private String name; private String type; private Long size; private String url; private boolean is_delete; private boolean enable; }
|
文件controller层
controller.FileController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package online.zorange.springboot.controller;
import online.zorange.springboot.common.Result; import online.zorange.springboot.service.IFileService; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException;
@RestController @RequestMapping("/file") public class FileController { @Resource private IFileService fileService;
@PostMapping("/upload") public String upload(@RequestParam MultipartFile file) throws IOException { return fileService.upload(file); }
@GetMapping("/download/{fileUuid}") public Result download(@PathVariable String fileUuid, HttpServletResponse response) throws IOException { return fileService.download(fileUuid, response); }
}
|
先保存在磁盘里面

文件Srevice层
FileServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| package online.zorange.springboot.service.impl;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.crypto.SecureUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import online.zorange.springboot.common.Result; import online.zorange.springboot.entity.Files; import online.zorange.springboot.mapper.FileMapper; import online.zorange.springboot.service.IFileService; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.URLEncoder;
@Service public class FileServiceImpl extends ServiceImpl<FileMapper, Files> implements IFileService {
@Value("${files.upload.path}") private String uploadPath;
@Override public String upload(MultipartFile file) throws IOException { String originalFilename = file.getOriginalFilename(); String type = FileUtil.extName(originalFilename); long size = file.getSize(); File upLoadParentFile = new File(uploadPath); if (!upLoadParentFile.exists()) { upLoadParentFile.mkdirs(); }
String uuid = IdUtil.fastSimpleUUID(); String FileUuid = uuid + "." + type;
File upLoadFile = new File(uploadPath + FileUuid); String md5 = SecureUtil.md5(file.getInputStream()); String Url; Files one = getFilesMd5(md5); if (one != null) { return one.getUrl(); } else { file.transferTo(upLoadFile); Url = "http://localhost:8181/file/download/" + FileUuid; } Files saveFile = new Files(); saveFile.setName(originalFilename); saveFile.setType(type); saveFile.setSize(size / 1024); saveFile.setUrl(Url); saveFile.setMd5(md5); this.save(saveFile); return Url; }
@Override public Result download(String fileUuid, HttpServletResponse response) throws IOException { File file = new File(uploadPath + fileUuid); ServletOutputStream os = response.getOutputStream(); response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileUuid, "UTF-8")); response.setContentType("application/octet-stream"); os.write(FileUtil.readBytes(file)); os.flush(); os.close(); return Result.success("下载成功"); }
private Files getFilesMd5(String md5){ QueryWrapper<Files> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("md5", md5); Files one = this.getOne(queryWrapper); if (one != null) { return one; } else { return null; } } }
|
创建对应的Mapper,Mapper.xml,IService,ServiceImpl
- 添加:header属性
1 2 3 4 5 6 7 8 9
| <el-upload class="avatar-uploader" action="http://localhost:8181/file/upload" :headers="headersO" :show-file-list="false" :on-success="handleAvatarSuccess"> <img v-if="form.avatar" :src="form.avatar" class="avatar"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload>
|
- 设置headersO
1 2 3 4 5 6 7 8
| computed:{ headersO(){ const token = this.user.token return { token: token } } },
|
问题:
mybatis-plus
1
描述:
改用 mybatis-plus 之后,改用 updateOrSave 或者 updateById 都会
报错:can not execute. because can not find cache of TableInfo for entity!
@TableName 和@TableId 都加了也不行
解决:
降低 springboot 的版本
2 request 未定义
描述:
定义 request.js , 使用 Vue.prototype.request = request;引入
使用 request 报错:未定义
解决:
- import request from ‘@/utils/request.js’
- 使用时加 this
