bootstrap table + java 分页案例 --- 两种分页方法详细解析
本文主要介绍了使用 bootstrap table 与 java 实现的数据拉取与分页展示的 2 个案例。
第一个案例通过分页参数 limit 与 offset 直接从数据查询,第二个案例则是使用com.github.pagehelper.Page
的分页插件进行分页处理。相比之下,第一个案例的使用比较简单,但是代码冗余较大。而第二个案例则比较轻量,但是用起来比较麻烦。
案例1:使用分页参数直接从数据库查询
需求:查询年龄大于20且居住地为上海的用户并展示在表格上。
这里使用 bootstrap
插件,需要导入 bootstrap
的包。
下面先介绍代码
(1)前端html
代码
//查询参数
<div id="toolbar" class="form-inline">
<input type="number" class="form-control" id="search_age">
<input type="text" class="form-control" id="search_address">
<button type="button" class="btn btn-primary" id="btnQuery">
<i class="glyphicon glyphicon-search"></i> 查询
</button>
</div>
//表格
<table id="table" class="table table-bordered" data-show-refresh="true"
data-show-columns="true">
</table>
(2)前端 js
代码
//点击查询触发表格加载
$("#btnQuery").click(function () {
$('#table').bootstrapTable({
method:'get',
url:"/user/getUserByAge",//请求路径
pageNumber: 1,//初始化加载第一页
striped: true, //是否显示行间隔色
showRefresh: true,//显示刷新按钮
pagination:true, //是否分页
pageSize:5, //初始化单页记录数
pagination: true, //是否分页
pageList: [5, 10, 20, 30, 50],
search: true, //显示查询框
sidePagination: "server", //使用服务端分页
//下面是封装的请求参数
queryParams:function(params){
var temp = {
limit:params.limit, // 每页显示数量
offset:params.offset, // SQL语句起始索引
//查询参数
age : $('search_age').val().trim(),
address: $('search_address').val().trim()
};
return JSON.stringify(temp);
},
//将返回的数据封装到表格
columns:[
{
checkbox: true , width: '3%', visible: true //开启复选框
},
{
field: 'id', title: 'id'
},
{
field: 'name', title: '姓名'
},
{
field: 'age', title: '年龄'
},
{
field: 'address', title: '地址'
}
]
});
});
(3)后端封装数据的User
对象、封装分页请求参数的PageReq
类、封装返回给前端数据的PageRes
类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User extents PageReq{
private Integer id;
private String name;
private Integer age;
private String address;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageReq{
//每页显示数量
private int limit;
//sql语句开始的索引
private int offset;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageRes<Bean>{
//封装实体类数据的list
private List<Bean> rows;
//数据总条数
private Integer total;
}
(4)controller
层以及mapper
层代码
为了方便展示,这里不多分一层 service
层。
@RequestMapping("/user")
public class UserController{
@RequestMapping("/getUserByAge")
@ResponseBody
public PageRes<User> getUserByAge(User user , HttpServletRequest request){
//每页显示数据条数
int limit = user.getLimit();
//数据库查询索引起始值
int offset = user.getOffset();
//age与address
int age = user.getAge();
String address = user.getAddress();
//查询出所有满足条件的数据(当前页面)
List<User> list= userMapper.pageByAgeAndAddress(limit,offset,age,address);
//封装返回给前端的数据
PageRes<User> result = new PageRes<User>();
result.setRows(list);
result.setTotal(list.size());
return result;
}
}
需要在 UserMapper 中添加1个方法用于分页查询,这里需要注意的是,地址字段需要进行模糊查询。
//查询出分页查询的数据
@Select("select * from User where 1=1 and age > #{age,jdbcType=INTEGER} and address like concat('%',#{address,jdbcType=VARCHAR},'%') limit #{offset,jdbcType=INTEGER} , #{limit,jdbcType=INTEGER}")
List<User> pageByAge(int limit ,int offset ,int age , String address);
案例2:使用 com.github.pagehelper.Page
的分页插件进行分页处理
下面介绍这个案例涉及的一些类。
(1)代码
(1)前端 html
与 js
代码
html
与上面一致,不做赘述。js
中的代码,只有 queryParams
需要进行修改,新的代码如下:
queryParams: function (params) {
var query={
pageNum:params.pageNumber,//页数
pageSize:params.pageSize, //每一页中记录的数量
bean:{
age : $('search_age').val().trim(),
address: $('search_address').val().trim()
}
};
return JSON.stringify(query);
}
(2)mybatis generator
生成的数据库相关类
在数据库中创建一个 user
表,sql
代码如下:
CREATE TABLE `USER` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`age` bigint(8) DEFAULT NULL COMMENT '年龄',
`address` varchar(20) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
用mybatis generator
就会生成 User.java、UserExample.java、UserMapper.java、UserMapper.xml
4个类,生成之后,UserMapper.java
需要继承Mapper
的基础类 BaseMapper
。
public interface BaseMapper<BEAN,BEAN_EXAMPLE,KEYTYPE> {
int countByExample(BEAN_EXAMPLE example);
int deleteByExample(BEAN_EXAMPLE example);
int deleteByPrimaryKey(KEYTYPE id);
int insert(BEAN record);
int insertSelective(BEAN record);
List<BEAN> selectByExample(BEAN_EXAMPLE example);
BEAN selectByPrimaryKey(KEYTYPE id);
int updateByExampleSelective(@Param("record") BEAN record, @Param("example") BEAN_EXAMPLE example);
int updateByExample(@Param("record") BEAN record, @Param("example") BEAN_EXAMPLE example);
int updateByPrimaryKeySelective(BEAN record);
int updateByPrimaryKey(BEAN record);
}
这些类代码太多,此处就不赘述,下面使用到某些类的某些方法的时候再列出来。
(3)涉及的工具类
接口 IService
,这个接口的方法比较多,为了方便展示,只列出 add
和 page
2个方法,事实上里面还可以自定义很多数据库操作方法,如 get
、deleteByPrimaryKey
等
public interface IService<BEAN, BEAN_EXAMPLE,KEYTYPE> {
/**
* 添加记录接口.
*/
BEAN add(BEAN bean);
/**
* 分页接口
*/
Page<BEAN> page(BEAN_EXAMPLE bean, int page, int pageSize);
}
其次是 操作数据库的抽象服务总类AbstractService
,这个类的方法比较多,为了方便展示,只列出 add
和 page
2个方法以及抽象方法getRepository,同样可以自定义很多数据库操作方法。
public abstract class AbstractService<BEAN, BEAN_EXAMPLE,KEYTYPE> implements IService<BEAN, BEAN_EXAMPLE,KEYTYPE>{
protected abstract BaseMapper<BEAN,BEAN_EXAMPLE,KEYTYPE> getRepository();
@Override
public BEAN add(BEAN bean) {
int insert = getRepository().insert(bean);
return bean;
}
@Override
public Page<BEAN> page(BEAN_EXAMPLE bean, int page, int pageSize) {
com.github.pagehelper.Page<BEAN> pageResult = PageHelper.startPage(page, pageSize).doSelectPage(new ISelect() {
@Override
public void doSelect() {
getRepository().selectByExample(bean);
}
});
return new Page<>(pageResult.getResult(),pageResult.getTotal());
}
}
下面是封装请求参数的PageReq
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageReq<T> {
T bean;
private Integer pageSize;
private Integer pageNum;
}
封装返回数据的类Page
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Page<Bean> {
private List<Bean> rows;
private long total;
}
(4)controller
层与 service
层代码
首先是 UserController
@RequestMapping("/user")
@Controller
public class UserController{
@RequestMapping("/getUserByAge")
@ResponseBody
public Page<User> getUserByAge(@RequestBody(required = true) PageReq<UserExample> pageReq){
return new BaseView(UserService.page(pageReq.getBean(), pageReq.getPageNum(), pageReq.getPageSize()));
}
}
其次是 UserService
,这个类需要继承AbstractService<User, UserExample, Integer>
,同时实现AbstractService
的getRepository
方法。
由于AbstractService
的getRepository
方法返回BaseMapper
对象,而UserMapper
继承了BaseMapper
,因此getRepository
方法返回UserMapper
。
@Service
public class UserService extends AbstractService<User, UserExample, Integer>{
//这个方法返回 UserMapper(因为 UserMapper 继承了 BaseMapper)
@Override
protected BaseMapper<User, UserExample, Integer> getRepository() {
return userMapper;
}
/**
* 分页查询用户数据
*/
@Override
public Page<User> page(UserExample bean, int page, int pageSize){
UserExample.Criteria criteria = bean.createCriteria();
String address = bean.getAddress();
if(StringUtils.isNotEmpty(address)){
bean.setAddress("");//将bean中的address值置空,才能设置模糊查询
criteria.andAddressLike("%" + address + "%");
}
Integer age = bean.getAge();
if(null != age){
bean.setAge(null);
//设置age大于20岁的查询条件
criteria.andAgeGreaterThan(20)
}
Page<AdScript> page = super.page(bean, page, pageSize);
return page;
}
}
(2)代码解析
整个page方法的调用流程如下图所示:
由于 UserService
中实现getRepository()
,且其返回 UserMapper
,那么 UserService
中调用其父类AbstractService
中的page
方法查询分页数据的时候,page
方法中的getRepository().selectByExample(bean);
相当于UserMapper.selectByExample(bean);
,而PageHelper.startPage(page, pageSize)
将查询的数据进行分页操作。
同样,如果想在UserService
执行插入方法,由于 UserMapper
中有这种方法,可以直接调用UserMapper
的插入方法UserMapper.insert(bean)
;也可以向上面一样,调用UserService
的父类AbstractService
的add
方法super.add(bean)
。
这里就有一个问题:既然都是调用UserMapper
的方法,为什么还要搞出一个AbstractService
类,为什么不在UserService
直接调用UserMapper
的方法,我们这样操作不是复杂了很多?
答:这是因为,我们在实际的业务中,会对很多表进行crud操作,会生成很多个类似于UserMapper
的类,种类的方法虽然都很齐全,但是对于分页这种特殊的方法,Userapper
类原生没有page
方法。如果我们把分页方法都写到AdScriptMapper
这种类中,那么每写一个mapper
,都需要写一个page
方法。
如果把page
写到 AbstractService
中,那么我们只需要通过泛型传递参数就可以,不需要写那么多个page
方法。同样,如果有类似于page
的 mybatis generator
无法生成的通用的方法,只需要在 AbstractService
中添加一个方法即可!这样就会大大节省代码量。
这个分页方法比较复杂,但是正式的开发环境中,在处理多个数据的分页等通用需求的时候,这个方法使用起来会很方便,可以省很多代码。