Mybatis分页和Spring的集成
Mybatis分页和Spring的集成
miemiedev 发表于5年前
Mybatis分页和Spring的集成
  • 发表于 5年前
  • 阅读 47737
  • 收藏 114
  • 点赞 22
  • 评论 108

移动开发云端新模式探索实践 >>>   

写了一个Mybatis分页控件,在这记录一下使用方式。

在Maven中加入依赖:

<dependencies>
  ...
    <dependency>
        <groupId>com.github.miemiedev</groupId>
        <artifactId>mybatis-paginator</artifactId>
        <version>1.2.17</version>
    </dependency>
 ...
</dependencies>


Mybatis配置文件添加分页插件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
        "http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
    <plugins>
        <plugin interceptor="com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor">
            <property name="dialectClass" value="com.github.miemiedev.mybatis.paginator.dialect.OracleDialect"/>
        </plugin>
    </plugins>
</configuration>


创建一个查询,内容可以是任何Mybatis表达式,包括foreach和if等:

<select id="findByCity" resultType="map">
    select * from TEST_USER where city = #{city};
</select>


Dao中的方法或许是这样(用接口也是类似):

public List findByCity(String city, PageBounds pageBounds){

    Map<String, Object> params = new HashMap<String, Object>();
    params.put("city",city);

    return getSqlSession().selectList("db.table.user.findByCity", params, pageBounds);
}


调用方式(分页加多列排序):

int page = 1; //页号
int pageSize = 20; //每页数据条数
String sortString = "age.asc,gender.desc";//如果你想排序的话逗号分隔可以排序多列
PageBounds pageBounds = new PageBounds(page, pageSize , Order.formString(sortString));
List list = findByCity("BeiJing",pageBounds);

//获得结果集条总数
PageList pageList = (PageList)list;
System.out.println("totalCount: " + pageList.getPaginator().getTotalCount());

PageList类是继承于ArrayList的,这样Dao中就不用为了专门分页再多写一个方法。

使用PageBounds这个对象来控制结果的输出,常用的使用方式一般都可以通过构造函数来配置。

new PageBounds();//默认构造函数不提供分页,返回ArrayList
new PageBounds(int limit);//取TOPN操作,返回ArrayList
new PageBounds(Order... order);//只排序不分页,返回ArrayList

new PageBounds(int page, int limit);//默认分页,返回PageList
new PageBounds(int page, int limit, Order... order);//分页加排序,返回PageList
new PageBounds(int page, int limit, List<Order> orders, boolean containsTotalCount);//使用containsTotalCount来决定查不查询totalCount,即返回ArrayList还是PageList


========================================= 

如果用的是Spring MVC的话可以把JSON的配置写成这样:

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
            <constructor-arg value="UTF-8" />        
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="com.github.miemiedev.mybatis.paginator.jackson2.PageListJsonMapper" />
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>


那么在Controller就可以这样用了:

@ResponseBody
@RequestMapping(value = "/findByCity.json")
public List findByCity(@RequestParam String city,
                 @RequestParam(required = false,defaultValue = "1") int page,
                 @RequestParam(required = false,defaultValue = "30") int limit,
                 @RequestParam(required = false) String sort,
                 @RequestParam(required = false) String dir) {
    return userService.findByCity(city, new PageBounds(page, limit, Order.create(sort,dir)));
}


然后序列化后的JSON字符串就会变成这样的:

{
    "items":[
        {"NAME":"xiaoma","AGE":30,"GENDER":1,"ID":3,"CITY":"BeiJing"},
        {"NAME":"xiaoli","AGE":30,"SCORE":85,"GENDER":1,"ID":1,"CITY":"BeiJing"},
        {"NAME":"xiaowang","AGE":30,"SCORE":92,"GENDER":0,"ID":2,"CITY":"BeiJing"},
        {"NAME":"xiaoshao","AGE":30,"SCORE":99,"GENDER":0,"ID":4,"CITY":"BeiJing"}
    ],
    "slider": [1, 2, 3, 4, 5, 6, 7],
    "hasPrePage": false,
    "startRow": 1,
    "offset": 0,
    "lastPage": false,
    "prePage": 1,
    "hasNextPage": true,
    "nextPage": 2,
    "endRow": 30,
    "totalCount": 40351,
    "firstPage": true,
    "totalPages": 1346,
    "limit": 30,
    "page": 1
}


=========================================

在SpringMVC中使用JSTL的话可以参考一下步骤(懒人用法)

在Spring配置文件中加入拦截器,或则参考拦截器实现定义自己的拦截器

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.github.miemiedev.mybatis.paginator.springmvc.PageListAttrHandlerInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

然后Controller方法可以这样写

@RequestMapping(value = "/userView.action")
public ModelAndView userView(@RequestParam String city,
                 @RequestParam(required = false,defaultValue = "1") int page,
                 @RequestParam(required = false,defaultValue = "30") int limit,
                 @RequestParam(required = false) String sort,
                 @RequestParam(required = false) String dir) {
    List users = userService.findByCity(city, new PageBounds(page, limit, Order.create(sort,dir)));
    return new ModelAndView("account/user","users", users);
}

JSP中就可以这样用了,拦截器会将PageList分拆添加Paginator属性,默认命名规则为"原属性名称"+"Paginator"

<table>
    <c:forEach items="${users}" var="user">
        <tr>
            <td>${user['ID']}</td>
            <td>${user['NAME']}</td>
            <td>${user['AGE']}</td>
        </tr>
    </c:forEach>
</table>
上一页: ${usersPaginator.prePage} 
当前页: ${usersPaginator.page} 
下一页: ${usersPaginator.nextPage} 
总页数: ${usersPaginator.totalPages} 
总条数: ${usersPaginator.totalCount} 
更多属性参考Paginator类提供的方法

=========================================

如果用如下方法设置pageBounds,当前这个查询就可以用两个线程同时查询list和totalCount

pageBounds.setAsyncTotalCount(true);

如果所有的分页查询都是用异步的方式查询list和totalCount,可以在插件配置加入asyncTotalCount属性

<plugin interceptor="com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor">
    <property name="dialectClass" value="com.github.miemiedev.mybatis.paginator.dialect.OracleDialect"/>
    <property name="asyncTotalCount" value="true"/>
</plugin>

但是你仍然可以用下面代码强制让这个查询不用异步

pageBounds.setAsyncTotalCount(false);

当然需要注意的是,只要你用到了异步查询,由于里面使用了线程池,所以在使用时就要加入清理监听器,以便在停止服务时关闭线程池。需要在web.xml中加入

<listener>
    <listener-class>com.github.miemiedev.mybatis.paginator.CleanupMybatisPaginatorListener</listener-class>
</listener>


完。

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
miemiedev
粉丝 69
博文 2
码字总数 1543
作品 2
评论 (108)
miemiedev

引用来自“飞天小色猫”的评论

没有把结果集在封装下 totalPage里面么?

谢谢你的反馈。
Paginator对象有一个getTotalPages方法可以获得,现在PageListJsonSerializer的JSON结果集中不包含TotalPages。如果你急用可以参照PageListJsonSerializer自己实现一个,我会在后面的更新中把TotalPages加进去。
sharefun2068
支持postpresql 数据库吗
miemiedev

引用来自“sharefun2068”的评论

支持postpresql 数据库吗

支持,你可以在com.github.miemiedev.mybatis.paginator.dialect这个包下找到你需要的方言
于磊leilei
那我要是没有 daomapper 实现类 就是没有daoImpl呢。。这个插件怎么用
miemiedev

引用来自“于磊leilei”的评论

那我要是没有 daomapper 实现类 就是没有daoImpl呢。。这个插件怎么用

配置和使用方式都是一样的
CraneH
请教,我用Mybatis官方的代码生成器生成了的代码,使用阁下的分页插件后分页是实现了,但是在增加查询条件后就查不到数据了,DAO接口的参数是pagerowbunds和生成的example,请教
CraneH
补充,原来是生成的方法like要自己加%,问题已解决,楼主的插件很好用!
developer_java
<property name="objectMapper">
<bean class="com.github.miemiedev.mybatis.paginator.jackson2.PageListJsonMapper" />
返回的json 时间格式怎么处理?
miemiedev

引用来自“developer_java”的评论

<property name="objectMapper">
<bean class="com.github.miemiedev.mybatis.paginator.jackson2.PageListJsonMapper" />
返回的json 时间格式怎么处理?

这里是默认的方法,你可以根据规则实现自己的类替换它
IToy
您好,问一下,当page = 1 时,返回的pageResult没有paginator,报空指针,后来做了对比,发现page > 1时,offset != 0 所以 if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {可以通过,当= 1时就不能通过了。我的Mapper接口和Servie接口里分页查询方法直接返回的是PageList, 我没有先得List再强转.
miemiedev

引用来自“IToy”的评论

您好,问一下,当page = 1 时,返回的pageResult没有paginator,报空指针,后来做了对比,发现page > 1时,offset != 0 所以 if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {可以通过,当= 1时就不能通过了。我的Mapper接口和Servie接口里分页查询方法直接返回的是PageList, 我没有先得List再强转.

请问你是不是用的PageBounds(int limit)这个构造函数来创建PageBounds的,里面有个叫containsTotalCount的参数,如果为false的话就不会构建paginator。

IToy

引用来自“无悔这一生”的评论

好了 我自己的问题!

请问一下,我也遇到这个问题了,您是怎么解决的呢

qqzhijun
79把以前的分页驱动强化了太多了,3q
六只
请问,PageListJsonMapper用这东西序列化的时候,Date的怎么指定格式?我格式化出来的都是毫秒数,1399906550000这种。objectMapper.setDateFormat(new SimpleDateFormat("yyyy-mm-MM"));这句貌似也不起作用。
miemiedev

引用来自“六只”的评论

请问,PageListJsonMapper用这东西序列化的时候,Date的怎么指定格式?我格式化出来的都是毫秒数,1399906550000这种。objectMapper.setDateFormat(new SimpleDateFormat("yyyy-mm-MM"));这句貌似也不起作用。
是这样,默认格式化出来是毫秒,你只需要使用js的new Date(毫秒)来构建日期就可以了。本着前端显示和后端解耦的思路并不建议把日期类型在后端格式化成具体的字符串。如果非要这样的话你具体参考这个文档 http://wiki.fasterxml.com/JacksonFAQDateHandling
miemiedev
很懒,不喜欢写文档,最近问的人有点多,也是因为懒,就加了点文档....
lijinqiu20

引用来自“无悔这一生”的评论

您好,我在PageList pageList = (PageList)list; 强制转换这里报错了,我有点不解,为啥这里来个强转,就能取到Paginator对象? 我debug了下,list就是一个普通的List对象,如何能变出一个Paginator对象?
lijinqiu20

引用来自“miemiedev”的评论

引用来自“唐文韬”的评论

您好,我在PageList pageList = (PageList)list; 强制转换这里报错了,我有点不解,为啥这里来个强转,就能取到Paginator对象? 我debug了下,list就是一个普通的List对象,如何能变出一个Paginator对象?

区别在于containsTotalCount参数是否为true。有时候只想取TopN就可以不查询记录总数了。
您好,我想请教下强转问题是怎么解决的
miemiedev

引用来自“miemiedev”的评论

引用来自“唐文韬”的评论

您好,我在PageList pageList = (PageList)list; 强制转换这里报错了,我有点不解,为啥这里来个强转,就能取到Paginator对象? 我debug了下,list就是一个普通的List对象,如何能变出一个Paginator对象?

区别在于containsTotalCount参数是否为true。有时候只想取TopN就可以不查询记录总数了。

引用来自“lijinqiu20”的评论

您好,我想请教下强转问题是怎么解决的
本质是在拦截器中判断参数,然后返回不同类的实例,他们都是实现List接口的。
_笔记本_
插件非常好用
另外请问,下一页的时候每次都会count吗?
×
miemiedev
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: