一、分页(pagehelper)
pagehelper 是一个强大实用的 MyBatis 分页插件,可以帮助我们快速的实现MyBatis分页功能,而且pagehelper有个优点是,分页和Mapper.xml完全解耦,并以插件的形式实现,对Mybatis执行的流程进行了强化,这有效的避免了我们需要直接写分页SQL语句来实现分页功能。
github项目地址:https://github.com/pagehelper/Mybatis-PageHelper
中文帮助:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md
1.1、快速起步
1.1.1、添加依赖
代码语言:javascript复制 <dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
这里需要注意也MyBatis的兼容问题,如果springboot pagehelper插件启动报错 [com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration]则需要更换版本,我使用2.5.13的Spring Boot与1.3.0的pagehelper是兼容的,示例项目完整的pom如下:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zhangguo</groupId>
<artifactId>mybatisdemo3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatisdemo3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.1.2、添加配置
在application.yaml文件中添加如下配置信息:
代码语言:javascript复制# pagehelper
pagehelper:
helperDialect: mysql #数据库类型
reasonable: true #查询合理化 当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页
supportMethodsArguments: true #支持方法参数 支持通过 Mapper 接口参数来传递分页参数
params: count=countSql #参数
1.1.3、数据访问接口
StudentDao.java:
代码语言:javascript复制package com.zhangguo.mybatisdemo3.dao;
import com.zhangguo.mybatisdemo3.entity.Student;
import java.util.List;
public interface StudentDao {
//查询学生并分页
public List<Student> selectPager();
}
1.1.4、接口映射
resource/mapper/StudentDao.xml:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhangguo.mybatisdemo3.dao.StudentDao">
<select id="selectPager" resultType="Student">
SELECT
student.id,
student.`name`,
student.sex
FROM
student
</select>
</mapper>
1.1.4、实现分页
StudentService.java:
代码语言:javascript复制package com.zhangguo.mybatisdemo3.service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhangguo.mybatisdemo3.dao.StudentDao;
import com.zhangguo.mybatisdemo3.entity.Student;
import com.zhangguo.mybatisdemo3.util.PageRequest;
import com.zhangguo.mybatisdemo3.util.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentService {
@Autowired
StudentDao studentDao;
public PageInfo<Student> selectPager(int pageNum,int pageSize){
//开始分页,指定页码与每页记录数
PageHelper.startPage(pageNum,pageSize);
//执行查询,请求会被分页插件拦截
List<Student> students = studentDao.selectPager();
//返回分页对象与数据
return new PageInfo<Student>(students);
}
}
1.1.5、调用分页方法
PageController.java:
代码语言:javascript复制package com.zhangguo.mybatisdemo3.controller;
import com.github.pagehelper.PageInfo;
import com.zhangguo.mybatisdemo3.entity.Student;
import com.zhangguo.mybatisdemo3.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PageController {
@Autowired
StudentService studentService;
@GetMapping
public PageInfo<Student> hello(@RequestParam(value = "pageNum",required = false,defaultValue = "1") int pageNum, @RequestParam(value = "pageSize",required = false,defaultValue = "3")int pageSize){
return studentService.selectPager(pageNum,pageSize);
}
}
pageNum参数用于指定页号,默认值为1,pageSize用于指定每页记录数,默认值为3。
运行结果:
默认值情况
带参数情况
pageNum:当前页的页码 pageSize:每页显示的条数 size:当前页显示的真实条数 total:总记录数 pages:总页数 prePage:上一页的页码 nextPage:下一页的页码 isFirstPage/isLastPage:是否为第一页/最后一页 hasPreviousPage/hasNextPage:是否存在上一页/下一页 navigatePages:导航分页的页码数 navigatepageNums:导航分页的页码,[1,2,3,4,5]
1.2、封装请求与结果
默认情况下请求参数并没有使用对象封装,返回结果包含冗余信息且需要与具体的业务关联。
1.2.1、请求参数封装
PageRequest.java
代码语言:javascript复制package com.zhangguo.mybatisdemo3.util;
/**
* 分页请求
*/
public class PageRequest {
/**
* 当前页码
*/
private int pageNum;
/**
* 每页数量
*/
private int pageSize;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
1.2.2、响应结果封装
PageResult.java
代码语言:javascript复制package com.zhangguo.mybatisdemo3.util;
import com.github.pagehelper.PageInfo;
import java.util.List;
/**
* 分页返回结果
*/
public class PageResult {
/**
* 当前页码
*/
private int pageNum;
/**
* 每页数量
*/
private int pageSize;
/**
* 记录总数
*/
private long totalSize;
/**
* 页码总数
*/
private int totalPages;
/**
* 数据模型
*/
private List<?> content;
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getTotalSize() {
return totalSize;
}
public void setTotalSize(long totalSize) {
this.totalSize = totalSize;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
public List<?> getContent() {
return content;
}
public void setContent(List<?> content) {
this.content = content;
}
/**
* 将分页信息封装到统一的接口
* @return
*/
public static PageResult getPageResult(PageInfo<?> pageInfo) {
PageResult pageResult = new PageResult();
pageResult.setPageNum(pageInfo.getPageNum());
pageResult.setPageSize(pageInfo.getPageSize());
pageResult.setTotalSize(pageInfo.getTotal());
pageResult.setTotalPages(pageInfo.getPages());
pageResult.setContent(pageInfo.getList());
return pageResult;
}
}
二、跨域
2.1、跨域概要
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
例如:a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。注意:跨域限制访问,其实是浏览器的限制。理解这一点很重要!!!
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
2.2、返回新的CorsFilter(全局跨域)
CORS,全称Cross-Origin Resource Sharing ,是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。
在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。
代码语言:javascript复制@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送 Cookie
config.setAllowCredentials(true);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}
2.3、重写WebMvcConfigurer(全局跨域)
代码语言:javascript复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}
2.4、使用注解 (局部跨域)
在控制器(类上)上使用注解 @CrossOrigin:,表示该类的所有方法允许跨域。
代码语言:javascript复制@RestController
@CrossOrigin(origins = "*")
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello world";
}
}
在方法上使用注解 @CrossOrigin:
代码语言:javascript复制 @RequestMapping("/hello")
@CrossOrigin(origins = "*")
//@CrossOrigin(value = "http://localhost:8081") //指定具体ip允许跨域
public String hello() {
return "hello world";
}
2.5、手动设置响应头(局部跨域)
使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 “*”,表示全部放行。
代码语言:javascript复制@RequestMapping("/index")
public String index(HttpServletResponse response) {
response.addHeader("Access-Allow-Control-Origin","*");
return "index";
}
2.6、使用自定义filter实现跨域
首先编写一个过滤器,可以起名字为MyCorsFilter.java
代码语言:javascript复制package cn.wideth.aop;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
@Component
public class MyCorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
三、上传
前端使用Vue Axios实现AJAX上传文件,upfile.html如下:
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传文件示例</title>
</head>
<body>
<div id="app">
<h2>上传文件示例</h2>
<input type="file" @change="getfiles($event)"/>
<button type="button" @click="upfiles($event)">提交</button>
</div>
<script src="vue/vue.js"></script>
<script src="axios/axios.min.js"></script>
<script>
var app=new Vue({
el:"#app",
data:{
file:{}
},
methods:{
getfiles(event){
this.file=event.target.files[0];
console.log(this.file);
},
upfiles:function(event){
event.preventDefault;
var formdata=new FormData();
formdata.append("file",this.file);
axios.post("http://localhost:8080/upfile",formdata,{
Headers:{
"Content-Type":"multipart/form-data"
}
}).then(function(response){
console.log(response);
console.log("ok");
}).catch(function(error){
console.log(error);
});
}
}
});
</script>
</body>
</html>
application.yaml文件
代码语言:javascript复制#文件的限制大小
servlet:
multipart:
max-file-size: 100MB #文件最大值
max-request-size: 100MB #请求最大值
prop:
up-folder: F:NFSpring bootdemosCORSDemo1uploads #上传目标位置
后端控制器
代码语言:javascript复制package com.zhangguo.mybatisdemo3.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@RestController
public class UpfileController {
@Value("${prop.up-folder}")
String path;
@PostMapping("/upfile")
public String upfile(@RequestPart("file") MultipartFile file) throws IOException {
String oldname=file.getOriginalFilename();
//新的文件名=UUID 原文件后缀名
String newname= UUID.randomUUID().toString() oldname.substring(oldname.lastIndexOf("."));
file.transferTo(new File(path file.getOriginalFilename()));
return newname;
}
}
运行效果
四、启动Banner定制
我们在应用启动的时候,可以看到控制台显示了Spring的Banner信息,我们可以通过定制这个功能,来放置我们自己的应用信息。
如果要定制自己的Banner, 只需要在 resources 下放置一个 banner.txt 文件,输入自己的banner字符即可。
重新启动项目
Banner字符可以通过类似以下网站生成:
http://patorjk.com/software/taag
http://www.network-science.de/ascii/
五、lombok
5.1、lombok概要
Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。
在项目中使用Lombok可以减少很多重复代码的书写。比如说getter/setter/toString等方法的编写。
“Boilerplate”是一个术语,用于描述在应用程序的许多部分中很少改动就重复的代码。对Java语言最常见的批评就是在大多数项目中都可以找到这种类型的代码,由于语言本身的局限性而更加严重。龙目岛计划(Project Lombok)旨在通过用简单的注释集代替众多的代码。
Lombok也存在一定风险,在一些开发工具商店中没有Project Lombok支持选择。 IDE和JDK升级存在破裂的风险,并且围绕项目的目标和实施存在争议。
常用注解:
- @Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
- @Getter :使用方法同上,区别在于生成的是getter方法。
- @ToString :注解在类,添加toString方法。
- @EqualsAndHashCode: 注解在类,生成hashCode和equals方法。
- @NoArgsConstructor: 注解在类,生成无参的构造方法。
- @RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
- @AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。
- @Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
- @Slf4j: 注解在类,生成log变量,严格意义来说是常量。
5.2、引入依赖
在pom文件中添加如下部分。(不清楚版本可以在Maven仓库中搜索)
代码语言:javascript复制 <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
5.3、使用注解
代码语言:javascript复制import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@AllArgsConstructor // 注解在类,生成包含类中所有字段的构造方法。
@NoArgsConstructor //注解在类,生成无参的构造方法。
public class Department {
private Integer departmentId;
private String departmentName;
}
5.4、运行测试
测试类:
代码语言:javascript复制package com.zhangguo.mybatisdemo3;
import com.zhangguo.mybatisdemo3.entity.Department;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class DepartmentTest {
@Test
public void lombokTest(){
Department department=new Department(1,"研发部");
System.out.println(department);
}
}
测试结果:
代码语言:javascript复制Department(departmentId=1, departmentName=研发部)
可以看到有带参构造方法,toString方法也被重写过了。
需要注意的是新版本的IDEA不再需要安装插件,已经默认整合了。