前言
本文旨在使用SpringBoot + MyBatisPlus + thymeleaf 模版引擎实现分页效果。目标功能实现如下:
在此案例中,表单数据从数据库拿到,可动态显示当前页,总页数以及记录数。并且实现分页按钮组,包括动态生成页码,前一页和后一页。
首先,需要引入MyBatisPlus以及thymleaf模版引擎相关starter
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
相关环境引入之后,本文以User表为案例进行测试
测试实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
//指定该entity映射到数据库中user_table这张表
@TableName("user_table")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
数据库表及字段(案例参照mybatisPlus官方文档)
CREATE TABLE user_table
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
增加SpringBoot配置类引入分页插件组件
@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor paginationInterceptor(){
//新建MybatisPlus拦截器
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//新建分页拦截器paginationInnerInterceptor
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
//设置分页拦截器的一些属性
paginationInnerInterceptor.setOverflow(true);
paginationInnerInterceptor.setMaxLimit(100L);
//把分页拦截器添加到MybatisPlus拦截器中
mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);
//添加组件,大功告成!
return mybatisPlusInterceptor;
}
}
- 这一步的主要目标是往Spring容器中mybatisPlus的分页插件拦截器
UserMapper接口
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}
- 实体类Mapper需要继承MybatisPlus的
BaseMapper<T>
类,其中的范性为该目标实体User
。该写法为MyBatisPlus的标准写法,继承BaseMapper<T>
后,可以给予<T>
CRUD功能
Service层
-
UserService
接口:public interface UserService extends IService<User> { }
- 直接实现MyBatisPlus中的顶级接口
IService<T>
,其中的范型为目标实体
- 直接实现MyBatisPlus中的顶级接口
-
UserServiceImpl
:UserService
的真正实现类@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
-
首先,肯定要实现其对应的接口:
implements Userservice
-
其次需要继承
ServiceImpl
,该类为mybatisPlus中的IService<T>
实现类,需要指定两个范性<UserMapper, User>
。显而易见,第一个范性为User对应的Mybatis需要指定的的Mapper类,第二个范性为目标实体类User
-
Controller层
@Controller
public class TableController {
//自动注入userService
@Autowired
UserService userService;
@GetMapping("/table")
public String tableController(@RequestParam(value = "pn",defaultValue = "1") Integer pn, Model model){
//新建分页构造函数Page<T> T为目标实体类
Page<User> userPage = new Page<User>(pn, 2);
//result为分页查询结果
Page<User> result = userService.page(userPage);
model.addAttribute("result",result);
return "table";
}
}
Controller层相对复杂,主要是以下几个方面:
-
控制器参数部分
@RequestParam(value = "pn",defaultValue = "1") Integer pn
:前端传入一个需要跳转到的页面值pn
,默认为第一页 -
Page<User> userPage = new Page<User>(pn, 2)
:new一个分页构造函数类Page
的实体userPage
这个Page对象是所有分页功能最重要的一个类,强烈建议看看源码!!分页查询的结果与分页信息会全部包装在这个类中返回!!!
其构造函数源代码如下:
/** * 分页构造函数 * * @param current 当前页 * @param size 每页显示条数 */ public Page(long current, long size) { this(current, size, 0); }
即:传入你想指定的当前页值,和每页显示条数(好像是废话。。。)
-
Page<User> result = userService.page(userPage);
:调用userService
里面的page()
方法,传入我们实例化好的userPage
。问题在这里:
userService
没有定义任何方法,为什么会出现page()
这个方法呢?这其实是
userServiceImpl
所继承的ServiceImpl<UserMapper, User>
提供的方法而这里的Page类型的result返回值,里面会包括所有我们查询到的结果以及分页信息!!
-
model.addAttribute("result",result);
没什么好说的,把这个result直接丢进Model,返回到前端处理
前端页面
前端使用了thymeleaf模版引擎,具体引入请参照官方文档
https://www.thymeleaf.org/
样式截取关键代码
首先是表单区域:
<tbody>
<tr class="gradeX" th:each="user,stat:${result.records}">
<td th:text="${stat.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
<td th:text="${user.email}"></td>
</tr>
</tbody>
th:each="user,stat:${result.records}"
:这里的result为之前controller放进model的Page<User> result
对象,我们可以通过result.records
获得我们查询返回的结果,并且命名每一个结果为user
进行循环输出,其中stat
为每个user
的状态(提供自thymeleaf模版),可以得到许多额外信息
显示分页信息区域
<div class="row-fluid">
<div class="span6">
<div class="dataTables_info" id="dynamic-table_info">
当前第 [[${result.current}]]
总计 [[${result.pages}]] 页
共[[${result.total}]] 条记录</div>
</div>
</div>
- 该区域动态显示其数据页面信息,如前文所解释,
result
为Page<User>
对象,里面包含了所有分页相关的信息,我们可以调用其属性动态显示当前页码,页码总数,记录总数等信息!! [[${}]]
该写法为thyemleaf取值的行内写法,具体请见相关文档
页码按钮栏区域
<ul>
<li th:class="${result.current == 1}? 'prev disabled':'prve'" class="prev disabled">
<a th:href="@{/table(pn=${result.current}-1)}">← Previous</a></li>
<li th:class="${num == result.current} ?'active':''"
th:each="num:${#numbers.sequence(1,result.pages)}">
<a th:href="@{/table(pn=${num})}">[[${num}]]</a>
</li>
<li class="next" th:class="${result.current == result.pages?'disabled next':'next'}">
<a th:href="@{/table(pn=${result.current}+1)}">Next → </a></li>
</ul>
该区域看似比较复杂,实则结构十分简单,简化如下:
<ul>
<li><a href="#">← Previous</a></li>
<li><a href="#">动态生成的页面数</a></li>
<li><a href="#">Next →</a></li>
</ul>
其中,Previous按钮与Next按钮的实现大同小异,以Previous按钮举例:
<li th:class="${result.current == 1}? 'prev disabled':'prve'" class="prev disabled">
<a th:href="@{/table(pn=${result.current}-1)}">← Previous</a></li>
-
首先是
<li>
标签内:th:class
是thymeleaf模版引擎语法,可以修改class属性。
${result.current == 1}? 'prev disabled':'prve'
是一个三元运算符,由显示分页信息区域 模块的解释可知,result
是一个Page<User>
对象,可以得到分页信息,如果当当前页面为1时,给<li>
标签添加disabled
样式禁用其选择。否则允许选择 -
<a>
标签内:th:href
语法同理可以修改a href
里面的属性,@{/table(pn=${result.current}-1)}
表示该<a>
标签链接指向/table
请求而(pn=${result.current}-1)
为thymeleaf语句,即传入一个参数pn
,其值为result.current -1
,即当前页数-1,传回给Controller。(见Controller章节写法)
动态生成页数区域:
<li th:class="${num == result.current} ?'active':''"
th:each="num:${#numbers.sequence(1,result.pages)}">
<a th:href="@{/table(pn=${num})}">[[${num}]]</a>
</li>
<li>
标签中${num == result.current} ?'active':''"
三元运算符给予当前页被选中的样式,如上同理,不多解释。th:each="num:${#numbers.sequence(1,result.pages)}"
表示遍历生成${#numbers.sequence(1,result.pages)}
这些数字,每个数字叫做num
。而#numbers.sequence(from,to)
为themeleaf内置函数,可以生成从from
到to
的序列。在此例中表示生成1到result.pages的序列<a>
标签中的原理与上类似,不同点在于赋值给pn
对应的页面值
至此大功告成!