12

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

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

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

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

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

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

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

谁能告诉我,

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

任何输入都受到高度重视。谢谢 。

4

5 回答 5

11

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

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

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

于 2013-07-07T17:52:30.033 回答
10

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

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

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

这几乎是在Volodymyr 发布的链接中提到的,在 Using Mappers 标题下,并且可以使用更多的重点:

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

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

于 2014-07-31T22:19:17.263 回答
5

pagination has two types, physical and logical

  • logical means to retrieve all the data first then sort them in memory
  • physical means database level subset select

the default mybatis pagination is logical... thus when you select a massive database e.g 100GB of blobs, the rowbound method will still be very slow

the solution is to use the physical pagination

  • you can do your own way through the mybatis interceptor
  • or using plugins pre made by someone else
于 2016-05-18T11:00:35.897 回答
4

如果您使用的是 Spring MyBatis,您可以使用 2 个 MyBatis 查询和有用的 SpringPagePageable接口手动实现分页。

您创建一个更高级别的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 实现调用 2 个方法UploadMapper。这些是:

  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},因为 SpringDirection类是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);
    }

}
于 2019-11-18T15:33:58.017 回答
0

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

于 2014-06-10T17:44:42.083 回答