本文主要介绍了使用 bootstrap table 与 java 实现的数据拉取与分页展示的 2 个案例。 第一个案例通过分页参数 limit 与 offset 直接从数据查询,第二个案例则是使用 `com.github.pagehelper.Page`的分页插件进行分页处理。相比之下,第一个案例的使用比较简单,但是代码冗余较大。而第二个案例则比较轻量,但是用起来比较麻烦。

本文主要介绍了使用 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)前端 htmljs 代码

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,这个接口的方法比较多,为了方便展示,只列出 addpage2个方法,事实上里面还可以自定义很多数据库操作方法,如 getdeleteByPrimaryKey

public interface IService<BEAN, BEAN_EXAMPLE,KEYTYPE> {  
  /**  
   * 添加记录接口.   
  */  
  BEAN add(BEAN bean);  
  
 /**  
  * 分页接口 
 */   
 Page<BEAN> page(BEAN_EXAMPLE bean, int page, int pageSize);    
}

其次是 操作数据库的抽象服务总类AbstractService ,这个类的方法比较多,为了方便展示,只列出 addpage2个方法以及抽象方法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> ,同时实现AbstractServicegetRepository方法。

由于AbstractServicegetRepository方法返回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方法的调用流程如下图所示:

JAVA分页加载数据_分页

由于 UserService中实现getRepository(),且其返回 UserMapper,那么 UserService中调用其父类AbstractService中的page方法查询分页数据的时候,page方法中的getRepository().selectByExample(bean);相当于UserMapper.selectByExample(bean);,而PageHelper.startPage(page, pageSize)将查询的数据进行分页操作。

同样,如果想在UserService执行插入方法,由于 UserMapper中有这种方法,可以直接调用UserMapper的插入方法UserMapper.insert(bean);也可以向上面一样,调用UserService的父类AbstractServiceadd方法super.add(bean)

这里就有一个问题:既然都是调用UserMapper的方法,为什么还要搞出一个AbstractService类,为什么不在UserService直接调用UserMapper的方法,我们这样操作不是复杂了很多?

:这是因为,我们在实际的业务中,会对很多表进行crud操作,会生成很多个类似于UserMapper的类,种类的方法虽然都很齐全,但是对于分页这种特殊的方法,Userapper类原生没有page方法。如果我们把分页方法都写到AdScriptMapper这种类中,那么每写一个mapper,都需要写一个page方法。

如果把page写到 AbstractService中,那么我们只需要通过泛型传递参数就可以,不需要写那么多个page方法。同样,如果有类似于pagemybatis generator 无法生成的通用的方法,只需要在 AbstractService中添加一个方法即可!这样就会大大节省代码量。

这个分页方法比较复杂,但是正式的开发环境中,在处理多个数据的分页等通用需求的时候,这个方法使用起来会很方便,可以省很多代码。