【问题标题】:How to do Pagination with mybatis?如何用mybatis进行分页?
【发布时间】:2013-07-04 21:08:24
【问题描述】:

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

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

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

谷歌搜索我发现了以下方法来完成这项任务:

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

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

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

谁能告诉我,

  1. 哪种方法更好地实现分页?
  2. mybatis 是否支持一种比仅仅依赖上述 SQL 查询(如休眠条件 API)更好的方式来实现分页。

高度赞赏任何输入。 谢谢。

【问题讨论】:

    标签: mysql hibernate pagination mybatis


    【解决方案1】:

    如果您使用的是 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>
    
    

    注意 - &lt;sql&gt; 块(连同 &lt;include refid=" ... " &gt;)在这里非常有用,可确保您的 countselect 查询对齐。此外,在排序时,我们使用条件,例如&lt;if test="order.property == 'projectId'"&gt;project_id ${order.direction}&lt;/if&gt; 映射到列(并停止 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);
        }
    
    }
    

    【讨论】:

      【解决方案2】:

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

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

      这在 the link Volodymyr posted 中几乎是事后提及的,在 Using Mappers 标题下,并且可以更加强调:

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

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

      【讨论】:

      • Rowbound是DB层分页还是ORM层分页?
      【解决方案3】:

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

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

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

      解决办法是使用物理分页

      • 你可以通过mybatisinterceptor做你自己的方式
      • 或使用别人预先制作的plugins

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          我自己在 sql 查询中使用您的第二个选项与 LIMIT。

          但是有许多方法支持使用 RowBounds 类进行分页。 这在 mybatis 文档here中有很好的描述

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

          【讨论】:

            猜你喜欢
            • 2011-08-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-02-03
            • 2012-11-10
            • 1970-01-01
            • 2011-06-17
            • 2018-03-04
            相关资源
            最近更新 更多