几种常见mybatis分页的实现方式
作者:luffy5459
几种常见mybatis分页实现
mybatis框架分页实现,有几种方式,最简单的就是利用原生的sql关键字limit来实现,还有一种就是利用interceptor来拼接sql,实现和limit一样的功能,再一个就是利用PageHelper来实现。
这里讲解这三种常见的实现方式:
无论哪种实现方式,我们返回的结果,不能再使用List了,需要一个自定义对象Pager。
package com.xxx.mybatis.bean; import java.util.List; public class Pager<T> { private int page;//分页起始页 private int size;//每页记录数 private List<T> rows;//返回的记录集合 private long total;//总记录条数 public int getPage() { return page; } public void setPage(int page) { this.page = page; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public List<T> getRows() { return rows; } public void setRows(List<T> rows) { this.rows = rows; } public long getTotal() { return total; } public void setTotal(long total) { this.total = total; } }
limit关键字实现
UserDao.java增加两个方法
public List<User> findByPager(Map<String, Object> params); public long count();
UserMapper.xml中增加两个查询
<select id="findByPager" resultType="com.xxx.mybatis.domain.User"> select * from xx_user limit #{page},#{size} </select> <select id="count" resultType="long"> select count(1) from xx_user </select>
UserService.java中增加分页方法
public Pager<User> findByPager(int page,int size){ Map<String, Object> params = new HashMap<String, Object>(); params.put("page", (page-1)*size); params.put("size", size); Pager<User> pager = new Pager<User>(); List<User> list = userDao.findByPager(params); pager.setRows(list); pager.setTotal(userDao.count()); return pager; }
这是最直观的实现方式,也是最简单的,不用任何插件或者工具就能够很方便的实现的方法。
interceptor plugin实现
需要定义一个类实现Interceptor接口
MyPageInterceptor.java
package com.xxx.mybatis.bean; import java.sql.Connection; import java.util.Map; import java.util.Properties; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})}) public class MyPageInterceptor implements Interceptor { private int page; private int size; @SuppressWarnings("unused") private String dbType; @SuppressWarnings("unchecked") @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("plugin is running..."); StatementHandler statementHandler = (StatementHandler)invocation.getTarget(); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); while(metaObject.hasGetter("h")){ Object object = metaObject.getValue("h"); metaObject = SystemMetaObject.forObject(object); } while(metaObject.hasGetter("target")){ Object object = metaObject.getValue("target"); metaObject = SystemMetaObject.forObject(object); } MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement"); String mapId = mappedStatement.getId(); if(mapId.matches(".+ByPager$")){ ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler"); Map<String, Object> params = (Map<String, Object>)parameterHandler.getParameterObject(); page = (int)params.get("page"); size = (int)params.get("size"); String sql = (String) metaObject.getValue("delegate.boundSql.sql"); sql += " limit "+(page-1)*size +","+size; metaObject.setValue("delegate.boundSql.sql", sql); } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { String limit = properties.getProperty("limit","10"); this.page = Integer.parseInt(limit); this.dbType = properties.getProperty("dbType", "mysql"); } }
我们之前在service的findByPager方法里面,为了给limit传入两个参数,其中page做了计算,这里使用拦截器的方式就无需计算了:
public Pager<User> findByPager(int page,int size){ Map<String, Object> params = new HashMap<String, Object>(); params.put("page", page); params.put("size", size); Pager<User> pager = new Pager<User>(); List<User> list = userDao.findByPager(params); pager.setRows(list); pager.setTotal(userDao.count()); return pager; }
spring配置中,增加plugin设置:
到这里,你也许也猜到了MyPageInterceptor实际上是以一种拦截器的方式在程序执行findByPager方法的时候,对语句会增加limit page,size的拼接,还是和第一种原生实现思路一样,所以这里需要对UserMapper.xml配置文件中的findByPager这个查询对应的语句中的limit #{page},#{size}这部分去掉,变为如下的样子:
至此,通过拦截器插件的方式也实现了分页功能了。
PageHelper实现
这种方式实现需要我们引入maven依赖。
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>4.2.1</version> </dependency>
spring.xml配置文件做一下修改:
<bean id="pageInterceptor" class="com.github.pagehelper.PageHelper"> <property name="properties"> <props> <prop key="helperDialect">mysql</prop> <prop key="reasonable">true</prop> <prop key="supportMethodsArguments">true</prop> <prop key="params">count=countSql</prop> </props> </property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:com/xxx/mybatis/dao/*Mapper.xml"/> <property name="plugins" ref="pageInterceptor"></property> </bean>
service层的方法,做一些修改:
public Pager<User> findByPager(int page,int size){ Pager<User> pager = new Pager<User>(); Page<User> res = PageHelper.startPage(page,size); userDao.findAll(); pager.setRows(res.getResult()); pager.setTotal(res.getTotal()); return pager; }
至此,PageHelper工具方法实现分页也实现了。其实PageHelper方法也是第二种使用Interceptor拦截器方式的一种三方实现,它内部帮助我们实现了Interceptor的功能。
所以我们不用自定义MyPageInterceptor这个类了。实际上也是在运行查询方法的时候,进行拦截,然后设置分页参数。
所以PageHelper.startPage(page,size)这一句需要显示调用,然后再执行userDao.findAll(),在查询所有用户信息的时候,会进行一个分页参数设置,让放回的结果只是分页的结果,而不是全部集合。
mybatis实现分页查询
使用mybatis整合时,最容易遇到的需求就是分页了,pagehelper 分页插件是一个非常实用的 MyBatis分页插件,可以快速的实现MyBatis分页功能,而且pagehelper有个优点就是分页和Mapper.xml完全解耦,并以插件的形式实现,有效的避免了直接写分页SQL语句来实现分页功能
分页插件原理
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,添加对应的物理分页语句和物理分页参数
举例:
select * from student
拦截sql后重写为:
select t.* from (select * from student)t limit 0,10
1.引入pagehelper依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.11</version> </dependency>
2.配置拦截器
这个是配置在mybatis-config.xml文件中
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="param1" value="value1"/> </plugin> </plugins>
3.在代码中的使用
@RequestMapping("/emps") public String list(@RequestParam(required = false,defaultValue = "1",value = "pn")Integer pn, Map<String,Object> map){ //引入分页查询,使用PageHelper分页功能 //在查询之前传入当前页,然后多少记录 PageHelper.startPage(pn,5); //startPage后紧跟的这个查询就是分页查询 List<Employee> emps = employeeService.getAll(); //使用PageInfo包装查询结果,只需要将pageInfo交给页面就可以 PageInfo pageInfo = new PageInfo<>(emps,5); //pageINfo封装了分页的详细信息,也可以指定连续显示的页数 map.put("pageInfo",pageInfo); return "list"; }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。