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

新睿云服务器60天免费使用,快来体验!>>>   

写了一个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
粉丝 68
博文 2
码字总数 1543
作品 2
评论 (108)
miemiedev

引用来自“_笔记本_”的评论

插件非常好用
另外请问,下一页的时候每次都会count吗?
这个你可以自己控制,不想count可以把containsTotalCount设成false
Biker
请问下 这个parameterType支持自定义类型entity的吗 还是只支持map,我试了下自定义类型bean参数老是报错
miemiedev

引用来自“Biker”的评论

请问下 这个parameterType支持自定义类型entity的吗 还是只支持map,我试了下自定义类型bean参数老是报错
parameterType一般来说不用如输入,如果你指定了类型就得自己拆解它。你说的问题这个插件并不控制,都是mybatis的行为。
Biker

引用来自“Biker”的评论

请问下 这个parameterType支持自定义类型entity的吗 还是只支持map,我试了下自定义类型bean参数老是报错

引用来自“miemiedev”的评论

parameterType一般来说不用如输入,如果你指定了类型就得自己拆解它。你说的问题这个插件并不控制,都是mybatis的行为。
例如 没用插件的时候接口方法:queryUser(User userCon),在xml里select配置的parameterType为“user” 能正常查询。 使用插件后,修改接口方法为queryUser(User userCon,PageBounds pageBounds),查询时提示类型转换出错Ex:User cant cast to String。 但是如果将修改后的接口参数userCon改为自己组装的Map,而非bean类,又能正常查询。 这是为啥呢?
miemiedev

引用来自“Biker”的评论

请问下 这个parameterType支持自定义类型entity的吗 还是只支持map,我试了下自定义类型bean参数老是报错

引用来自“miemiedev”的评论

parameterType一般来说不用如输入,如果你指定了类型就得自己拆解它。你说的问题这个插件并不控制,都是mybatis的行为。

引用来自“Biker”的评论

例如 没用插件的时候接口方法:queryUser(User userCon),在xml里select配置的parameterType为“user” 能正常查询。 使用插件后,修改接口方法为queryUser(User userCon,PageBounds pageBounds),查询时提示类型转换出错Ex:User cant cast to String。 但是如果将修改后的接口参数userCon改为自己组装的Map,而非bean类,又能正常查询。 这是为啥呢?
哦我明白你的意思了,现在的版本由于分页时加了参数所以用POJO传递时会有两个或者以上的额外参数,直接引用参数名称时mybatis就无法自行拆解了,你可以使用_parameter或者使用接口的@Param来让它运作起来
ldl123292

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

没有把结果集在封装下 totalPage里面么?
没有看到
涵冰
用了分页插件,现在发现有个问题解决不了, 列表中如果新增或删除数据刷新查询的时候, 列表中的数据条数是对的, 但分页总数没变化, 导致页码生成有误, 要等一会再来操作或F5连续刷新多次分页总数才会正确,请帮忙解答一下,十分感谢!
miemiedev

引用来自“涵冰”的评论

用了分页插件,现在发现有个问题解决不了, 列表中如果新增或删除数据刷新查询的时候, 列表中的数据条数是对的, 但分页总数没变化, 导致页码生成有误, 要等一会再来操作或F5连续刷新多次分页总数才会正确,请帮忙解答一下,十分感谢!
有没有刷新cache?
涵冰

引用来自“涵冰”的评论

用了分页插件,现在发现有个问题解决不了, 列表中如果新增或删除数据刷新查询的时候, 列表中的数据条数是对的, 但分页总数没变化, 导致页码生成有误, 要等一会再来操作或F5连续刷新多次分页总数才会正确,请帮忙解答一下,十分感谢!

引用来自“miemiedev”的评论

有没有刷新cache?
是指mybatis的缓存吗? 我看了你的分页总数查询, 是有判断缓存, 我将usecache是设置false了, 每次都有执行SQLHelp.getCount, 列表的数据会有变化的, 就是总页数不会变
尹逸韶
你好,用你的插件返回的对象都是ArrayList。并且排序不生效,如下:
调用:
String sortString = "status.asc";
PageBounds pageBounds = new PageBounds(index, limit, Order.formString(sortString));
List list = userinfoDao.findPageByCondition(parameters, pageBounds);
return (PageList<FwapUserinfo>) list; //报错java.lang.ClassCastException,调试发现对象为ArrayList

DAO层:
List<T> findPageByCondition(Map<String, Object> parameters, PageBounds rb);
<select id="findPageByCondition" parameterType="map" resultMap="fwapUserinfoRM">
    select <include refid="userinfo_columns"/>
    from FWAP_USERINFO
    <where>
      1=1
      <if test="userAccount != null">
        and USER_ACCOUNT like '%${userAccount}%'
      </if>
      <if test="orgnumber != null">
        and ORGNUMBER=#{orgnumber}
      </if>
      <if test="workdate != null">
        and WORKDATE=#{workdate}
      </if>
      <if test="updatetime != null">
        and UPDATETIME=#{updatetime}
      </if>
    </where>
  </select>
尹逸韶

引用来自“尹逸韶”的评论

你好,用你的插件返回的对象都是ArrayList。并且排序不生效,如下:
调用:
String sortString = "status.asc";
PageBounds pageBounds = new PageBounds(index, limit, Order.formString(sortString));
List list = userinfoDao.findPageByCondition(parameters, pageBounds);
return (PageList<FwapUserinfo>) list; //报错java.lang.ClassCastException,调试发现对象为ArrayList

DAO层:
List<T> findPageByCondition(Map<String, Object> parameters, PageBounds rb);
<select id="findPageByCondition" parameterType="map" resultMap="fwapUserinfoRM">
    select <include refid="userinfo_columns"/>
    from FWAP_USERINFO
    <where>
      1=1
      <if test="userAccount != null">
        and USER_ACCOUNT like '%${userAccount}%'
      </if>
      <if test="orgnumber != null">
        and ORGNUMBER=#{orgnumber}
      </if>
      <if test="workdate != null">
        and WORKDATE=#{workdate}
      </if>
      <if test="updatetime != null">
        and UPDATETIME=#{updatetime}
      </if>
    </where>
  </select>

DAO层用MyBatisRepository注释@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyBatisRepository { }
尹逸韶

引用来自“尹逸韶”的评论

你好,用你的插件返回的对象都是ArrayList。并且排序不生效,如下:
调用:
String sortString = "status.asc";
PageBounds pageBounds = new PageBounds(index, limit, Order.formString(sortString));
List list = userinfoDao.findPageByCondition(parameters, pageBounds);
return (PageList<FwapUserinfo>) list; //报错java.lang.ClassCastException,调试发现对象为ArrayList

DAO层:
List<T> findPageByCondition(Map<String, Object> parameters, PageBounds rb);
<select id="findPageByCondition" parameterType="map" resultMap="fwapUserinfoRM">
    select <include refid="userinfo_columns"/>
    from FWAP_USERINFO
    <where>
      1=1
      <if test="userAccount != null">
        and USER_ACCOUNT like '%${userAccount}%'
      </if>
      <if test="orgnumber != null">
        and ORGNUMBER=#{orgnumber}
      </if>
      <if test="workdate != null">
        and WORKDATE=#{workdate}
      </if>
      <if test="updatetime != null">
        and UPDATETIME=#{updatetime}
      </if>
    </where>
  </select>

调试发现没有进入到OffsetLimitInterceptor拦截器
撕花焚玉
果然 !! 只要配置 <mvc:annotation-driven /> 就不行 返回序列化字符串就不行 ,楼主你知道为什么吗??
miemiedev

引用来自“撕花焚玉”的评论

果然 !! 只要配置 <mvc:annotation-driven /> 就不行 返回序列化字符串就不行 ,楼主你知道为什么吗??
能说的更详细点吗
撕花焚玉

引用来自“撕花焚玉”的评论

果然 !! 只要配置 <mvc:annotation-driven /> 就不行 返回序列化字符串就不行 ,楼主你知道为什么吗??

引用来自“miemiedev”的评论

能说的更详细点吗
我返回json数据,正常的是[{"id":106,"username":"6","age":6,"password":"6"},{"id":107,"username":"6","age":6,"password":"6"}] 这样的数据,但是现在我想序列化他,返回序列化后的json,根据楼主的配置,页面一直返回的还是没序列之前的,我看去年一个人回复 把 去掉就好了,,,我就尝试的去掉了,果然好了 返回的是序列化后的数据了,我还没搞清楚原因。。。。
撕花焚玉
两个会重复吗
<mvc:annotation -driven /> <mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
.......................
</mvc:annotation-driven>
miemiedev

引用来自“撕花焚玉”的评论

两个会重复吗
<mvc:annotation -driven /> <mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
.......................
</mvc:annotation-driven>
这个转换器就是在annotation-driven配置的,你得保证当前用的这个annotation-driven是包含转换器的就行。默认只写是不包含转换器的。
绿妖精
请问这是哪里的插件啊?是开源的吗
mascothaojun
您好,我这里按照你的配置信息,做了一次demo,代码如下

    int page = 1; //页号
    int pageSize = 20; //每页数据条数
    String sortString = "phase_id";//如果你想排序的话逗号分隔可以排序多列
    PageBounds pageBounds = new PageBounds(page, pageSize , Order.formString(sortString),true);
    pageBounds.setLimit(1);
    pageBounds.setLimit(1);
    List<PhaseE> list = phaseApi.queryPageList(entity, pageBounds);
    PageList pageList = (PageList)list;
//    System.out.println("totalCount: " + pageList.getPaginator().getTotalCount());
    System.out.println("size: "+list.size());
    System.out.println("pageListSize: "+pageList.size());
//    System.out.println("totalCount: " + pageList.getPaginator().getTotalCount());
    for(PhaseE p : list){
      System.out.println("phaseName: "+p.getPhaseName());
    }
打印的sql没追加rownnum,只做了order by的追加,是什么原因造成,所有的配置都按照你贴的东西做了,是什么原因造成的?
doa的代码
public <V extends T> List<V> selectPageList(T query, PageBounds pageBounds) {
    return sqlSessionTemplate.selectList(getSqlName(SqlId.SQL_SELECT), query, pageBounds);
}
mascothaojun
也就是只做了查询的功能,没做分页的查询!应该怎么解决?
×
miemiedev
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: