@Mapper
public interface UserMapper {
List<User> selectByPage(@Param("offset") Integer offset,
@Param("pageSize") Integer pageSize);
Long selectTotalCount();
}
XML 映射文件
xml复制代码
<!-- 分页查询 -->
<select id="selectByPage" resultType="User">
SELECT * FROM user
ORDER BY id DESC
LIMIT #{offset}, #{pageSize}
</select>
<!-- 总记录数 -->
<select id="selectTotalCount" resultType="java.lang.Long">
SELECT COUNT(*) FROM user
</select>
Service 层
java复制代码
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public PageResult<User> getUsers(PageParam param) {
List<User> users = userMapper.selectByPage(param.getOffset(), param.getPageSize());
Long total = userMapper.selectTotalCount();
return PageResult.<User>builder()
.list(users)
.total(total)
.pageNum(param.getPageNum())
.pageSize(param.getPageSize())
.build();
}
}
方案 2:PageHelper 插件(推荐)
配置插件
java复制代码
@Configuration
public class PageHelperConfig {
@Bean
public PageInterceptor pageInterceptor() {
PageInterceptor pageInterceptor = new PageInterceptor();
Properties props = new Properties();
props.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(props);
return pageInterceptor;
}
}
Service 层使用
java复制代码
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getUsersByPage(PageParam param) {
PageHelper.startPage(param.getPageNum(), param.getPageSize());
List<User> users = userMapper.selectAll(); // 无需手动拼接分页参数
return new PageInfo<>(users);
}
}
Mapper 接口
java复制代码
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user")
List<User> selectAll();
}
方案 3:MyBatis-Plus 分页插件(推荐)
配置插件
java复制代码
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
Service 层使用
java复制代码
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public Page<User> getUsersByPage(PageParam param) {
Page<User> page = new Page<>(param.getPageNum(), param.getPageSize());
return userMapper.selectPage(page, null);
}
}
public class CursorParam {
private Integer pageSize = 10; // 每页数量
private Long lastCursor; // 上一页最后记录的游标值
// Getter & Setter
}
2. 响应参数类
java复制代码
public class CursorResult<T> {
private List<T> list; // 当前页数据
private Boolean hasNext; // 是否有下一页
private Long nextCursor; // 下一页起始游标
// Getter & Setter
}
3. Mapper 接口
java复制代码
@Mapper
public interface UserMapper {
List<User> selectByCursor(@Param("cursor") Long cursor,
@Param("pageSize") Integer pageSize);
}
4. XML 映射文件
xml复制代码
<select id="selectByCursor" resultType="User">
SELECT * FROM user
<where>
<if test="cursor != null">
id < #{cursor}
</if>
</where>
ORDER BY id DESC
LIMIT #{pageSize}
</select>
5. Service 层逻辑
java复制代码
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public CursorResult<User> getUsersByCursor(CursorParam param) {
// 实际查询 pageSize + 1 条用于判断是否有下一页
List<User> users = userMapper.selectByCursor(param.getLastCursor(), param.getPageSize() + 1);
CursorResult<User> result = new CursorResult<>();
boolean hasNext = users.size() > param.getPageSize();
if (hasNext) {
result.setList(users.subList(0, param.getPageSize()));
result.setNextCursor(users.get(param.getPageSize() - 1).getId());
} else {
result.setList(users);
result.setNextCursor(null);
}
result.setHasNext(hasNext);
return result;
}
}
四、分页插件对比
插件/方案
优点
缺点
适用场景
手动分页
完全控制 SQL
代码冗余,维护成本高
简单项目、特殊分页需求
PageHelper
零侵入,简单易用
依赖特定语法(PageHelper.startPage())
传统 MyBatis 项目
MyBatis-Plus
深度整合,支持 Lambda 表达式
需继承 BaseMapper
MyBatis-Plus 项目
游标分页
高性能,无 OFFSET
无法跳页
移动端列表、实时数据流
五、注意事项
索引优化 :确保排序字段(如 id)有索引
安全限制 :限制最大 pageSize(建议 ≤ 100)
数据一致性:分页期间数据变化可能导致结果差异
参数校验 :校验 pageNum ≥1,pageSize ≥1
六、扩展建议
统一分页响应格式:
java复制代码
public class R<T> {
private Integer code;
private String msg;
private T data;
private PageInfo page; // 分页元数据
}
动态排序支持:
xml复制代码
<select id="selectByPage" resultType="User">
SELECT * FROM user
ORDER BY ${orderBy} ${orderDir}
LIMIT #{offset}, #{pageSize}
</select>
Redis 缓存优化:
java复制代码
// 缓存总记录数
public Long getTotalCount() {
String cacheKey = "user:total";
Long total = redisTemplate.opsForValue().get(cacheKey);
if (total == null) {
total = userMapper.selectTotalCount();
redisTemplate.opsForValue().set(cacheKey, total, 5, TimeUnit.MINUTES);
}
return total;
}