一、分页(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、添加依赖
<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如下:
<?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文件中添加如下配置信息:
# pagehelper pagehelper: helperDialect: mysql #数据库类型 reasonable: true #查询合理化 当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页 supportMethodsArguments: true #支持方法参数 支持通过 Mapper 接口参数来传递分页参数 params: count=countSql #参数
1.1.3、数据访问接口
StudentDao.java:
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:
<?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:
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:
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
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
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)浏览器会禁止这种跨域请求。
示例:
<!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>Document</title> </head> <body> <script src="axios/axios.min.js"></script> <script> axios.get("http://localhost:8080/students").then(response=>{ console.log(response.data); }).catch(error=>{ console.log(error); }); </script> </body> </html>
前端访问提示
在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。
package com.zhangguo.mybatis4.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 GlobalCorsConfig { @Bean public CorsFilter corsFilter() { //1. 添加 CORS配置信息 CorsConfiguration config = new CorsConfiguration(); //放行哪些原始域 config.addAllowedOrigin("http://127.0.0.1:5500/"); //是否发送 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(全局跨域)
@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:,表示该类的所有方法允许跨域。
@RestController @CrossOrigin(origins = "*") public class HelloController { @RequestMapping("/hello") public String hello() { return "hello world"; } }
在方法上使用注解 @CrossOrigin:
@RequestMapping("/hello") @CrossOrigin(origins = "*") //@CrossOrigin(value = "http://localhost:8081") //指定具体ip允许跨域 public String hello() { return "hello world"; }
2.5、手动设置响应头(局部跨域)
使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 “*”,表示全部放行。
@RequestMapping("/index") public String index(HttpServletResponse response) { response.addHeader("Access-Allow-Control-Origin","*"); return "index"; }
2.6、使用自定义filter实现跨域
首先编写一个过滤器,可以起名字为MyCorsFilter.java
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如下:
<!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="selectFile($event)"/> <input type="button" value="上传" @click="upfile($event)" /> <p> <img :src="'files/'+filename" v-show="isUpload"/> </p> </div> <script src="vue/vue.js"></script> <script src="axios/axios.min.js"></script> <script> var app = new Vue({ el: "#app", data:{ file:null, filename:'', isUpload:false }, methods:{ selectFile(event){ //从input的文件集合中获取第1个文件对象,就是要上传的文件对象 this.file=event.target.files[0]; console.log(this.file); }, upfile(event){ event.preventDefault(); //阻止默认事件 //创建一个表单数据对象 var formdata=new FormData(); //向表单数据对象中添加表单数据,指定名称与数据内容 formdata.append("file",app.file); //使用axios上传数据 axios.post("http://localhost:8080/upfile",formdata,{ Headers:{ "Content-Type":"multipart/form-data" } }).then(response=>{ console.log(response.data); //上传成功了 app.isUpload=true; //指定上传成功后的文件名 app.filename=response.data; }).catch(error=>{ console.log(error); }); return false; } } }); </script> </body> </html>
application.yaml文件
#文件的限制大小
servlet:
multipart:
max-file-size: 100MB #文件最大值
max-request-size: 100MB #请求最大值
prop:
up-folder: F:\NF\Spring boot\demos\CORSDemo1\uploads\ #上传目标位置
后端控制器
package com.zhangguo.mybatis4.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.UUID; @RestController public class UpController { @Value("${prop.up-folder}") String upFolder; @PostMapping("/upfile") public String upfile(@RequestPart MultipartFile file) throws IOException { //获取旧文件名称 String oldname=file.getOriginalFilename(); //生成新文件名称 String newname= UUID.randomUUID().toString()+oldname.substring(oldname.lastIndexOf(".")); //文件保存路径 String path=upFolder+newname; //保存文件 file.transferTo(new File(path)); return newname; } }
运行效果
在vscode中因为安装了Live Server会实时的刷新页面,上传后会自动刷新,可以忽视*.png与*.jpg文件上传到前端目录中,设置如下:
点击设置
点击扩展设置,找到如下项
在中间添加两项
保存配置即可。
四、启动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仓库中搜索)
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
5.3、使用注解
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、运行测试
测试类:
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); } }
测试结果:
Department(departmentId=1, departmentName=研发部)
可以看到有带参构造方法,toString方法也被重写过了。
需要注意的是新版本的IDEA不再需要安装插件,已经默认整合了。
六、视频
https://www.bilibili.com/video/BV1fi4y1S79P?share_source=copy_web
https://space.bilibili.com/87194917/video
作业解答:https://www.bilibili.com/video/BV1Hs411F71x?share_source=copy_web