MyBatis分页插件:比PageHelper都要简单,使用很灵活,目前支持Oracle和MySQL
Posted ykzhen2015
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis分页插件:比PageHelper都要简单,使用很灵活,目前支持Oracle和MySQL相关的知识,希望对你有一定的参考价值。
1、概述:
经过努力写了个分页插件,目前支持mysql和Oracle。它的使用应该是目前为止最简单的,对于PageHelper据原作者描述使用了ThreadLocal等技术,避免多线程的不一致,PageHelper实现较为复杂,其次也有其定义的startPage,endPage这样的开关代码,造成代码侵入,而没有屏蔽PageHelper导致代码污染。
我的插件十分简单易用,代码污染性极小,也无需考虑多线程的问题,反正都是一个事务中处理的,其次使用方法支持四种,十分灵活。
2、插件代码,配置和简易的POJO
我们先看看插件的代码:
package com.learn.mybatis.plugin; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.transaction.NotSupportedException; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; 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; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.Configuration; import org.apache.log4j.Logger; import com.learn.mybatis.chapter1.main.MyBatisExample; /** * * @author ykzhen2015 */ @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})}) public class PagingPlugin implements Interceptor { /** * 插件默认参数,可配置 */ private Integer defaultPage; //默认页码 private Integer defaultPageSize;//默认每页条数 private Boolean defaultUseFlag; //默认是否启用插件 private Boolean defaultCheckFlag; //默认是否检测页码参数 private Boolean defaultCleanOrderBy; //默认是否清除最后一个order by 后的语句 private static final String DB_TYPE_MYSQL = "mysql"; private static final String DB_TYPE_ORACLE = "oracle"; /** * 插件实现方法 */ @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler stmtHandler = (StatementHandler) getUnProxyObject(invocation.getTarget()); MetaObject metaStatementHandler = SystemMetaObject.forObject(stmtHandler); String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); String dbType = this.getDataSourceType(mappedStatement); //不是select语句. if (!this.checkSelect(sql)) { return invocation.proceed(); } BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql"); Object parameterObject = boundSql.getParameterObject(); PageParams pageParams = getPageParamsForParamObj(parameterObject); if (pageParams == null) { //无法获取分页参数,不进行分页。 return invocation.proceed(); } //获取配置中是否启用分页功能. Boolean useFlag = pageParams.getUseFlag() == null? this.defaultUseFlag : pageParams.getUseFlag(); if (!useFlag) { //不使用分页插件. return invocation.proceed(); } //获取相关配置的参数. Integer pageNum = pageParams.getPage() == null? defaultPage : pageParams.getPage(); Integer pageSize = pageParams.getPageSize() == null? defaultPageSize : pageParams.getPageSize(); Boolean checkFlag = pageParams.getCheckFlag() == null? defaultCheckFlag : pageParams.getCheckFlag(); Boolean cleanOrderBy = pageParams.getCleanOrderBy() == null? defaultCleanOrderBy : pageParams.getCleanOrderBy(); int total = this.getTotal(invocation, metaStatementHandler, boundSql, cleanOrderBy, dbType); pageParams.setTotal(total); //计算总页数. int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1; pageParams.setTotalPage(totalPage); //检查当前页码的有效性. this.checkPage(checkFlag, pageNum, totalPage); //修改sql return this.preparedSQL(invocation, metaStatementHandler, boundSql, pageNum, pageSize, dbType); } /*** * 分离出分页参数. * @param parameterObject --执行参数 * @return 分页参数 * @throws Exception */ public PageParams getPageParamsForParamObj(Object parameterObject) throws Exception { PageParams pageParams = null; if (parameterObject == null) { return null; } //处理map参数和@Param注解参数,都是MAP if (parameterObject instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> paramMap = (Map<String, Object>) parameterObject; Set<String> keySet = paramMap.keySet(); Iterator<String> iterator = keySet.iterator(); while(iterator.hasNext()) { String key = iterator.next(); Object value = paramMap.get(key); if (value instanceof PageParams) { return (PageParams)value; } } } else if (parameterObject instanceof PageParams) { //参数POJO继承了PageParams return (PageParams) parameterObject; } else { //从POJO尝试读取分页参数. Field[] fields = parameterObject.getClass().getDeclaredFields(); //尝试从POJO中获得类型为PageParams的属性 for (Field field : fields) { if (field.getType() == PageParams.class) { PropertyDescriptor pd = new PropertyDescriptor (field.getName(), parameterObject.getClass()); Method method = pd.getReadMethod(); return (PageParams) method.invoke(parameterObject); } } } return pageParams; } /** * 判断是否sql语句. * @param sql * @return */ private boolean checkSelect(String sql) { String trimSql = sql.trim(); int idx = trimSql.toLowerCase().indexOf("select"); return idx == 0; } /** * 检查当前页码的有效性. * @param checkFlag * @param pageNum * @param pageTotal * @throws Throwable */ private void checkPage(Boolean checkFlag, Integer pageNum, Integer pageTotal) throws Throwable { if (checkFlag) { //检查页码page是否合法. if (pageNum > pageTotal) { throw new Exception("查询失败,查询页码【" + pageNum + "】大于总页数【" + pageTotal + "】!!"); } } } /** * 预编译改写后的SQL,并设置分页参数 * @param invocation * @param metaStatementHandler * @param boundSql * @param pageNum * @param pageSize * @param dbType * @throws IllegalAccessException * @throws InvocationTargetException */ private Object preparedSQL(Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql, int pageNum, int pageSize, String dbType) throws Exception { //获取当前需要执行的SQL String sql = boundSql.getSql(); String newSql = this.getPageDataSQL(sql, dbType); //修改当前需要执行的SQL metaStatementHandler.setValue("delegate.boundSql.sql", newSql); //执行编译,这里相当于StatementHandler执行了prepared()方法,这个时候,就剩下2个分页参数没有设置。 Object statementObj = invocation.proceed(); //设置两个分页参数。 this.preparePageDataParams((PreparedStatement)statementObj, pageNum, pageSize, dbType); return statementObj; } /** * 获取综述. * * @param ivt Invocation * @param metaStatementHandler statementHandler * @param boundSql sql * @param cleanOrderBy 是否清除order by语句 * * @param dbType * @return sql查询总数. * @throws Throwable 异常. */ private int getTotal(Invocation ivt, MetaObject metaStatementHandler, BoundSql boundSql, Boolean cleanOrderBy, String dbType) throws Throwable { //获取当前的mappedStatement MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); //配置对象 Configuration cfg = mappedStatement.getConfiguration(); //当前需要执行的SQL String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); //去掉最后的order by语句 if (cleanOrderBy) { sql = this.cleanOrderByForSql(sql); } String countSql = this.getTotalSQL(sql, dbType); //获取拦截方法参数,根据插件签名,知道是Connection对象. Connection connection = (Connection) ivt.getArgs()[0]; PreparedStatement ps = null; int total = 0; try { //预编译统计总数SQL ps = connection.prepareStatement(countSql); //构建统计总数SQL BoundSql countBoundSql = new BoundSql(cfg, countSql, boundSql.getParameterMappings(), boundSql.getParameterObject()); //构建MyBatis的ParameterHandler用来设置总数Sql的参数。 ParameterHandler handler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), countBoundSql); //设置总数SQL参数 handler.setParameters(ps); //执行查询. ResultSet rs = ps.executeQuery(); while (rs.next()) { total = rs.getInt("total"); } } finally { //这里不能关闭Connection否则后续的SQL就没法继续了。 if (ps != null && ps.isClosed()) { ps.close(); } } return total; } private String cleanOrderByForSql(String sql) { StringBuilder sb = new StringBuilder(sql); String newSql = sql.toLowerCase(); //如果没有order语句,直接返回 if (newSql.indexOf("order") == -1) { return sql; } int idx = newSql.lastIndexOf("order"); return sb.substring(0, idx).toString(); } /** * 从代理对象中分离出真实对象. * * @param ivt --Invocation * @return 非代理StatementHandler对象 */ private Object getUnProxyObject(Object target) { MetaObject metaStatementHandler = SystemMetaObject.forObject(target); // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过循环可以分离出最原始的的目标类) Object object = null; while (metaStatementHandler.hasGetter("h")) { object = metaStatementHandler.getValue("h"); } if (object == null) { return target; } return object; } /** * 生成代理对象 * @param statementHandler 原始对象 * @return 代理对象 */ @Override public Object plugin(Object statementHandler) { return Plugin.wrap(statementHandler, this); } /** * 设置插件配置参数。 * @param props */ @Override public void setProperties(Properties props) { String strDefaultPage = props.getProperty("default.page", "1"); String strDefaultPageSize = props.getProperty("default.pageSize", "50"); String strDefaultUseFlag = props.getProperty("default.useFlag", "false"); String strDefaultCheckFlag = props.getProperty("default.checkFlag", "false"); String StringDefaultCleanOrderBy = props.getProperty("default.cleanOrderBy", "false"); this.defaultPage = Integer.parseInt(strDefaultPage); this.defaultPageSize = Integer.parseInt(strDefaultPageSize); this.defaultUseFlag = Boolean.parseBoolean(strDefaultUseFlag); this.defaultCheckFlag = Boolean.parseBoolean(strDefaultCheckFlag); this.defaultCleanOrderBy = Boolean.parseBoolean(StringDefaultCleanOrderBy); } /** * TODO * 计算总数的SQL, * 这里需要根据数据库的类型改写SQL,目前支持MySQL和Oracle * @param currSql —— 当前执行的SQL * @return 改写后的SQL * @throws NotSupportedException */ private String getTotalSQL(String currSql, String dbType) throws NotSupportedException { if (DB_TYPE_MYSQL.equals(dbType)) { return "select count(*) as total from (" + currSql + ") $_paging"; } else if (DB_TYPE_ORACLE.equals(dbType)) { return "select count(*) as total from (" + currSql +")"; } else { throw new NotSupportedException("当前插件未支持此类型数据库"); } } /** * TODO 需要使用其他数据库需要改写 * 分页获取参数的SQL * 这里需要根据数据库的类型改写SQL,目前支持MySQL和Oracle * @param currSql —— 当前执行的SQL * @return 改写后的SQL * @throws NotSupportedException */ private String getPageDataSQL(String currSql, String dbType) throws NotSupportedException { if (DB_TYPE_MYSQL.equals(dbType)) { return "select * from (" + currSql + ") $_paging_table limit ?, ?"; } else if (DB_TYPE_ORACLE.equals(dbType)) { return " select * from (select cur_sql_result.*, rownum rn from (" + currSql + ") cur_sql_result where rownum <= ?) where rn > ?"; } else { throw new NotSupportedException("当前插件未支持此类型数据库"); } } /** * TODO 需要使用其他数据库需要改写 * 使用PreparedStatement预编译两个分页参数,如果数据库的规则不一样,需要改写设置的参数规则。目前支持MySQL和Oracle * @throws SQLException * @throws NotSupportedException * */ private void preparePageDataParams(PreparedStatement ps, int pageNum, int pageSize, String dbType) throws Exception { //prepared()方法编译SQL,由于MyBatis上下文没有我们分页参数的信息,所以这里需要设置这两个参数. //获取需要设置的参数个数,由于我们的参数是最后的两个,所以很容易得到其位置 int idx = ps.getParameterMetaData().getParameterCount(); if (DB_TYPE_MYSQL.equals(dbType)) { //最后两个是我们的分页参数. ps.setInt(idx -1, (pageNum - 1) * pageSize);//开始行 ps.setInt(idx, pageSize); //限制条数 } else if (DB_TYPE_ORACLE.equals(dbType)) { ps.setInt(idx -1, pageNum * pageSize);//结束行 ps.setInt(idx, (pageNum - 1) * pageSize); //开始行 } else { throw new NotSupportedException("当前插件未支持此类型数据库"); } } /** * * TODO 需要使用其他数据库需要改写 * 目前支持MySQL和Oracle * @param mappedStatement * @return * @throws Exception */ private String getDataSourceType(MappedStatement mappedStatement) throws Exception { String dbConnectionStr = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection().toString(); dbConnectionStr = dbConnectionStr.toLowerCase(); if (dbConnectionStr.contains(DB_TYPE_MYSQL)) { return DB_TYPE_MYSQL; } else if (dbConnectionStr.contains(DB_TYPE_ORACLE)) { return DB_TYPE_ORACLE; } else { throw new NotSupportedException("当前插件未支持此类型数据库"); } } }
如果你是使用非Oracle或者MySQL的,你需要自己注解有TODO的方法,修改是也很简单。
有了这个插件,还要配置一下,MyBatis的配置文件如下:
<configuration> ...... <plugins> <plugin interceptor="com.learn.mybatis.plugin.PagingPlugin"> <!-- 默认页码 --> <property name="default.page" value="1" /> <!-- 默认每页条数--> <property name="default.pageSize" value="20" /> <!-- 是否启动分页插件功能 --> <property name="default.useFlag" value="true" /> <!-- 是否检查页码有效性,如果非有效,则抛出异常. --> <property name="default.checkFlag" value="false" /> <!-- 针对哪些含有order by的SQL,是否去掉最后一个order by 以后的SQL语句,提高性能 --> <property name="default.cleanOrderBy" value="false" /> </plugin> </plugins> ...... </configuration>
上面我们可以看到一些默认值,可以根据我自己定义的一个POJO修改上面配置的默认值,这个POJO十分简单,它符合JavaBean的一切规范,如下:(其属性含义见其注解)
package com.learn.mybatis.plugin; import java.util.List; /** * * @author ykzhen2015 */ public class PageParams { private Integer page;//第几页 private Integer pageSize;//每页限制条数 private Boolean useFlag;//是否启动插件,如果不启动,则不作分页 private Boolean checkFlag;//是否检测页码的有效性,如果为true,而页码大于最大页数,则抛出异常 private Boolean cleanOrderBy;//是否清除最后order by 后面的语句 private Integer total; //总条数,插件会回填这个值 private Integer totalPage; //总页数,插件会回填这个值. public Integer getPage() { return page; } public void setPage(Integer page) { this.page = page; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Boolean getUseFlag() { return useFlag; } public void setUseFlag(Boolean useFlag) { this.useFlag = useFlag; } public Boolean getCheckFlag() { return checkFlag; } public void setCheckFlag(Boolean checkFlag) { this.checkFlag = checkFlag; } public Boolean getCleanOrderBy() { return cleanOrderBy; } public void setCleanOrderBy(Boolean cleanOrderBy) { this.cleanOrderBy = cleanOrderBy; } public Integer getTotal() { return total; } public void setTotal(Integer total) { this.total = total; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } }
当以下属性被设置会有这样的效果:
- page --插件默认设置default.page作废,采取这个值,整数型
- pageSize --插件默认设置default.pageSize作废,采取这个值,整数型
- useFlag --插件默认设置default.useFlag作废,采取这个值,布尔值
- checkFlag --插件默认设置default.checkFlag作废,采取这个值,布尔值
- cleanOrderBy -- 插件默认设置default.cleanOrderBy作废,采用这个值,布尔值
而:
total ——代表总条数,插件回自动帮我们回填,整数型
3、插件的使用:
这个插件使用起来十分简单,只要传递给MyBatis的SqlMap以下的方式都是允许的:
- 继承com.learn.mybatis.plugin.PageParams
- 使用map,但是map中的键值对应,有一个值是com.learn.mybatis.plugin.PageParams类型
- 使用@Param注解,只要里面有一个参数是com.learn.mybatis.plugin.PageParams类型
- 使用POJO,但是这个POJO中有一个属性为com.learn.mybatis.plugin.PageParams类型
以上4种形式都可以使用这个分页插件,是不是很灵活易用??
让我们试用一下这个插件,看看这四种传递参数的方法。
现在我们定义一个map.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.learn.mybatis.chapter1.mapper.RoleMapper"> <select id="getRole" parameterType="long" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from t_role where id=#{id} </select> <select id="selectAllRole" parameterType="com.learn.mybatis.plugin.TestParams" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from t_role <where> <if test="roleName != null"> role_name like concat('%', #{roleName}, '%') </if> </where> order by id desc </select> <select id="selectAllRole2" parameterType="com.learn.mybatis.plugin.TestParams2" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from t_role <where> <if test="roleName != null"> role_name like concat('%', #{roleName}, '%') </if> </where> order by id desc </select> <select id="selectRoleByMap" parameterType="map" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from t_role <where> <if test="roleName != null"> role_name like concat('%', #{roleName}, '%') </if> </where> order by id desc </select> <select id="selectRoleByMap2" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from t_role </select> <select id="selectRoleByKey" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from t_role <where> <if test="roleName != null"> role_name like concat('%', #{roleName}, '%') </if> </where> order by id desc </select> <select id="test$sql" resultType="com.learn.mybatis.chapter1.po.TRole"> select id, role_name as roleName, note from ${tableName} <where> <if test="roleName != null"> role_name like concat('%', #{roleName}, '%') </if> </where> order by role_name desc </select> </mapper>
定义其接口:
package com.learn.mybatis.chapter1.mapper; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.session.RowBounds; import com.learn.mybatis.chapter1.po.TRole; import com.learn.mybatis.plugin.PageParams; import com.learn.mybatis.plugin.TestParams; import com.learn.mybatis.plugin.TestParams2; public interface RoleMapper { //不含分页参数,就不会启用 public TRole getRole(Long id); //继承方式 public List<TRole> selectAllRole(TestParams pageParams); //参数为POJO,但是POJO内部包含一个类型为com.learn.mybatis.plugin.PageParams的属性 public List<TRole> selectAllRole2(TestParams2 testParams2); //使用map,里面有一个com.learn.mybatis.plugin.PageParams类型的值 public List<TRole> selectRoleByMap(Map<String, Object> params); //使用注解的形式传递分页参数。 public List<TRole> selectRoleByMap2(@Param("roleName") String roleName, @Param("page221232323") PageParams pageParams); }
好,看到注解,里面已经包含了上述4种传参的方法,这个插件都是支持的。先看到selectAllRole()方法
里面的参数TestParams定义(继承PageParams):
package com.learn.mybatis.plugin; public class TestParams extends PageParams { String roleName; public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } }再看到selectAllRole2()方法,里面的参数TestParams2定义(其属性有一个PageParams):
package com.learn.mybatis.plugin; public class TestParams2 { private PageParams pageParams = null; private String roleName; public PageParams getPageParams() { return pageParams; } public void setPageParams(PageParams pageParams) { this.pageParams = pageParams; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } }
好其他的都很简单了。
我们来测试这个接口,测试代码如下,注意看四种方式传递参数,代码的污染是很小的:
package com.learn.mybatis.chapter1.main; import java.util.HashMap; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.apache.log4j.Logger; import com.learn.mybatis.chapter1.mapper.RoleMapper; import com.learn.mybatis.chapter1.mb.util.MyBatisUtil; import com.learn.mybatis.plugin.PageParams; import com.learn.mybatis.plugin.TestParams; import com.learn.mybatis.plugin.TestParams2; public class MyBatisExample { private static Logger logger = Logger.getLogger(MyBatisExample.class); public static void main(String[] args) { testGetRole(); testInherit(); testMap(); testAtParam(); testPojoProperty(); } /** * 继承PageParams的参数传递. */ public static void testGetRole() { logger.info("#################测试没有分页参数的查询###########################"); SqlSession sqlSession = null; try { sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); roleMapper.getRole(1L); } finally { sqlSession.close(); } } /** * 继承PageParams的参数传递. */ public static void testInherit() { logger.info("#################测试分页参数以继承PageParams形式传递###########################"); SqlSession sqlSession = null; try { sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); TestParams params = new TestParams(); params.setUseFlag(true); params.setCheckFlag(false); params.setPage(2); params.setPageSize(5); params.setRoleName("test"); roleMapper.selectAllRole(params); logger.info("总条数==>" + params.getTotal()); logger.info("总页数 ==>" + params.getTotalPage()); } finally { sqlSession.close(); } } /** * 使用map方式。(map的键随意取) */ public static void testMap() { logger.info("#################测试分页参数以Map形式传递###########################"); SqlSession sqlSession = null; try { sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); Map<String, Object> paramMap = new HashMap<String, Object>(); PageParams pageParams = new PageParams(); pageParams.setUseFlag(true); pageParams.setCheckFlag(false); pageParams.setPage(2); pageParams.setPageSize(5); paramMap.put("roleName", "test"); paramMap.put("page_drsdsd2233", pageParams); roleMapper.selectRoleByMap(paramMap); logger.info("总条数==>" + pageParams.getTotal()); logger.info("总页数 ==>" + pageParams.getTotalPage()); } finally { sqlSession.close(); } } /** * 使用MyBatis @Param注解 传递参数. */ public static void testAtParam() { logger.info("#################测试分页参数以@Param形式传递###########################"); SqlSession sqlSession = null; try { sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); PageParams pageParams2 = new PageParams(); pageParams2.setUseFlag(true); pageParams2.setCheckFlag(true); pageParams2.setPage(2); pageParams2.setPageSize(5); pageParams2.setCleanOrderBy(true); roleMapper.selectRoleByMap2("test", pageParams2); logger.info("总条数==>" + pageParams2.getTotal()); logger.info("总页数 ==>" + pageParams2.getTotalPage()); } finally { sqlSession.close(); } } /** * 使用POJO传递,但是POJO内部有一个PageParams属性,和它的setter方法. */ public static void testPojoProperty() { logger.info("#################测试分页参数作为参数属性传递###########################"); SqlSession sqlSession = null; try { sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); PageParams pageParams3 = new PageParams(); pageParams3.setUseFlag(true); pageParams3.setCheckFlag(false); pageParams3.setPage(3); pageParams3.setPageSize(5); pageParams3.setCleanOrderBy(true); TestParams2 p2 = new TestParams2(); p2.setPageParams(pageParams3); p2.setRoleName("test"); roleMapper.selectAllRole2(p2); logger.info("总条数==>" + pageParams3.getTotal()); logger.info("总页数 ==>" + pageParams3.getTotalPage()); } finally { sqlSession.close(); } } }
测试的结果如下(我这里使用mysql也可以使用oracle,我也有测试过的):
INFO 2016-06-23 21:02:37,966 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试没有分页参数的查询########################### DEBUG 2016-06-23 21:02:38,169 org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:02:38,267 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection DEBUG 2016-06-23 21:02:38,470 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 103887628. DEBUG 2016-06-23 21:02:38,470 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,485 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1752203484. DEBUG 2016-06-23 21:02:38,486 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, role_name as roleName, note from t_role where id=? DEBUG 2016-06-23 21:02:38,508 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long) DEBUG 2016-06-23 21:02:38,532 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0 DEBUG 2016-06-23 21:02:38,533 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,534 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,535 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool. INFO 2016-06-23 21:02:38,539 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数以继承PageParams形式传递########################### DEBUG 2016-06-23 21:02:38,565 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection DEBUG 2016-06-23 21:02:38,565 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool. DEBUG 2016-06-23 21:02:38,565 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,575 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1682463303. DEBUG 2016-06-23 21:02:38,576 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging DEBUG 2016-06-23 21:02:38,576 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String) DEBUG 2016-06-23 21:02:38,577 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-06-23 21:02:38,577 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging_table limit ?, ? DEBUG 2016-06-23 21:02:38,579 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 5(Integer), 5(Integer), test(String) DEBUG 2016-06-23 21:02:38,583 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 5 INFO 2016-06-23 21:02:38,584 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10 INFO 2016-06-23 21:02:38,584 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2 DEBUG 2016-06-23 21:02:38,584 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,585 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,585 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool. INFO 2016-06-23 21:02:38,585 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数以Map形式传递########################### DEBUG 2016-06-23 21:02:38,588 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection DEBUG 2016-06-23 21:02:38,588 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool. DEBUG 2016-06-23 21:02:38,588 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,601 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 156545103. DEBUG 2016-06-23 21:02:38,601 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging DEBUG 2016-06-23 21:02:38,602 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String) DEBUG 2016-06-23 21:02:38,604 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-06-23 21:02:38,604 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging_table limit ?, ? DEBUG 2016-06-23 21:02:38,605 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 5(Integer), 5(Integer), test(String) DEBUG 2016-06-23 21:02:38,608 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 5 INFO 2016-06-23 21:02:38,609 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10 INFO 2016-06-23 21:02:38,609 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2 DEBUG 2016-06-23 21:02:38,609 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,610 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,610 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool. INFO 2016-06-23 21:02:38,610 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数以@Param形式传递########################### DEBUG 2016-06-23 21:02:38,612 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection DEBUG 2016-06-23 21:02:38,613 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool. DEBUG 2016-06-23 21:02:38,613 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,627 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 668210649. DEBUG 2016-06-23 21:02:38,627 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role) $_paging DEBUG 2016-06-23 21:02:38,628 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: DEBUG 2016-06-23 21:02:38,630 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-06-23 21:02:38,630 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from (select id, role_name as roleName, note from t_role) $_paging_table limit ?, ? DEBUG 2016-06-23 21:02:38,631 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 5(Integer), 5(Integer) DEBUG 2016-06-23 21:02:38,633 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 5 INFO 2016-06-23 21:02:38,634 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10 INFO 2016-06-23 21:02:38,634 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2 DEBUG 2016-06-23 21:02:38,634 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,635 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,635 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool. INFO 2016-06-23 21:02:38,635 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数作为参数属性传递########################### DEBUG 2016-06-23 21:02:38,636 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection DEBUG 2016-06-23 21:02:38,636 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool. DEBUG 2016-06-23 21:02:38,636 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,643 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1361289747. DEBUG 2016-06-23 21:02:38,644 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') ) $_paging DEBUG 2016-06-23 21:02:38,644 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String) DEBUG 2016-06-23 21:02:38,645 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-06-23 21:02:38,645 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select * from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging_table limit ?, ? DEBUG 2016-06-23 21:02:38,646 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 10(Integer), 5(Integer), test(String) DEBUG 2016-06-23 21:02:38,648 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0 INFO 2016-06-23 21:02:38,648 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10 INFO 2016-06-23 21:02:38,648 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2 DEBUG 2016-06-23 21:02:38,648 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,649 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]] DEBUG 2016-06-23 21:02:38,649 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool.
此时为了测试验证页码的和去掉order by语句的效果,我们再把testPojoProperty()方法修改一下:
/** * 使用POJO传递,但是POJO内部有一个PageParams属性,和它的setter方法. */ public static void testPojoProperty() { logger.info("#################测试分页参数作为参数属性传递###########################"); SqlSession sqlSession = null; try { sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); PageParams pageParams3 = new PageParams(); pageParams3.setUseFlag(true); pageParams3.setCheckFlag(true);//检验页码的有效性。 pageParams3.setPage(6);//超过最大页数 pageParams3.setPageSize(5); pageParams3.setCleanOrderBy(false);//不去掉order by 语句 TestParams2 p2 = new TestParams2(); p2.setPageParams(pageParams3); p2.setRoleName("test"); roleMapper.selectAllRole2(p2); logger.info("总条数==>" + pageParams3.getTotal()); logger.info("总页数 ==>" + pageParams3.getTotalPage()); } finally { sqlSession.close(); } }
再运行一下,得到日志:
INFO 2016-06-23 21:09:09,643 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数作为参数属性传递########################### DEBUG 2016-06-23 21:09:09,839 org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. DEBUG 2016-06-23 21:09:09,856 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:09:09,856 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:09:09,856 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:09:09,857 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections. DEBUG 2016-06-23 21:09:09,957 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection DEBUG 2016-06-23 21:09:10,166 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 142555199. DEBUG 2016-06-23 21:09:10,166 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:09:10,181 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 192794887. DEBUG 2016-06-23 21:09:10,182 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging DEBUG 2016-06-23 21:09:10,218 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String) DEBUG 2016-06-23 21:09:10,243 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-06-23 21:09:10,245 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]] DEBUG 2016-06-23 21:09:10,245 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]] DEBUG 2016-06-23 21:09:10,246 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 142555199 to pool. Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.lang.reflect.UndeclaredThrowableException ### The error may exist in com\learn\mybatis\chapter1\sqlmap\role.xml ### The error may involve com.learn.mybatis.chapter1.mapper.RoleMapper.selectAllRole2-Inline ### The error occurred while setting parameters ### Cause: java.lang.reflect.UndeclaredThrowableException at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:26) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:111) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102) at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:119) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52) at com.sun.proxy.$Proxy4.selectAllRole2(Unknown Source) at com.learn.mybatis.chapter1.main.MyBatisExample.testPojoProperty(MyBatisExample.java:131) at com.learn.mybatis.chapter1.main.MyBatisExample.main(MyBatisExample.java:23) Caused by: java.lang.reflect.UndeclaredThrowableException at com.sun.proxy.$Proxy5.prepare(Unknown Source) at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:73) at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:59) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:137) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:96) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:77) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108) ... 7 more Caused by: java.lang.Exception: 查询失败,查询页码【6】大于总总页【2】!! at com.learn.mybatis.plugin.PagingPlugin.checkPage(PagingPlugin.java:168) at com.learn.mybatis.plugin.PagingPlugin.intercept(PagingPlugin.java:99) at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:60) ... 15 more
说明页码超过了最大的页数,注意这里的因为有了这句:pageParams3.setCleanOrderBy(false);所以从日志可以看出,和上次不太一样,插件没有帮我们去掉了影响性能的order by语句。这些都是可以通过参数由你来控制的。当然使用默认的参数也是可以的。
以上是关于MyBatis分页插件:比PageHelper都要简单,使用很灵活,目前支持Oracle和MySQL的主要内容,如果未能解决你的问题,请参考以下文章