如何用mybatis进行分页?

时间:2013-07-07 10:26:57

标签: mysql hibernate pagination mybatis

我目前正在开发一个电子商务应用程序,我必须使用搜索功能显示可用产品列表。

与每次搜索一样,我必须在这里实施分页。

我使用mybatis作为我的ORM工具,使用mysql作为底层数据库。

在谷歌上搜索我找到了以下方法来完成这项任务:

  1. 客户端分页 :在这里,我将不得不从一个笔划中匹配搜索条件的数据库中获取所有结果,并在我的代码级别处理分页(可能是最终代码)。

  2. 服务器端分页: 使用mysql,我可以使用Limit和结果集的偏移量来构造一个类似的查询: SELECT * FROM sampletable WHERE condition1>1 AND condition2>2 LIMIT 0,20

  3. 在这里,每次用户在搜索结果中导航时选择新页面时,我都必须传递偏移和限制计数。

    任何人都可以说,

    1. 哪种方式更好的实现分页?
    2. mybatis支持更好的实现分页的方法,而不仅仅依赖于上面的SQL查询(比如hibernate标准API)。
    3. 任何输入都是高度适应的。 谢谢。

5 个答案:

答案 0 :(得分:7)

我自己在sql查询中使用LIMIT的第二个opion。

但是有一系列方法支持使用RowBounds类进行分页。 这在mybatis文档here

中有详细描述

注意使用正确的结果集类型。

答案 1 :(得分:5)

如果您正在使用Mappers(比使用原始SqlSessions容易得多),应用限制的最简单方法是在映射函数的参数列表中添加RowBounds参数,例如:

// without limit
List<Foo> selectFooByExample(FooExample ex);

// with limit
List<Foo> selectFooByExample(FooExample ex, RowBounds rb);

在使用Mappers标题的the link Volodymyr posted中,这几乎是一个事后的想法,并且可以使用更多的重点:

  

您还可以将RowBounds实例传递给方法以限制查询结果。

请注意,对RowBounds的支持可能因数据库而异。 Mybatis文档暗示Mybatis将负责使用适当的查询。但是,至少对于Oracle来说,这可以通过对数据库进行非常低效的重复调用来处理。

答案 2 :(得分:3)

分页有两种类型,物理和逻辑

  • 逻辑意味着首先检索所有数据然后在内存中对它们进行排序
  • 物理意味着数据库级子集选择

默认的mybatis分页是合乎逻辑的...因此当您选择一个大型数据库,例如100GB的blob时,rowbound方法仍然会非常慢

解决方案是使用物理分页

  • 你可以通过mybatis interceptor
  • 按自己的方式行事
  • 或使用其他人预先制作的plugins

答案 3 :(得分:2)

如果您正在使用Spring MyBatis,则可以使用2个MyBatis查询以及有用的Spring PagePageable接口来手动实现分页。

您创建了更高级别的DAO界面,例如UploadDao

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface UploadDao {

    Page<Upload> search(UploadSearch uploadSearch, Pageable pageable);

}

...,其中Upload映射到upload表,而UploadSearch是参数POJO,例如

@Data  // lombok
public class UploadSearch {

    private Long userId;
    private Long projectId;
    ... 

}

UploadDao的实现(将注入MyBatis UploadMapper映射器)如下:

public class DefaultUploadDao implements UploadDao {

    @Autowired
    private UploadMapper uploadMapper;

    public Page<Upload> searchUploads(UploadSearch uploadSearch, Pageable pageable) {
        List<Upload> content = uploadMapper.searchUploads(uploadSearch, pageable);
        Long total = uploadMapper.countUploads(uploadSearch);
        return new PageImpl<>(content, pageable, total);
    }

}

DAO实现调用UploadMapper的2个方法。这些是:

  1. UploadMapper.searchUploads-根据搜索参数(UploadSearch)和Pageable参数(包含偏移量/限制等)返回结果页面。
  2. UploadMapper.countUploads-再次基于搜索参数UploadSearch返回总数。注意-这里不需要Pageable参数,因为我们只是确定搜索参数过滤到的总行,而不关心页码/偏移量等。

注入的UploadMapper界面看起来像...

@Mapper
public interface UploadMapper {

    List<Upload> searchUploads(
        @Param("search") UploadSearch search,
        @Param("pageable") Pageable pageable);

    long countUploads(
        @Param("search") UploadSearch search);

}

...以及包含动态SQL的映射器XML文件,例如upload_mapper.xml包含...

<mapper namespace="com.yourproduct.UploadMapper">

    <select id="searchUploads" resultType="com.yourproduct.Upload">
        select u.*
          from upload u
         <include refid="queryAndCountWhereStatement"/>
         <if test="pageable.sort.sorted">
             <trim prefix="order by">
                 <foreach item="order" index="i" collection="pageable.sort" separator=", ">
                     <if test="order.property == 'id'">id ${order.direction}</if>
                     <if test="order.property == 'projectId'">project_id ${order.direction}</if>
                 </foreach>
             </trim>
         </if>
        <if test="pageable.paged">
            limit #{pageable.offset}, #{pageable.pageSize}  
        </if>
        <!-- NOTE: PostgreSQL has a slightly different syntax to MySQL i.e. 
             limit #{pageable.pageSize} offset #{pageable.offset} 
        -->
    </select>

    <select id="countUploads" resultType="long">
        select count(1)
         from upload u
        <include refid="queryAndCountWhereStatement"/>
    </select>

    <sql id="queryAndCountWhereStatement">
        <where>
            <if test="search != null">
                <if test="search.userId != null"> and u.user_id = #{search.userId}</if>
                <if test="search.productId != null"> and u.product_id = #{search.productId}</if>
                ...
            </if>
        </where>
    </sql>
</mapper>

  

注意-<sql>块(以及<include refid=" ... " >)在这里非常有用,可确保您的countselect查询对齐。此外,在排序时,我们使用条件,例如<if test="order.property == 'projectId'">project_id ${order.direction}</if>映射到列(并停止SQL注入)。 ${order.direction}是安全的,因为Spring Direction类是enum

UploadDao然后可以从例如一个Spring控制器:

@RestController("/upload")
public UploadController {

    @Autowired
    private UploadDao uploadDao;  // Likely you'll have a service instead (which injects DAO) - here for brevity

    @GetMapping
    public Page<Upload>search (@RequestBody UploadSearch search, Pageable pageable) {
        return uploadDao.search(search, pageable);
    }

}

答案 4 :(得分:0)

如果您使用的是MyBatis Generator,您可能需要尝试官方网站上的Row Bounds插件:org.mybatis.generator.plugins.RowBoundsPlugin。这个插件将添加一个新版本 接受RowBounds参数的selectByExample方法。