Mybatis分页插件PageHelper简单使用

Mybatis分页插件PageHelper简单使用

引言

对于使用Mybatis时,最头痛的就是写分页,需要先写一个查询count的select语句,然后再写一个真正分页查询的语句,当查询条件多了之后,会发现真不想花双倍的时间写count和select,

如下就是项目在没有使用分页插件的时候的语句

<!-- 根据查询条件获取查询获得的数据量 -->
<select id="size" parameterType="Map" resultType="Long">
    select count(*) from help_assist_student
    <where>
        <if test="stuId != null and stuId != ''">
            AND stu_id like
            CONCAT(CONCAT('%',
            #{stuId,jdbcType=VARCHAR}),'%')
        </if>
        <if test="name != null and name != ''">
            AND name like
            CONCAT(CONCAT('%',
            #{name,jdbcType=VARCHAR}),'%')
        </if>
        <if test="deptId != null">
            AND dept_id in
            <foreach item="item" index="index" collection="deptId" open="("
                separator="," close=")">
                #{item}
            </foreach>
        </if>
        <if test="bankName != null">
            AND bank_name in
            <foreach item="item" index="index" collection="bankName"
                open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
    </where>
</select>
<!-- 分页查询获取获取信息 -->
<select id="selectByPageAndSelections" parameterType="cn.edu.uestc.smgt.common.QueryBase"
    resultMap="BaseResultMap">
    select * from help_assist_student
    <where>
        <if test="parameters.stuId != null and parameters.stuId != ''">
            AND stu_id like
            CONCAT(CONCAT('%',
            #{parameters.stuId,jdbcType=VARCHAR}),'%')
        </if>
        <if test="parameters.name != null and parameters.name != ''">
            AND name like
            CONCAT(CONCAT('%',
            #{parameters.name,jdbcType=VARCHAR}),'%')
        </if>
        <if test="parameters.deptId != null">
            AND dept_id in
            <foreach item="item" index="index" collection="parameters.deptId"
                open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
        <if test="parameters.bankName != null">
            AND bank_name in
            <foreach item="item" index="index" collection="parameters.bankName"
                open="(" separator="," close=")">
                #{item}
            </foreach>
        </if>
    </where>
    order by dept_id,stu_id
    limit #{firstRow},#{pageSize}
</select>

1,配置分页插件

PageHelper的原理是基于拦截器实现的。拦截器的配置有两种方法,一种是在mybatis的配置文件中配置,一种是直接在spring的配置文件中进行;

官方xml配置

Config PageHelper
1. Using in mybatis-config.xml

<!-- In the configuration file, 
plugins location must meet the requirements as the following order:
properties?, settings?, 
typeAliases?, typeHandlers?, 
objectFactory?,objectWrapperFactory?, 
plugins?, 
environments?, databaseIdProvider?, mappers?
-->

<plugins>
	<plugin interceptor="com.github.pagehelper.PageInterceptor">
    	<!-- config params as the following -->
    <property name="param1" value="value1"/>
	</plugin>
</plugins> 
///
2. Using in Spring application.xml
config org.mybatis.spring.SqlSessionFactoryBean as following:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  	<!-- other configuration -->
 	 <property name="plugins">
	<array>
  		<bean class="com.github.pagehelper.PageInterceptor">
   				 <property name="properties">
    	  			<!-- config params as the following -->
    	 			 <value>
        				param1=value1
     		 		</value>
   		 		</property>
  		</bean>
 		</array>
	 </property>
</bean>
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
        <property name="helperDialect" value="mysql"/>
    </plugin>
</plugins>

2.编写mapper.xml接口配置文件

<select id="selectByPageAndSelections" resultMap="BaseResultMap">
        SELECT *
        FROM doc
        ORDER BY doc_abstract
    </select>

然后在Mapper.java中编写对应的接口

public List<Doc> selectByPageAndSelections();

3.分页实现
@Service

public class DocServiceImpl implements IDocService {

@Autowired
private DocMapper docMapper;

@Override
public PageInfo selectDocByPage1(int currentPage, int pageSize) {
//设置分页参数 当前页 + 每页的条目数
PageHelper.startPage(currentPage, pageSize);
//页面要显示的内容
List docs = docMapper.selectByPageAndSelections();
//将结果集 交给分页插件处理
PageInfo pageInfo = new PageInfo<>(docs);
return pageInfo;
}
}
4.不安全的分页
使用了PageHelper.startPage(currentPage, pageSize);

什么时候会导致不安全的分页?

PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。

只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。


  list = new ArrayList<Country>();
}

这种写法就能保证安全。
如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:
List<Country> list;
if(param1 != null){
    PageHelper.startPage(1, 10);
    try{
        list = countryMapper.selectAll();
    } finally {
       //查询所有手动清理ThreadLocal存储的分页参数
        PageHelper.clearPage();
    }
} else {
    list = new ArrayList<Country>();
}

PageHelper底层代码分析:
PageHelper(Mybatis - 通用分页拦截器)继承了PageMethod(基础分页方法);

同时实现了Dialect(数据库方言,针对不同数据库进行实现)

public class PageHelper extends PageMethod implements Dialect {
    private PageParams pageParams;
    private PageAutoDialect autoDialect;
  ......
}  

看一下,PageMethod

  1. 成员位置定义了开始分页的线程绑定;
    protected static final ThreadLocal LOCAL_PAGE = new ThreadLocal();

  2. 开始分页方法执行之前public static Page startPage(Object params) {}; LOCAL_PAGE 针对传入进来的Page参数进行绑定;只要后面跟着Mybatis查询方法,直接使用了此次的PageHelper产生的分页参数直接被使用;就不会出现分页"乱数据";

  3. 清除传入的参数;
    public static void clearPage() {

    LOCAL_PAGE.remove();
    

    }

/**
 * 基础分页方法
 *
 * @author liuzh
 */
public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();

    /**
     * 设置 Page 参数
     *
     * @param page
     */
    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

    /**
     * 获取 Page 参数
     *
     * @return
     */
    public static <T> Page<T> getLocalPage() {
        return LOCAL_PAGE.get();
    }

    /**
     * 移除本地变量
     */
    public static void clearPage() {
        LOCAL_PAGE.remove();
    }

    /**
     * 获取任意查询方法的count总数
     *
     * @param select
     * @return
     */
    public static long count(ISelect select) {
        Page<?> page = startPage(1, -1, true);
        select.doSelect();
        return page.getTotal();
    }

    /**
     * 开始分页
     *
     * @param params
     */
    public static <E> Page<E> startPage(Object params) {
        Page<E> page = PageObjectUtil.getPageFromObject(params, true);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);
        return page;
    }

    /**
     * 开始分页
     *
     * @param pageNum  页码
     * @param pageSize 每页显示数量
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize) {
        return startPage(pageNum, pageSize, true);
    }

    /**
     * 开始分页
     *
     * @param pageNum  页码
     * @param pageSize 每页显示数量
     * @param count    是否进行count查询
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count) {
        return startPage(pageNum, pageSize, count, null, null);
    }

    /**
     * 开始分页
     *
     * @param pageNum  页码
     * @param pageSize 每页显示数量
     * @param orderBy  排序
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize, String orderBy) {
        Page<E> page = startPage(pageNum, pageSize);
        page.setOrderBy(orderBy);
        return page;
    }

    /**
     * 开始分页
     *
     * @param pageNum      页码
     * @param pageSize     每页显示数量
     * @param count        是否进行count查询
     * @param reasonable   分页合理化,null时用默认配置
     * @param pageSizeZero true且pageSize=0时返回全部结果,false时分页,null时用默认配置
     */
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);
        return page;
    }

    /**
     * 开始分页
     *
     * @param offset 页码
     * @param limit  每页显示数量
     */
    public static <E> Page<E> offsetPage(int offset, int limit) {
        return offsetPage(offset, limit, true);
    }

    /**
     * 开始分页
     *
     * @param offset 页码
     * @param limit  每页显示数量
     * @param count  是否进行count查询
     */
    public static <E> Page<E> offsetPage(int offset, int limit, boolean count) {
        Page<E> page = new Page<E>(new int[]{offset, limit}, count);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);
        return page;
    }

    /**
     * 排序
     *
     * @param orderBy
     */
    public static void orderBy(String orderBy) {
        Page<?> page = getLocalPage();
        if (page != null) {
            page.setOrderBy(orderBy);
        } else {
            page = new Page();
            page.setOrderBy(orderBy);
            page.setOrderByOnly(true);
            setLocalPage(page);
        }
    }


}

欢迎评论,指正,互相学习;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值