文章目录
一.概念
- 什么是springboot
springboot是spring快速开发脚手架,通过约定大于配置的方式,快速构建和启动spring项目- 为什么要学习springboot
- spring的缺点:
- 复杂的配置(需要写大量的xml文件)
- 混乱的依赖管理
而springboot可以解决以上两个问题
- springboot的特点
- 快速开发spring应用的框架
- 内嵌tomcat和jetty容器,不需要单独安装容器,jar包直接发布一个web应用
- 简化maven配置,parent这种方式,一站式引入需要的各种依赖
- 基于注解的零配置思想
- 和各种流行框架,spring web mvc,mybatis,spring cloud无缝整合
二.入门案例
- 创建好工程项目后添加依赖
- 添加父工程坐标
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
- 添加web启动器
为了让SpringBoot帮我们完成各种自动配置,我们必须引入SpringBoot提供的自动配置依赖,我们称为 启动器 。
因为我们是web项目,这里我们引入web启动器:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
需要注意的是,我们并没有在这里指定版本信息。因为SpringBoot的父工程已经对版本进行了管理了。
- 管理jdk版本
<properties>
<java.version>1.8</java.version>
</properties>
- 启动类
springboot项目通过main函数即可启动,我们需要创建一个启动类,然后编写main函数。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 编写controller
接下来,就可以像以前一样开发springmvc项目了!
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello, spring boot!";
}
}
补充:
- 启动测试
通过运行main函数,访问localhost:8080/hello即可
三.全注解配置和属性注入
在入门案例中,我们没有任何的配置,就可以实现一个SpringMVC的项目了。也许有的人会有疑问,没有xml,如何配置bean呢?
- 以连接池为例:以前会这样配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
而现在3.0以后的Spring的注解已经非常完善了,所以现在将采用注解代替xml的方式对bean进行配置
3.1 Spring全注解配置
spring全注解配置主要靠java类和一些注解,比较常用的注解有:
- @Configuration :声明一个类作为配置类,代替xml文件
- @Bean :声明在方法上,将方法的返回值加入Bean容器,代替< bean>标签
- @value :属性注入(给bean属性赋值)
- @PropertySource :指定外部属性文件(有了外部文件,@Value才能注入值)
- 接下来用java配置来尝试实现连接池配置
- 引入Druid连接池依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
- 创建一个jdbc.properties文件
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/lxs
jdbc.username=root
jdbc.password=123
2. 在任意位置通过@Autowired注入DataSource
@RestController
public class HelloController {
@Autowired
private DataSource dataSource;
@GetMapping("hello")
public String hello() {
return "hello, spring boot!" + dataSource;
}
}
- 通过Debug运行查看
发现注入成功,即bean配置成功!
3.2 SpringBoot属性注入
在上面的案例中,我们实验了java配置方式。不过属性注入使用的是@Value注解。这种方式虽然可行,但是不够
强大,因为它只能注入基本类型值。
在SpringBoot中,提供了一种新的属性注入方式,支持各种java基本数据类型及复杂类型的注入。
- 新建一个类JdbcProperties并配置注解@ConfigurationProperties
- 将jdbc.properties改名为application.properties,这是Springboot默认读取的属性文件名。
- 在配置有@Configuration的类中开启注解@EnableConfigurationProperties
- @ConfigurationProperties注解声明当前类为属性读取类,刚创建时如果没有类对其进行引用将报错。
- prefix=“jdbc” 读取属性文件中,前缀为jdbc的值。
- 这里我们并没有指定属性文件的地址,所以我们需要把jdbc.properties名称改为
application.properties,这是SpringBoot默认读取的属性文件名:
- 配置带@ConfigurationProperteis的类的引入总共有3种方式,个人推荐更为简单直观的第三种。
//方式一:作为成员变量
@Autowired
private JdbcProperties prop;
//方式二:构造函数
private JdbcProperties prop;
public JdbcConfig(Jdbcproperties prop){
this.prop = prop;
}
//声明有@Bean的方法参数注入
@Bean
public Datasource dataSource(JdbcProperties prop){
// ...
- 或许有人觉得这种方式更加麻烦了,然而比起@Value这种方式有着更强大的功能,下面来看下两者的比较。
- Relaxed binding:松散绑定
- 不严格要求属性文件中的属性名与成员变量名一致。支持驼峰,中划线,下划线等等转换。比如user.frinder.name,这里friend也是对象,而@Value注解就无法完成这种注入。
- meta-data support:元数据支持
- 帮助IDE生成属性提示(写开源框架会用到)。
总结:
- 如果只是为了获取配置文件中某个值,则使用 @Value;
- 如果是一组配置和javaBean进行映射,则使用 @ConfigurationProperties
3.3 更优雅的注入
事实上,如果一段属性只有一个Bean使用,则无需注入到类中,而是直接在需要的地方声明即可。
四.自动配置原理
通过刚才的案例看到,一个整合了SpringMVC的WEB工程开发,变的无比简单,那些繁杂的配置都消失不见了,这是如何做到的?这些都得从springboot启动器开始讲起。
- @SpringBootApplication
通过查看源码可以发现,这里重点的注解有3个
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
- @SpringBootConfiguration
通过这段我们可以看出,在这个注解上面,又有一个 @Configuration 注解。这个注解的作用就是声明当前类是一个配置类,然后Spring会自动扫描到添加了 @Configuration 的类,并且读取其中的配置信息。
- @ComponentScan
并没有看到什么特殊的地方,查看注释:
大意是:
配置组件扫描的指令。提供了类似与 context:component-scan 标签的作用
通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声明这个注解的类所在的包开始,扫描包及子包.
- 而我们的@SpringBootApplication注解声明的类就是main函数所在的启动类,因此扫描的包是该类所在包及其子包。
- 因此,一般启动类会放在一个比较前的包目录中。
- @EnableAutoConfiguration
关于这个注解,官网上有一段说明:
第二级的注解 @EnableAutoConfiguration ,告诉SpringBoot基于你所添加的依赖,去“猜测”你想要如何配置
Spring。比如我们引入了 spring-boot-starter-web ,而这个启动器中帮我们添加了 tomcat 、 SpringMVC 的
依赖。此时自动配置就知道你是要开发一个web应用,所以就帮你完成了web及SpringMVC的默认配置了!
4.1 默认配置原理
@EnableAutoConfiguration会开启SpringBoot的自动配置,并且根据你引入的依赖来生效对应的默认配置,
springboot如何做到的?
其实在我们的项目中,已经引入了一个依赖:spring-boot-autoconfigure,其中定义了大量自动配置类,几乎涵盖了现在主流的开源框架,以下只列举一部分。
我们来看一个熟悉的,例如SpringMVC,查看mvc的自动配置类。
查看源码发现,这里有两个注解值得我们重点关注:
- @ConditionalOnClass
- 这里的条件是OnClass,也就是满足以下类存在:Servlet、DispatcherServlet、WebMvcConfigurer,其中Servlet只要引入了tomcat依赖自然会有,后两个需要引入SpringMVC才会有。这里就是判断你是否引入了相关依赖,引入依赖后该条件成立,当前类的配置才会生效。
- @ConditionalOnMissingBean
- 这个条件与上面不同,OnMissingBean,即环境中没有指定的Bean这个才生效。其实这就是自定义配置的入口,也就是说,如果我们自己配置了一个WebMVCConfigurationSupport的类,那么这个默认配置就会失效。
我们接着查看该类中定义了什么内容
- 视图解析器
- 处理器适配器(HandlerAdapter)
总结:
SpringBoot为我们提供了默认配置,而默认配置生效的条件一般有两个:
- 引入了相关依赖
- 无自定义配置类
五.整合SpringMVC
刚才的案例已经实现mvc自动配置,接下来我们要解决三个问题:
- 修改端口
- 静态资源
- 拦截器配置
5.1 修改端口
在全局配置文件(application.properties)中进行修改:
server.port=80
5.2 静态资源
ResourceProperteis的类,定义了静态资源的默认查找路径
一般习惯放在classpath:/static/目录下
5.3 拦截器
- 定义拦截器
public class LoginInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("处理器执行前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("处理器执行后");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("跳转后执行");
}
}
- 配置拦截器
通过实现 WebMvcConfigurer 并添加 @Configuration 注解来实现自定义部分SpringMvc配置:
@Configuration
public class MvcConfig implements WebMvcConfigurer {
/**
* 将定义的拦截器注册到Spring容器
* @return
*/
@Bean
public LoginInterceptor loginInterceptor() {
return new LoginInterceptor();
}
/**
* 添加自定义拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.loginInterceptor()).addPathPatterns("/**");
}
}
路径通配符
- ‘?’ 匹配任何单字符
- ‘*’ 匹配0或者任意数量的字符
- ‘/**’ 匹配0或者更多的目录
结构如下:
但是当我们查看日志的时候会发现什么都没有,因为我们记录的log级别是debug,默认是显示info以上,所以要对日志级别进行配置,依旧是在全局配置文件中进行配置:
logging.level.com.leyou=debug
六.通用mapper
6.1 概念
使用Mybatis时,最大的问题是,要写大量的重复SQL语句在xml文件中,除了特殊的业务逻辑SQL语句之外,还有大量结构类似的增删改查SQL。而且,当数据库表结构改动时,对应的所有SQL以及实体类都需要更改。因此避免重复书写CRUD映射的框架有两个
- 通用mybatis(tk mybatis)
- mybatis plus,通能更加强大。
6.2 配置
- 引入依赖
<!-- 通用mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
- 实体类
tk mybatis 实体类使用的注解是jpa注解
@Table(name = "tb_user")
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 用户名
private String userName;
- 默认表名=类名,也可通过
@Table(name="tableName")
注解指定- 默认列名=属性名,也可通过
@Column(name="fieldName")
指定- 主键使用注解
@Id
,若是自增id还应加上注解@GenerateValue(strategy=GenerationType.IDENTITY)
- 表中无该字段,而实体类需要使用,则添加@Transient注解,但该类要实现序列化接口
- 定义一个接口继承Mapper类即可使用
import com.lsy.domain.User;
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper<User> {
}
若有特殊的业务逻辑需求,可自定义方法再对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.lxs.demo.dao.UserMapper">
<select id="findByUser" resultType="user">
SELECT
*
FROM
tb_user
<where>
<if test="name != null">
name like '%${name}%'
</if>
<if test="note != null">
and note like '%${note}%'
</if>
</where>
</select>
七.Thymeleaf
7.1 概念
Thymeleaf 是一个跟 FreeMarker 类似的模板引擎,它可以完全替代 JSP 。
特点:
- 动静结合:运行与网络状态无关,无网络显示静态内容,有网络用后台得到数据替换静态内容
- 与SpringBoot完美整合,springboot默认整合thymeleaf
动静结合该怎么理解呢,也许有人会说可以先写好静态页面,等有网络再用ajax获得数据也可以。
答案是当然可以,但那样做效率会低很多,例如一个form表单中有一个table,以往的做法就是先填充些虚拟的数据,等有网络了再通过ajax获得新的数据进行填充,但是这样的话我们写的静态页面就仅是为了预览,对后期的页面布局以及数据获取没什么帮助,而Thymeleaf就能很好的帮我们解决这个问题,接下来就通过一个案例进行讲解吧。
7.2 案例
- service
@Service
public class UserService {
@Autowired
private UserDao userDao;
public List<User> queryAll() {
return this.userDao.selectAll();
}
}
- controller
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/all")
public String all(Model model) {
List<User> list = userService.findAll();
model.addAttribute("users", list);
// 返回模板名称(就是classpath:/templates/目录下的html文件名)
return "users";
}
}
- 引入启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
SpringBoot会自动为Thymeleaf注册一个视图解析器.
与解析JSP的InternalViewResolver类似,Thymeleaf也会根据前缀和后缀来确定模板文件的位置
- 默认前缀: classpath:/templates/
- 默认后缀: .html
所以如果我们返回视图: users ,会指向到 classpath:/templates/users.html
一般我们无需进行修改,默认即可
- 静态页面
把html 的名称空间,改成:
xmlns:th="http://www.thymeleaf.org"
会有语法提示
<tr th:each="user, status : ${users}" th:object="${user}">
<td th:text="${user.id}">1</td>
<td th:text="*{name}">张三</td>
<td th:text="*{userName}">zhangsan</td>
<td th:text="${user.age}">20</td>
<td th:text="${user.sex} == 1 ? '男': '女'">男</td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">1980-02-30</td>
<td th:text="${user.note}">1</td>
<td>
<a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
</td>
</tr>
基础语法解析:
th:each="user, status : ${users}" th:object="${user}">
- th-: th- 是利用了h5中的自定义属性来实现的。如果不支持H5,可以用 data-th- 来代替
- th:each :获取集合里的每个元素,类似c:foreach
- user:集合里元素的指代,名字任意。
- status:返回的集合模板名称
- ${users}:获取返回的集合值,比el表达式更强大
th:object="${user}"
- th:object :指定一个对象,如果返回的结果不是集合,则单独使用th:object即可
- ${user}:获取user的值。因为前面已经指定集合里每个元素名称是user
th:text
:声明标签中的文本- 关于
*{name}
以及超链接将会在后续继续介绍。
7.3 表达式
- 分类
表达式总共有三类:
- 变量表达式
- 选择或星号表达式
- URL表达式
- 变量表达式
变量表达式即OGNL表达式或Spring EL表达式(在Spring中用来获取model attribute的数据)。它们将以HTML标签的一个属性来表示,如上述的
${users}
,th:object="${user}"
- 选择(星号)表达式
选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行,如上述的
*{name}
,星号代替的就是th:object
中的对象
- URL表达式
该表达式一般用于超链接标签,总共有三类
- url表达式:
<a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
,变量表达式可用星号表达式替换。- 文本替换 (推荐)
<a th:href="|/update/${user.id}|">修改</a>
,${user.id}可用星号表达式进行替换。- 字符串拼接
<a th:href="'/approve/' + ${user.id}">审核</a>
,变量表达式可用星号表达式替换。
7.4 内联文本
内联文本:通过[[…]]方式,使用时,必须先用th:inline=”text/javascript/none”激活,用法主要是用于引用以及定义变量。
- 例如
<h6 th:text="${text}">静态内容<h6>
,然后在别处引用$ {text}的值<h6 th:inline="text">[[${text}]]</h6>
7.5 内嵌变量
为了模板更加易用,Thymeleaf还提供了一系列Utility对象(内置于Context中),可以通过 # 直接访问,下面只简单介绍两个。
- dates
<h6 th:text="${#dates.createNow()}">获取当前日期</h6>
- strings
<h6 th:text="${#strings.substring(text, 6, 9)}">截取字符串</h6>
7.6 缓存
Thymeleaf会在第一次对模板解析之后进行缓存,但是这给我们开发带来了不便,修改页面后并不会立刻看到效果,因此开发阶段可以关掉缓存使用:
# 开发阶段关闭thymeleaf的模板缓存
spring.thymeleaf.cache=false
八.Mybatis plus
8.1基础配置
- 导入依赖
<?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 http://maven.apache.org/xsd/maven-
4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lxs</groupId>
<artifactId>mybatis-plus-demo-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatisplus.version>3.3.2</mybatisplus.version>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置文件application.yml
在Springboot中,推荐使用properties或者YAML文件来完成配置,但是对于较复杂的数据结构来说,YAML又远远优于properties。先来看一个Springboot中的properties文件和对应YAML文件的对比:
- properties
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com
- yaml
environments:
dev:
url: http://dev.bar.com
name: Developer Setup
prod:
url: http://foo.bar.com
name: My Cool App
my:
servers:
- dev.bar.com
- foo.bar.com
可以明显的看到,在处理层级关系的时候,properties需要使用大量的路径来描述层级(或者属性),比如
environments.dev.url和environments.dev.name。其次,对于较为复杂的结构,比如数组(my.servers),写起来更为复杂。而对应的YAML格式文件就简单很多.
下面是application.yaml
# DataSource Config
spring:
datasource:
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
url: jdbc:h2:mem:test
username: root
password: test
# Logger Config
logging:
level:
com.lxs.quickstart: debug
h2数据库是一个基于内存的数据库,在jvm启动时,自动执行脚本加载相应的数据
springboot 中使用h2数据库直接按照上面配置,配置schema表结构脚本和data数据脚本即可
注意这里用户名密码可以省略不写,或者随意设定
8.2 入门案例
- 此处只写实体层和dao层,其它层和tk mapper流程类似
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
@Data注解可自动为该类提供get,set,equal,toString等方法,且成员变量改变时get,set方法可自动改变,不再像以前那样需要手动更改,但是该注解生效的前提是引入lombok依赖。
public interface UserMapper extends BaseMapper<User> {
}
8.3 常用注解
- @TableName:类名表名不一致使用(@TableName(value=""))
- @TableId:主键名不一致使用
- 如果主键策略默认什么也没配,MP会自动找ID作为主键 而没配@TableId但是主键不是ID而是user_id 此时MP会报错
- 解决方式:要么改成ID后MP会自动找,要么还使用user_id同时加上@TableId
- @TableFiled:成员名字段名不一致使用
- 主键策略
- 表中有主键
@TableId(type=IdType.AUTO)
private Long id;
- 表中无主键
/**
* 采用雪花算法生成全局唯一主键
**/
ASSIGN_ID(3),
- 字段策略
- 排除实体类中非表字段
使用@TableField(exist = false)
注解
8.4 内置CRUD
//删除
//批量删除:1
mapper.delete(new QueryWrapper<User>().like("name", "J"));
//批量删除:2
mapper.delete(Wrappers.<User>query().like("name", "J"));
//批量删除:3
mapper.delete(Wrappers.<User>query().lambda().like(User::getName, "J"));
//修改
//批量修改:1
mapper.update(null, Wrappers.<User>update().set("email","huike@163.com")
.like("name","J"));
//批量修改:2
mapper.update(new User().setEmail("huike@163.com"), Wrappers.<User>update()
.like("name","J"));
8.5 分页
8.5.1 内置分页
- 配置分页插件
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
// 开启 count 的 join 优化,只针对 left join !!!
return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
}
}
- 测试
@Test
public void testPage() {
System.out.println("------ baseMapper 自带分页 ------");
Page<User> page = new Page<>(1, 5);
IPage<User> pageResult=mapper.selectPage(page, new QueryWrapper<User>().eq("age", 20));
System.out.println("总条数 ------> " + pageResult.getTotal());
System.out.println("当前页数 ------> " + pageResult.getCurrent());
System.out.println("当前每页显示数 ------> " + pageResult.getSize());
pageResult.getRecords().forEach(System.out :: println);
}
通过上例可知,用内置分页插件时,需要传递一个page对象。上例的条件查询只有一个,若是有多个,则查询时较为麻烦,此时推荐使用自定义sql语句,然后selectPage的第二个参数传递封装好的对象即可。步骤如下:
8.5.2 自定义分页
- application.yml文件配置
# 配置mybatis plus
mybatis-plus:
type-aliases-package: com.lxs.crud.entity #别名搜索
mapper-locations: classpath:/mappers/*.xml #加载映射文件
- 编写接口
public interface UserMapper extends BaseMapper<User> {
/**
* 如果映射的接口方法有2个参数需要@Param定义参数名
* 定义参数名后,映射文件中使用p.属性 c.属性,具体访问
*
* @param page
* @param conditioin
* @return
*/
public IPage<User> selectUserByPage(@Param("p") IPage<User> page,
@Param("c") User conditioin);
}
- 映射文件
<mapper namespace="com.lxs.mybatisplus.samples.crud.mapper.UserMapper">
<sql id="selectSql">
SELECT
*
FROM
user
</sql>
<select id="selectUserByPage" resultType="user">
<include refid="selectSql"></include>
<where>
<if test="c.age !=null">
age = #{c.age}
</if>
<if test="c.email !=null">
and email like '%${c.email}%'
</if>
</where>
</select>
</mapper>
- 测试
Page<User> page = new Page<>(1, 5);
User user = new User();
user.setAge(20);
user.setEmail("test");
IPage<User> pr = mapper.selectUserByPage(page, user);
8.5.3 pagehelper分页
- 引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
- 配置类
@Configuration
@MapperScan("com.lxs.mybatisplus.samples.crud.mapper")
public class MybatisPlusConfig {
/**
* mp分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
// 开启 count 的 join 优化,只针对 left join !!!
return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
}
/**
* 两个分页插件都配置,不会冲突
* pagehelper的分页插件
*/
@Bean
public PageInterceptor pageInterceptor() {
return new PageInterceptor();
}
- 测试
@Test
public void testPageHelper() {
//链式查询,不带条件
PageInfo<User> page = PageHelper.startPage(1, 2).doSelectPageInfo(() ->
mapper.selectList(Wrappers.<User>query()));
//分步查询,不带条件,这种方式更容易理解
PageHelper.startPage(1,2);
PageInfo<User> page = new PageInfo<>(mapper.selectList(Wrappers.<User>query()));
//分步查询,带有条件,这种方式更容易理解
User u = new User();
u.setAge(20);
PageInfo<User> page = new PageInfo<User>(mapper.selectUserByPage2(u));
}
8.5.4 总结
- 内置分页
- 查询前使用
Page<T> page = new Page<>(pageNum, pageSize)
实现分页- 通过
IPage<T> pr = mapper.查询方法
获取结果
- PageHelper
- 查询前使用
PageHelper.startPage(pageNum,pageSize)
确定分页- 通过
mapper.方法
获取结果- 通过
PageInfo<T>
对查询结果进行封装才能实现分页
- 带条件查询
实际开发中,一般都是模糊查询才需要传递条件,虽然mapper封装了容器查询的方法,但有时候要求比较特别,example查询比较单一难以满足要求,所以上例中采用了手写接口进行自定义分页查询的实现。