文章目录
想着做一个小项目熟悉一下Java web和后端的情况,做一个项目吧,本来想着图书管理系统之类的,然后换成这个在线聊天系统。
目前设计的主要功能有
- 聊天:私聊、群聊、离线消息、文件传输
- 设置
暂定用到的技术
- 后端:SpringBoot、MyBatis,log4j,Neety,redis
- 前端:bootstrap,vue(要不要不知道,应该不需要)
框架
这里就说一下一些框架或者组件的引用吧,比如起码得知道怎么加载和使用
SpringBoot
创建
使用IDEA,有两种方式创建
使用maben
使用Spring Initializr
步骤:
- 新建项目
- 选择Spring Intializr
- URL选择默认的
https://start.spring.io/
,下一步 - 填写相应的项目信息,选择Java8
- 选择依赖,这里只使用一个
Web - Spring web
- 然后再填写相应的信息,下一步创建完成
此时如果第一次使用,它会一直解析依赖,就是会下载然后进行一些解析的操作,可能会比较费时间,启动程序的话需要等他下载好了才能启动。
启动
创建完以后,会有一个*Application
的类,有一个SpringBootApplication
注解,开启了自动配置,这个main()方法就是用于启动的方法。
但是在启动的时候需要一个新的文件MainController.java
在com.rbk.freechat
下面创建一个新的包叫controller
,创建一个文件MainController
filename:MainController.java
package com.rbk.freechat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MainController {
@ResponseBody
@RequestMapping("/test")
public String test(){
return "hello,this is a test function to ensure server is ok";
}
}
随后启动程序,访问127.0.0.1:8080/test
就可以看到回显的信息了。
注解
路由
有GetMapping和RequestMapping
,大致区别就是,前者只能处理Get请求,后者两个都可以
@RequestMapping(value=“test”,method=RequestMethod.GET)
@GetMapping(value=“test”)
好像可以有多个指向
@GetMapping(value={“/”,“/test”})
处理Json
可以使用开源的组件实现,比如Gson、fastjson等等,但是感觉都挺麻烦的,甚至SpringBoot自带的jackson也挺麻烦。
这个感觉就挺好的。
在com.rbk.freechat
下面创建一个新的包叫utils
,创建一个文件ResultJson.java
.
然后编写ResultJson.java
类。
这里使用到了@Data
注解,依赖于lombok
,这个可以给变量提供一些方法,比如getter、setter、toString等,这几个也可以变成注解单独使用,第一次使用的时候需要解析lombok这个依赖。
通过链式调用data()
方法可以对Body的数据追加或者重置。
切记这个类的返回值应该是ResultJson
类型。
filename:ResultJson.java
package com.rbk.freechat.utils;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class ResultJson {
private Boolean Status;
private String Message;
private int StatusCode;
private Map<String,Object> ContentData=new HashMap<>();
public ResultJson(){}
public static ResultJson response(Boolean Status,int StatusCode,String Message){
ResultJson Result=new ResultJson();
Result.setStatus(Status);
Result.setMessage(Message);
Result.setStatusCode(StatusCode);
return Result;
}
public ResultJson data(String Key,Object Value){
this.ContentData.put(Key,Value);
return this;
}
public ResultJson data(Map<String,Object> Data){
this.setContentData(Data);
return this;
}
}
静态资源
网页会用到好多前端的东西,比如jq、vue、bootstrap、css等资源,SpringBoot是以Jar包的形式部署的,好像不好直接使用这些资源,使用WebJars实现这个。
WebJars可以将前端的资源打包成一个个Jar包,然后把包部署到maven里面,就可以了。
需要引入 Web 前端资源时,只需要访问 WebJars 官网,找到所需资源的 pom 依赖,将其导入到项目中即可。
其实大概意思就是,如果一些常见的包,可能已经有人封装好了,直接下载然后加载就好了。
再比如如果需要使用BootStrap框架,添加如下依赖
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
当需要访问项目里的资源的时候,如/**
,比如找某个html文件,会按照如下顺序寻找
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
引用的时候这么写都可以,路径就是resources/static/
,底下创建css和js文件夹就行了。可以对多个网站创建多个文件夹分别管理。
<head>
......
<link type="text/css" href="../css/test.css" rel="stylesheet">
<script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script>
</head>
在src/main/resources/statis/
创建一个html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test页面</title>
</head>
<body>
<h1>这是一个test测试页面</h1>
</body>
</html>
然后访问127.0.0.1:8080/test.html
就可以看到页面了。
如果在src/main/resources/public/
创建一个index.html文件,那直接访问域名或者IP就可以了,如127.0.0.1:8080或者127.0.0.1:8080/index.html
均可,当作主页了。其他名称的网页还得是后者,带上html名,只有index不需要。
Thymeleaf
Thymeleaf教程(10分钟入门)
源码理解SpringBoot视图解析(总体第四篇)
这是一个Java的模板引擎,文件后缀是.html,因此可以直接在浏览器打开,也可以使用web应用程序打开,就是支持动态展示。
比如对于如下语句,直接打开显示的就是静态页面字样,如果从web程序打开就会动态显示Thymeleaf字样。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--th:text 为 Thymeleaf 属性,用于在展示文本-->
<h1 th:text="迎您来到Thymeleaf">欢迎您访问静态页面 HTML</h1>
</body>
</html>
其实我感觉这个有点类似于Django的那种模板语法,
在使用之前需要在html标签里先声明一下命名空间xmlns:th=“http://www.thymeleaf.org”
。
一些语法
比如${person.lastName}
是使用这个变量的值
调用内置工具对象方法判断字符串相等${#strings.equals(‘编程帮’,name)}
对于链接可以使用@
符号,比如表单和静态资源link
- 没有参数的表单链接:@{/xxx}
- 有参数的表单链接:@{/xxx(k1=v1,k2=v2)}
- css链接:
<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
甚至可以应用某一个html文件里的某一个片段,可以在代码里使用 th:fragment
声明一个代码块,指定name引用
~{templatename::fragmentname}
~{templatename::#id}
也可以抽取公共页面,相当于Django的模板继承
甚至可以在模板里面添加参数
SpringBoot中的应用
首先修改pox.xml
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
默认的模板路径是/src/main/resources/templates/
下
编写一个测试htmltest-thymeleaf.html
filename:test-thymeleaf.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是测试thyseleaf的页面</title>
</head>
<body>
<h1 th:text="'欢迎来到'+${name}"></h1>
</body>
</html>
然后编写业务代码,新建一个类ThyseleafController.java
,这里不能加@RequestBody
这个参数了,加上好像就是直接返回字符串,不加的话会根据名称直接调用对应的视图html文件。
而且好像只能解析/resources/templates
里面的html文件,这个是在Thymeleaf的配置文件里面写的,默认路径就是templates
里面的。
filename:ThyseleafController.java
package com.rbk.freechat.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@Controller
public class Thymeleaf {
@RequestMapping("/test-thymeleaf")
public String testThymeleaf(Map<String,Object> Dict){
Dict.put("name","这是从java类中传入的");
return "test-thymeleaf";
}
}
fragment碎片
参考:5)Thymeleaf 模板布局 th:fragment、th:replace、th:insert、th:remove
这个提供fragment的功能,当html里面有代码被复用的时候,比如导航栏啊菜单栏这种,可以使用这个fragment直接复用base.html之类的基页面,避免重复编码。
比如说可以定义一个片段叫copy
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
然后就可以使用th:insert或者th:replace
进行填充,前面的footer就表示使用到的片段所在的html页面,copy就是上面设置的名称。〜{…}
这个可以选择,不用加也行
<body>
<div th:insert="~{footer :: copy}"></div> ...
</body>
insert和replace的区别
我感觉一般应该用include的方式,这样即可以使用模板基类,而且还可以定制化额外添加所需的
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
<!--2、采用如下三种方式进行引用-->
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
<!--3、则三种引用方式的效果分别对应如下-->
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
<header th:fragment="commonHeader">
<h2>基类123</h2>
</header>
第一种insert,会连带header标签一起传入
<header>
<header><header>
<h2>基类123</h2>
</header></header>
<h1>222</h1>
</header>
第三种include,会把基类标签中的内容放在这的标签下,即header。但是如果这样,在这个有include的 header里面添加的标签都没用
<header>
<header th:include="test-thymeleaf-dir/base :: commonHeader"></header>
<h1>222</h1>
</header>
变为
<header>
<header>
<h2>基类123</h2>
</header>
<h1>222</h1>
</header>
但是上面这三种都会覆盖现有的模板
如何即使用公共模板,又添加引入自己的css,js之类的呢
Thymeleaf引入公共的head,添加私有的css和js
这么搞
公共模板,加一个th:fragment
,对应标签里面用replace
<head lang="en" th:fragment="head(title,links,scripts)">
<meta charset="UTF-8">
<title th:text="${title}">公共模板</title>
<!-- 公共模板中,这个样式文件可以不用引用,因为不会从浏览器直接访问公共模板,而是访问具体的模块-->
<link rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.min.css}"
href="../static/bootstrap/css/bootstrap.min.css">
<!-- 注意:如果模板片段中应用了其中的样式,则谁引用了模板片段,谁就要导入此样式文件-->
<!--引入参数-->
<th:block th:replace="${links}"/>
<script th:replace="${scripts}"></script>
</head>
其余文件,主标签使用th:replace
,填入参数,三个参数分别把对应的值传入到模板页面,同时在这里可以编写自己使用的非公共的静态资源文件等
<head lang="en" th:replace="test-thymeleaf-dir/base :: head('首页',~{::link},~{::script})">
<meta charset="UTF-8">
<link rel="stylesheet" th:href="@{/css/index.css}">
<!-- 私有的js-->
<!-- "欢迎"文字动画js -->
<script th:src="@{/js/animatetext1.js}"></script>
</head>
扩展SpringMVC
可以通过定义一个类,添加注解@Configuration
,来进行扩展
比如这里创建一个类Config
,这个配置就是修改传入的URL然后修改为对应的视图,可以指定一个视图名,但是只能指定视图,不会执行对应的方法。
比如可以使得index或者index.html
都跳转到login.html
页面去
package com.rbk.freechat.utils;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry){
//registry.addViewController("/").setViewName("test-thymeleaf");
registry.addViewController("/").setViewName("../public/index");
}
}
拦截器
Spring Boot拦截器精讲
拦截器可以提供对请求的URL进行拦截操作,有点类似中间件。可以用于登录和权限的校验、性能监控、异常处理等等。
使用拦截器大概有三步
- 定义拦截器
- 注册拦截器
- 指定拦截规则
创建一个类,实现HandlerInterceptor
接口即可,实现如下三个方法
定义登录拦截器
如果没有对应的session,返回登陆页面,否则继续。
然后需要注意的就是getRequestDispatcher
这里面的参数应该是一个路由,不是一个具体的URL或者html路径
比如我这里的是/login
,他就会判断,如果没有登陆,会跳转到这个路由,然后就回去找对应的controller方法,执行,然后返回页面。
这里其实好像还可以换成sendRedirect()
,大概区别是
- 前者是选择转发,服务器端进行的
- 后者是重定向,浏览器进行的
可以通过session添加信息返回给前端展示出来,比如提示请登录字样
public class LoginInterceptor implements HandlerInterceptor {
Logger Log= LoggerFactory.getLogger(getClass());
/*
接收请求之前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
String URI=request.getRequestURI();
Log.info("拦截的资源路径:"+URI);
HttpSession session=request.getSession();
Object login_status=request.getSession().getAttribute("login_status");
if (null == login_status){
Log.info("不存在用户,需要登陆");
session.setAttribute("message","请先登录");
response.sendRedirect("/");
//request.setAttribute("message","请先登录");
//request.getRequestDispatcher("/").forward(request,response);
return false;
}else{
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Log.info("postHandle执行{}", modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
Log.info("afterCompletion执行异常{}", ex);
}
}
前端
在前端的登陆页面可以使用模板来展示我们后台传递过来的一些提示信息。
这里如果采用重定向的方式,把需要发送给前端的数据放在session里面,前端需要如下编写,要带上session.
<p style="color: red" th:text="${session.message}" th:if="${not #strings.isEmpty(session.message)}"></p>
注册拦截器
就在配置类里面,重写方法addInterceptors
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor());
}
指定拦截规则
同样修改配置类,这里先指定拦截所有请求,然后放行一些请求
这里/**
表示拦截所有请求,包括html和静态资源
对于一些资源是必须放行的,比如登陆页面以及静态资源,使用excludePathPatterns
放行
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login","/", "/css/**", "/image/**", "/js/**","/favicon.ico");
}
}
配置好了以后还有,需要使用
可以创建一个LoginController处理登录请求,这里的路由就是上面getRequestDispacter()
里面的参数,会返回显示对应的页面,在这里可以处理有关登录的。
最后最好用重定向吧,如果单独用一个网页可能不太能识别得出session?用重定向
@Controller
public class LoginController {
Logger Log= LoggerFactory.getLogger(getClass());
@RequestMapping(value={"/","/login"})
public String login(HttpServletRequest request, Map<String, Object> map, HttpSession session, HttpServletResponse response, @RequestParam Map<String,String> params) throws Exception{
String Method=request.getMethod();
if (Method.equals("GET")){
//GET请求
Log.info("[ GET请求 ]\t需要登录");
return "/user/login";
}else if (Method.equals("POST")){
//POST请求
String UserName=params.get("username");
String Password=params.get("password");
Log.info("[ POST请求 ]\t登录:"+UserName+"\t密码:"+Password);
request.getSession().setAttribute("isLogin","1");
Log.info("保存了吗"+request.getSession().getAttribute("isLogin"));
request.getRequestDispatcher("/index").forward(request, response);
return "redirect:/index";
}else{
return "/error_page";
}
}
}
最后是主页的Controller。
这里这样获取session可以获取到,而且也可以成功通过拦截器进入其他页面
@Controller
public class MainController {
Logger Log= LoggerFactory.getLogger(getClass());
@RequestMapping(value={"/index"})
public String index(HttpServletRequest request){
Object LoginStatus=request.getAttribute("login_status");
Log.info("进入主页");
return "/index";
}
/**
* 测试页面
* @return
*/
@ResponseBody
@RequestMapping("/getSession")
public String test(HttpServletRequest request){
String LoginStatus=(String)request.getSession().getAttribute("login_status");
Log.info(LoginStatus);
Log.info("this is a log for getSession()");
return "hello,this is a test function to ensure server is ok";
}
}
获取参数
springBoot中前端通过表单(form)向后端发送数据,后端接收数据的几种方式
数据从前端通过json传递到后端,后端需要进行接收然后才能够处理
有4种方式,直接获取以及使用注解,还有直接手动从Request里面获取参数
配置一个专门的Mapping:然后参数使用控件name
获取sno1控件的字符串值
@GetMapping("/addStudentDispose")
public String addStudentDispose(String sno1){
System.out.println(sno1);
return "addStudent";
}
直接指定具体类型
通过注解@RequestParam
,获取指定name控件的值,并且可以重命名
@GetMapping("/addStudentDispose")
public String addStudentDispose(@RequestParam("sno1") String s){
System.out.println(s);
return "addStudent";
}
使用Map全部获取参数
可以将所有参数都保存到Map里,然后再获取。
value可以直接使用String也可以变成Object然后再转换。
@GetMapping("/addStudentDispose")
public String addStudentDispose(@RequestParam Map<String,String> map){
System.out.println(...);
return "addStudent";
}
直接从Request里面取出参数
@GetMapping("/addStudentDispose")
public String addStudentDispose(HttpServletRequest httpServletRequest){
String sno1=httpServletRequest.getParameter("sno1");
System.out.println(sno1);
return "addStudent";
}
日志
Java里面有很多日志框架,比如常见的log4j,logback等
日志框架可以分为两种,日志抽象层和日志实现
- 抽象层就是提供统一标准和规范的API框架,意义在于提供接口,如JCL、SLF4j、jboss-logging
- 实现层主要是日志的具体实现,比如log4j、log4j2、Logback
Javaweb里面每个框架好像都有自己的一个日志框架,不太一。
然后SpringBoot自带的是SLF4j+Logback。
Logback和log4j是同一个作者,但是比后者有更多的优点和更强的性能。
虽然我不理解为什么要分开
使用的话,这样
日志当然同样有五种方法:trace、debug、info、warn、error
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger Log= LoggerFactory.getLogger(getClass());
Log.info("this is a log for test()");
这样日志就会输出在终端控制台上。
还可以对日志进行一些配置。
修改resources/application.properties
filename:resources/application.properties
#日志级别
logging.level.net.biancheng.www=trace
#使用相对路径的方式设置日志输出的位置(项目根目录目录\my-log\mylog\spring.log)
logging.file.path=logs/springboot
#绝对路径方式将日志文件输出到 【项目所在磁盘根目录\springboot\logging\my\spring.log】
#logging.file.path=/spring-boot/logging
#控制台日志输出格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
#日志文件输出格式
logging.pattern.file=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
MyBatis(Plus)
mybatis是一个开源的持久化层框架,对jdbc进行了一个封装,实现了ORM的功能,以面向对象的方式操作数据库。
大致的步骤好像是需要创建一个实体类,在里面写上相应的字段属性,然后需要映射到配置文件,这个配置了文件里面面需要自己手写sql语句,相当于就是多了一层转换,但是还是要写sql。
查看大致步骤:
- 编写一个student.java,存放学生实体类,右键生成mybatis配置文件
- 在studentmapper.xml里面,需要手写要用到的对应的sql语句,以及映射的类等信息
- 然后编写config.xml,这个是mybatis的配置文件,比如有数据库的连接信息等
- 最后在代码里实例化即可
然后我查了一下,好像plus不需要写sql了,那我当然用plus了。
MyBatis-Plus是MyBatis的升级版,为了简化开发提高开发效率,封装了一些常用方法,其中就包含可以避免编写大量xml的方法,减少了很多代码量。
使用配置
添加相关依赖,记得先刷新依赖(右键,maven刷新),这里还要添加一个mysql依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
修改一下配置文件application.properties
这里的驱动名也是有讲究
- 8.X版本:com.mysql.cj.jdbc.Driver
- 5.X版本:com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/数据库名字
spring.datasource.username=root
spring.datasource.password=root
创建一个包叫database,创建一个User.java
类
import lombok.Data;
@Data
@TableName("user")
public class User {
private String ID;
private String UserName;
private String Role;
private String Email;
}
然后创建一个接口UserMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}
最后在启动类里添加注解,这个路径就是上面的database包,扫描mapper
@SpringBootApplication
@MapperScan("com.rbk.freechat")
public class FreechatApplication {
public static void main(String[] args) {
SpringApplication.run(FreechatApplication.class, args);
}
}
测试使用
随便写个接口,我这里就在我的登录逻辑上测试一下
@Autowire
private UserMapper UserManagement;
public String login(HttpServletRequest request, Map<String, Object> map, HttpSession session, HttpServletResponse response, @RequestParam Map<String,String> params) throws Exception{
String Method=request.getMethod();
if (Method.equals("GET")){
//GET请求
Log.info("[ GET请求 ]\t需要登录");
List<User> UsersData=UserManagement.selectList(null);
return "/user/login";
}
}
有个报错The server time zone value ‘Öйú±ê׼ʱ¼ä’,是说时区出问题了。
这个其实就是时区问题,数据库默认的是国外的时区,而系统时区则是国内,不一致。
在jdbc连接的url后面加上serverTimezone=GMT即可解决问题,如果需要使用gmt+8时区,需要写成GMT%2B8,否则会被解析为空。再一个解决办法就是使用低版本的MySQL jdbc驱动,5.1.28不会存在时区的问题。
我这里是降级版本了
基本方法
插入数据
int insert(T entity);
删除数据
int deleteById(Serializable id); // 根据主键 ID 删除
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 map 定义字段的条件删除
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 根据实体类定义的 条件删除对象
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 进行批量删除
修改数据
int updateById(@Param(Constants.ENTITY) T entity); // 根据 ID 修改实体对象。
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); // 根据 updateWrapper 条件修改实体对象
查询所有数据
T selectById(Serializable id); // 根据 主键 ID 查询数据
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 进行批量查询
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据表字段条件查询
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据实体类封装对象 查询一条记录
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询记录的总条数
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 entity 集合)
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 map 集合)
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(但只保存第一个字段的值)
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 entity 集合),分页
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询所有记录(返回 map 集合),分页