笔者写这篇博客是因为近期遇到的关于两者之间的分页代码差距,其实之前也遇见过但是没有去整理这篇博客,但由于还是被困扰了小一会儿时间,所以还是需要**加深记忆**。其实会看前后端传参解决这个问题很快、不麻烦。关于这两个框架的分页代码问题主要就是在业务层和MyBatis的SQL问题。**注意:**这里我不展示前端接口,需要知道的是前端会传给后端**当前页(page)**以及**每页条数(size)**。后端根据两个参数去实现**分页(limit)。**这里最容易踩坑的一个点:在MyBatis的分页中,(当前页需要 - 1) * size传入#{page},而在MyBatis-Plus中的new Page(page,size),则不需要去做运算,在Plus已经做好了这一点。详情请看下面代码块。
MyBatis
那么关于MyBatis这个半ORM框架来说,SQL还是需要自己去写在xml中的或者是在注解上实现。这里我就采用xml种实现的方式去实现啦。为了减少代码量(真实开发不能这样的哈),我就将所有业务代码都放到控制层中。
引入依赖
<!-- MyBatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- JDBC驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!-- MyBatis的分页插件PageHelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
修改yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/table?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapper/*.xml # 对应xml的位置
type-aliases-package: com.chf.entity # 对应namespace的实体类包名
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志类型
map-underscore-to-camel-case: true # 字段与属性的驼峰规则
# MyBatis的分页插件 这是最重要的
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countsql
业务代码
@RestController
@RequestMapping("/emp")
public class EmpController{
//@Autowired
//private EmpService empService;
// 为了博客简化代码量 所以直接调用Dao层接口
@Autowired
private EmpMapper empMapper;
/**
* 分页查询
* @param pageNum 当前页数
* @param pageSize 每页条数
* @return
*/
@GetMapping("/findAll/{page}/{size}")
public R findAll(@PathVariable("page") Integer pageNum, @PathVariable("size") Integer pageSize){
Integer page = (pageNum - 1) * pageSize;
List<Emp> empList = empMapper.selectByPage(page, pageSize);
PageInfo<Emp> pageInfo = new PageInfo<>(empList);
pageInfo.setTotal(empMapper.selectCount());
return R.ok(pageInfo);
}
}
@Mapper
public class EmpMapper{
List<Emp> selectByPage(@Param("page") Integer pageNum,
@Param("size") Integer pageSize);
Integer selectCount();
}
SQL
<!--返回前端的分页信息 这里的数据表字段名有点不规范-->
<select id="selectByPage" resultType="Emp">
SELECT
id,name,sex,idcard,phonenum,depart
FROM
emp
LIMIT
#{page},#{size}
</select>
<!--返回前端的总记录条数-->
<select id="selectCount" resultType="java.lang.Integer">
SELECT
count(*)
FROM
emp
</select>
测试返回数据
**返回的分页信息是在data中的list**
MyBatis-Plus
引入依赖
<!--整合MyBatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.3</version>
</dependency>
-->
<!--
这里需要注意哈:如果使用了MyBatis-Plus的话就不能引入MyBatis依赖 否则会报错
可能也不是因为两个不能互存 只是版本之间产生了依赖 报错原因如下图所示
-->
<!--数据源配置-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!--MyBatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<!--JDBC-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
修改yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/table?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:mapper/*.xml # 对应xml的位置
type-aliases-package: com.chf.entity # 对应namespace的实体类包名
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志类型
map-underscore-to-camel-case: true # 字段与属性的驼峰规则
业务代码
@RestController
@RequestMapping("/emp")
public class EmpController{
@Autowired
private EmpService empService;
/**
* 分页查询
* @param pageNum 当前页数
* @param pageSize 每页条数
* @return
*/
@GetMapping("/findAll/{page}/{size}")
public R findAll(@PathVariable("page") Integer pageNum, @PathVariable("size") Integer pageSize){
Page<Emp> page = new Page<>(pageNum, pageSize);
// 此方法还可以传入第二个参数:QueryWrapper条件构造器
// 用于增添一些查询条件用的 这里就不做展示了
empService.page(page);
// 如果是调用数据访问层的就是selectPage()方法即以下语句
// mapper.selectPage(page, QueryWrapper);
return R.ok(page);
}
}
public interface EmpService extends IService<Emp> {
}
@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
}
@Mapper
public interface EmpMapper extends BaseMapper<Emp> {
}
测试返回数据
可以看到在MyBatis-Plus返回前端的参数中使用**records封装分页信息**。看到这里以为结束了吗?仔细看total(总条数)会发现怎么会是0?还有pages(总页数)也是0,学过MyBatis-Plus应该都知道为了完成分页所以还需要配置分页插件才可以实现真正的分页。所以需要再添加一个配置类,代码如下:
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
添加好分页插件再次查询接口就会看到总条数和总页数都是真实的数值了。
PageHelper
其实MyBatis底层就是调用Github的PageHelper插件工具。但是如果直接使用Github的分页的话会更加简便,**但是有一个坑**。只需要将上面MyBatis的业务代码改为以下代码块。相比而言,代码行数一致。多了一句PageMethod.startPage()、少了一句设置总条数的语句。
业务代码
@RestController
@RequestMapping("/emp")
public class EmpController{
//@Autowired
//private EmpService empService;
// 为了博客简化代码量 所以直接调用Dao层接口
@Autowired
private EmpMapper empMapper;
/**
* 分页查询
* @param pageNum 当前页数
* @param pageSize 每页条数
* @return
*/
@GetMapping("/findAll/{page}/{size}")
public R findAll(@PathVariable("page") Integer pageNum, @PathVariable("size") Integer pageSize){
// 由于PageHelper继承PageMethod但未重写方法 所以写成下面的语句
Page<Object> page = PageMethod.startPage(pageNum, pageSize);
// 获取查询出的列表
List<Emp> empList = empMapper.selectByPage(page, pageSize);
PageInfo<Emp> pageInfo = new PageInfo<>(empList);
// pageInfo.setTotal(empMapper.selectCount());
// 此时page已经有列表信息、与总条数了 无需再一条SQL查询总条数
return R.ok(page);
}
}
踩坑
这里的踩坑处就是**Page对象一定要在想要查询的列表前先初始出来,否则过滤无效**。这样子Paga中的列表信息才是我们想要的查询出来的信息。比如说:业务中我需要查询A、B列表进行拼接啥的。主要是为了返回A列表的数据,所以只有以下两种组合方式(大于号表示业务代码中靠前出现):
Page > AList > BList。BList > Page > AList。
关于这个问题。我很好奇的追入源码中阅读。在PageMethod的startPage()中对Page对象进行设置。并且多了一步判断当前线程(ThreadLocal)是否存在Page对象。不存在则创建,存在则直接分页。至于源码追踪请参考这篇文章:Mybatis第三方PageHelper插件分页原理-腾讯云开发者社区-腾讯云
public abstract class PageMethod {
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page<E> page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
// 判断当前线程中是否存在Page对象(执行排序Order by之后)
Page<E> oldPage = getLocalPage(); // (Page)LOCAL_PAGE.get()
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page); // LOCAL_PAGE.set(page);
return page;
}
}
本文转载自: https://blog.csdn.net/m0_65563175/article/details/130563322
版权归原作者 字节尚未跳动 所有, 如有侵权,请联系我们删除。
版权归原作者 字节尚未跳动 所有, 如有侵权,请联系我们删除。