如何用mybatis进行分页?
Posted
技术标签:
【中文标题】如何用mybatis进行分页?【英文标题】:How to do Pagination with mybatis? 【发布时间】:2013-07-04 21:08:24 【问题描述】:我目前正在开发一个电子商务应用程序,我必须在其中使用搜索功能显示可用产品列表。
与每次搜索一样,我必须在这里实现分页。
我使用 mybatis 作为我的 ORM 工具,使用 mysql 作为底层数据库。
谷歌搜索我发现了以下方法来完成这项任务:
客户端分页 :在这里,我将不得不一次性从数据库中获取与搜索条件匹配的所有结果,并在我的代码级别处理分页(可能是前端代码)。
服务器端分页:
使用 mysql,我可以使用结果集的 Limit 和偏移量来构造如下查询:
SELECT * FROM sampletable WHERE condition1>1 AND condition2>2 LIMIT 0,20
在这里,每次用户在搜索结果中导航时选择新页面时,我都必须传递偏移量和限制计数。
谁能告诉我,
-
哪种方法更好地实现分页?
mybatis 是否支持一种比仅仅依赖上述 SQL 查询(如休眠条件 API)更好的方式来实现分页。
高度赞赏任何输入。 谢谢。
【问题讨论】:
【参考方案1】:如果您使用的是 Spring MyBatis,您可以使用 2 个 MyBatis 查询和有用的 Spring Page
和 Pageable
接口手动实现分页。
您创建更高级别的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 个方法。它们是:
UploadMapper.searchUploads
- 根据搜索参数 (UploadSearch
) 和 Pageable
参数(包含偏移量/限制等)返回一页结果。
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=" ... " >
)在这里非常有用,可确保您的count
和select
查询对齐。此外,在排序时,我们使用条件,例如<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);
【讨论】:
【参考方案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中有很好的描述
注意使用正确的结果集类型。
【讨论】:
以上是关于如何用mybatis进行分页?的主要内容,如果未能解决你的问题,请参考以下文章