SpringBoot - 后端条件分页查询-MyBatis解决方案

25 篇文章 0 订阅
14 篇文章 0 订阅

最近用SpringBoot+MyBatis在做个人博客网站,遇了条件分页查询,现在把解决流程与源代码记录下来,便于后续参考。

0. pom依赖

这里把主要的依赖放进来,其他相关依赖请自行导入

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- pagehelper 分页的一个工具 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.5</version>
        </dependency>

1.数据表

在这里插入图片描述
数据表sql:

CREATE DATABASE /*!32312 IF NOT EXISTS*/`myblog` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `myblog`;

/*Table structure for table `articles` */

DROP TABLE IF EXISTS `articles`;

CREATE TABLE `articles` (
  `article_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `title` varchar(255) DEFAULT NULL COMMENT '文章标题',
  `name` varchar(255) DEFAULT NULL,
  `view_num` int(11) DEFAULT NULL COMMENT '浏览数',
  `comment_num` int(11) DEFAULT NULL COMMENT '评论数',
  `category_id` bigint(20) DEFAULT NULL COMMENT '文章分类ID',
  `create_time` datetime DEFAULT NULL COMMENT '创建日期',
  `like_num` int(11) DEFAULT '0',
  PRIMARY KEY (`article_id`),
  KEY `key_category_id` (`category_id`),
  KEY `key_userId` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8 COMMENT='文章表';

/*Data for the table `articles` */

insert  into `articles`(`article_id`,`user_id`,`title`,`name`,`view_num`,`comment_num`,`category_id`,`create_time`,`like_num`) values (1,0,'555','555.md',0,3,1,'2020-09-22 00:00:00',0),(83,1,'daidai','daidai.md',0,4,1,'2020-09-30 11:09:42',0),(84,1,'redis安装','redis安装.md',0,1,1,'2020-09-30 11:09:43',0),(85,1,'淘宝革命的启示录','淘宝革命的启示录.md',0,2,1,'2020-09-30 11:09:43',0),(98,1,'444','444.md',0,9,0,'2020-10-01 10:55:45',0);

2. 定义pojo

定义与数据表相对应的Article 类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Article implements Serializable {

    private Long articleId;
    private Long userId=1l ;
    private String title;
    private String name;
    private Integer viewNum=0 ;
    private Integer commentNum=0 ;
    private long categoryId ;
    private Timestamp createTime;
}

定义查询类

@Data
public class ArticleQuery extends Article {

    //排序字段
    public String sortView;  //传入的sort字符串
    public String sort;

    public String getSort(){
        if(sortView!=null){
        	//驼峰命名转化为横线命名 例如commentNum=>commment_num 适应sql语句字段规则
            this.sort=StringTool.humpToLine(this.sortView);  
        }
        return this.sort;
    }

    //排序方向
    public String direction;
    //页面大小
    public Integer pageSize;
    //页数
    public Integer pageNum;

这里放一个驼峰命名转换的工具类:

public class StringTool {
    private static Pattern linePattern = Pattern.compile("_(\\w)");

    /** 下划线转驼峰 */
    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /** 驼峰转下划线(简单写法,效率低于{@link #humpToLine2(String)}) */
    public static String humpToLine(String str) {
        return str.replaceAll("[A-Z]", "_$0").toLowerCase();
    }

    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    /** 驼峰转下划线,效率比上面高 */
    public static String humpToLine2(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    public static void main(String[] args) {
        String lineToHump = lineToHump("f_parent_no_leader");
        System.out.println(lineToHump);// fParentNoLeader
        System.out.println(humpToLine(lineToHump));// f_parent_no_leader
        System.out.println(humpToLine2(lineToHump));// f_parent_no_leader
    }

}

3.MyBatis配置

在application.yml中对mybatis相关属性进地配置,其他数据库连接相关配置就不说了。

#mybatis配置
mybatis:
  #config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml  #定义.xml文件位置,注意不能与config-location共存
  type-aliases-package: com.xinxin.pojo  #定义别名,直接找到pojo这个包 
  configuration:
    map-underscore-to-camel-case: true  #驼峰命名设置为true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #运时打印出sql语句

4. Dao层

    //根据条件查询文章
    public List<Article> queryByCondition(ArticleQuery aq);

5. mapper层

在ArticleMapper.xml写对应的sql语句

    <!--文章查询的sql片断,建议是以单表为单位定义查询条件,建议将常用的查询条件都写出来-->
    <sql id="query_items_where">
        <if test="name!=null and name!=''">
            and name like '%${name}%'
        </if>
        <if test="title!=null and title!=''">
            and title like '%${title}%'
        </if>
        <if test="articleId!=null">
            and article_id = #{articleId}
        </if>
    </sql>

	
    <select id="queryByCondition" parameterType="ArticleQuery" resultType="Article">
            -- 注意ORDER BY后面用的是$而不是#
            select * from articles
            <where>
                <include refid="query_items_where" />
            </where>
            <if test="sort!=null and sort!=''">
                ORDER BY ${sort}
            </if>
            <if test="direction!=null and direction!=''">
                ${direction}
            </if>

    </select>

注意一定要开启mybatis二级缓存,这样如果第一次select,后面就不用都去查询数据库了,系统会变快!只需要在ArticleMapper.xml加一个“”即可。

<mapper namespace="com.xinxin.dao.ArticleDao">
<!--    开启二级缓存,这样对所有select操作都会作缓存处理-->
    <cache />
 ...
</mapper>

6. service层

    //根据条件查询文章
    public List<Article> queryByCondition(ArticleQuery aq);

7. service层对应的Impl

    public PageResult findPage(ArticleQuery aq) {
        return PageUtils.getPageResult(getPageInfo(aq));
    }
	
   private PageInfo<Article> getPageInfo(ArticleQuery aq) {
       PageHelper.startPage(aq.getPageNum(), aq.getPageSize());
       List<Article> sysMenus = articleDao.queryByCondition(aq);
       return new PageInfo<Article>(sysMenus);
    }

这里用到的工具类PageUtils定义为

import com.github.pagehelper.PageInfo;
public class PageUtils {
    
    /**
     * 将分页信息封装到统一的接口
     * @param pageInfo
     * @return
     */
    public static PageResult getPageResult(PageInfo<?> pageInfo) {
        PageResult pageResult = new PageResult();
        pageResult.setPageNum(pageInfo.getPageNum());
        pageResult.setPageSize(pageInfo.getPageSize());
        pageResult.setTotalSize(pageInfo.getTotal());
        pageResult.setTotalPages(pageInfo.getPages());
        pageResult.setContent(pageInfo.getList());
        return pageResult;
    }
}

对应的PageResulto类封了分页所需要的信息:

public class PageResult<T> {
    /**
     * 当前页码
     */
    private int pageNum;
    /**
     * 每页数量
     */
    private int pageSize;
    /**
     * 记录总数
     */
    private long totalSize;
    /**
     * 页码总数
     */
    private int totalPages;
    /**
     * 数据模型
     */
    private List<T> content;
    public int getPageNum() {
        return pageNum;
    }
    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }
    public int getPageSize() {
        return pageSize;
    }
    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
    public long getTotalSize() {
        return totalSize;
    }
    public void setTotalSize(long totalSize) {
        this.totalSize = totalSize;
    }
    public int getTotalPages() {
        return totalPages;
    }
    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }
    public List<T> getContent() {
        return content;
    }
    public void setContent(List<T> content) {
        this.content = content;
    }
}

8. Controller层

    @PostMapping("api/findPage")
    public Object findPage(ArticleQuery aq) {
        return articleService.findPage(aq);
    }

9. 用Postman工具进行测试

在这里插入图片描述

10. 打印的sql语句与显示结果

JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@119290b9] will not be managed by Spring
==>  Preparing: SELECT count(0) FROM articles
==> Parameters: 
<==    Columns: count(0)
<==        Row: 5
<==      Total: 1
==>  Preparing: -- 注意ORDER BY后面用的是$而不是# select * from articles ORDER BY comment_num desc LIMIT ?
==> Parameters: 2(Integer)
<==    Columns: article_id, user_id, title, name, view_num, comment_num, category_id, create_time
<==        Row: 98, 1, 444, 444.md, 0, 9, 1, 2020-10-01 10:55:45
<==        Row: 83, 1, daidai, daidai.md, 0, 4, 1, 2020-09-30 11:09:42
<==      Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1a345298]
Article(articleId=98, userId=1, title=444, name=444.md, viewNum=0, commentNum=9, categoryId=1, createTime=2020-10-01 10:55:45.0)
Article(articleId=83, userId=1, title=daidai, name=daidai.md, viewNum=0, commentNum=4, categoryId=1, createTime=2020-09-30 11:09:42.0)

成功打印出两行数据,且是按comment_num倒序排列,打印的sql语句也显示正确。

11.总结

这里没有用mybatis-plus是想自己多实际写一写sql语句,同时复习一下之前的知识,实际项目中仍然以原生sql语句为主。感兴趣的网友,可以参考。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值