springboot+mybatis实现分页

分页成果展示:www.qihea.xyz

1.整合mybatis框架

1.数据库准备好数据:我网站中的数据时通过python爬虫,直接保存到数据库中的。图片直接保存的豆瓣的url。

2.整合mybatis依赖和mybatis分页插件:

根据此教程完成初步操作:Kotlin+IDEA+SpringBoot+MyBatis+MySQL+Gradle项目(不要选择kotlin即可)

然后添加分页插件依赖:官网https://github.com/abel533/MyBatis-Spring-Boot

//mybatis分页插件
compile group: 'com.github.pagehelper', name:'pagehelper-spring-boot-starter',version: '1.2.10'

推荐阅读:(读不读都行)
1.为什么使用mybatis
2.Mybatis配置之配置元素详述
3.mybatis使用
 

2.结合mybatis分页插件编写动态sql实现分页功能。

有三种分页方式:JavaWeb分页显示内容之分页查询的三种思路(数据库分页查询)。我选择的是后台分页。

1.约定好url接口(REST风格)

  因为使用mybatis分页插件直接得到的是json数据,所以我直接使用接口来访问和传递数据。如果不想用接口的话,也可以使用模版等方式,不过我就不说了。

  使用接口后,前端可以直接通过ajax来通过rest风格接口获取数据。

示例:

  1. 如果什么都不选择http://www.qihea.xyz/page?size=1&start=0&sort=0
  2. {"total":4867,"list":[{"id":1292052,"name":"肖申克的救赎","score":9.6,"url":"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg"}],"pageNum":0,"pageSize":1,"size":1,"startRow":1,"endRow":1,"pages":4867,"prePage":0,"nextPage":1,"isFirstPage":false,"isLastPage":false,"hasPreviousPage":false,"hasNextPage":true,"navigatePages":8,"navigatepageNums":[1,2,3,4,5,6,7,8],"navigateFirstPage":1,"navigateLastPage":8}
  3. 比如选择:剧情分类(genres=11),地区为中国(place=中国),年代为2018(beginyear和endyear都为2018),排序为最热电影(sort=0),第一页(start=1),每页最多条目(size=10)来发送url请求。
  4. http://www.qihea.xyz/page?size=10&start=1&sort=0&genres=11&place=%E4%B8%AD%E5%9B%BD%E5%A4%A7%E9%99%86&beginyear=2018&endyear=2018
  5. {"total":6,"list":[{"id":26752088,"name":"我不是药神","score":9.0,"url":"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2519070834.jpg"},{"id":26425063,"name":"无双","score":8.2,"url":"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2535096871.jpg"},{"id":26647117,"name":"暴裂无声","score":8.2,"url":"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2517333671.jpg"},{"id":27110296,"name":"无名之辈","score":8.3,"url":"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2539661066.jpg"},{"id":25716096,"name":"狗十三","score":8.5,"url":"https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2540474819.jpg"},{"id":30206517,"name":"切尔诺贝利之春","score":8.0,"url":"https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2520682504.jpg"}],"pageNum":1,"pageSize":10,"size":6,"startRow":1,"endRow":6,"pages":1,"prePage":0,"nextPage":0,"isFirstPage":true,"isLastPage":true,"hasPreviousPage":false,"hasNextPage":false,"navigatePages":8,"navigatepageNums":[1],"navigateFirstPage":1,"navigateLastPage":1}

2.使用mybatis pagehelper分页插件。

我直接给出我的流程:

1.编写controller层(根据之前设计好的接口来)

@RestController
public class CategoryController {
    @Autowired
    private CategoryService categoryService;
    @GetMapping("/page")
    public PageInfo<MovieShowBean> page(@RequestParam(value = "start",defaultValue = "1") String start,
                                        @RequestParam(value = "size",defaultValue = "6") String size,
                                        @RequestParam(value = "genres",required=false) String genres ,
                                        @RequestParam(value = "sort",defaultValue = "0") String sort,
                                        @RequestParam(value = "beginyear",required=false) String beginyear,
                                        @RequestParam(value = "endyear",required=false) String endyear,
                                        @RequestParam(value = "place",required=false) String place
                                ){
        Map<String,String> myMap= new HashMap<>(7);
        myMap.put("start",start);
        myMap.put("size",size);
        myMap.put("genres",genres);
        myMap.put("sort",sort);
        myMap.put("beginyear",beginyear);
        myMap.put("endyear",endyear);
        myMap.put("place",place);
        return categoryService.homePage(myMap);
    }
}

2.编写service层:

 在执行sql前添加插件,完成分页功能:参考【MyBatis】MyBatis分页插件PageHelper的使用

我们知道,在MySQL中,分页的sql是使用limit来做,如果我们自己写sql,那分页肯定是没有任何问题的。但是一旦model多了起来,复杂了起来,我们很自然的想到使用mybatis的逆向工程来生成相应的po和mapper,但是同时也会带来弊端,比如这里的分页问题就不好解决了。 所以还是选择使用分页插件。

查询的sql语句执行之前,添加一行代码PageHelper.startPage(x,y);第一个参数表示第几页,第二个参数表示每页显示的记录数。这样在执行sql后就会将记录按照语句中设置的那样进行分页。如果需要获取总记录数的话,需要PageInfo类的对象,这个对象可以获取总记录数,下面看下测试的代码。

public interface CategoryService {
    PageInfo homePage(Map<String,String> map);
}

@Service
public class CategoryServiceImpl implements CategoryService {

    @Resource
    private MovieShowBeanMapper movieShowBeanMapper;
    @Resource
    private CategoryMapper categoryMapper;

    @Override
    public PageInfo homePage(Map<String, String> myMap) {
        //设置分页参数
        PageHelper.startPage(Integer.valueOf(myMap.get("start")),Integer.valueOf(myMap.get("size")));
        List<MovieShowBean> list=movieShowBeanMapper.findShowMovie(myMap.get("genres"),
                                                            myMap.get("sort"),
                                                            myMap.get("beginyear"),
                                                            myMap.get("endyear"),
                                                            myMap.get("place"));
        //使用对象包装返回结果
        return new PageInfo(list);
    }

}

3.编写dao层(或者说是mapper)

因为需要根据选择来判断是否添加限定条件,所以必须是动态sql。

因为注解方式很简单,所以我直接使用注解的方式完成操作。

参考阅读:mybatis的注解开发之三种动态sql

MyBatis框架核心之(五)注解使用resultMap及多表查询

@SelectProvider(type = MovieShowSqlProvider.class, method = "selectShow"):使用MovieShowSqlProvider.class这个类中的方法selectShow返回的sql字符串  作为查询的语句。

@Results注解:结果映射的列表, 包含了一个特别结果列如何被映射到属性或字段的详情。属性:value, id。value 属性是 Result 注解的数组。这个id的属性是结果映射的名称可以省略。

@Result注解:column为数据库字段名,porperty为实体类属性名,jdbcType为数据库字段数据类型,id为是否为主键。

public interface MovieShowBeanMapper {

    @SelectProvider(type = MovieShowSqlProvider.class, method = "selectShow")
    @Results(value = {
            @Result( id=true, property = "id",column = "id"),
            @Result(property = "name",column = "name"),
            @Result(property = "score",column = "score")
    })
    List<MovieShowBean> findShowMovie(
                                         @Param("genres") String genres ,
                                         @Param("sort") String sort,
                                         @Param("beginyear") String beginyear,
                                         @Param("endyear") String endyear,
                                         @Param("place") String place
    );
}

4.编写mapper中用到的动态sql:MovieShowSqlProvider类

参考:注解方式配置MyBatis执行动态SQL

然后在这个类中进行sql语句的拼接
 tempSql=new SQL().SELECT("movie_id").FROM("class_contact");
因为是分页查询,使用的是子查询的方式

public class MovieShowSqlProvider {
   /*完整的sql语句
    SELECT m.id,m.name,m.time from movie m WHERE id in (
        SELECT movie_id from place WHERE name ="中国大陆"
         and movie_id in
         (SELECT movie_id from class_contact WHERE class_id=11)
        ) and YEAR(m.time)>1995 and  YEAR(m.time)<=2018
         ORDER BY(YEAR(m.time)) desc*/
    public String selectShow(Map<String,Object> map){
        SQL tempSql=null;
        String order="order by m.comment_num";
        //1.是否有分类
        if(map.get("genres") != null){
            tempSql=new SQL().SELECT("movie_id").FROM("class_contact");
            if(map.get("place") != null){
               SQL tempSql2=new SQL().SELECT("movie_id").FROM("place")
                       .WHERE("name=#{place}");
               tempSql.WHERE("movie_id in("+tempSql2+")")
                       .WHERE("class_id=#{genres}");
            }
            else{
                tempSql.WHERE("class_id=#{genres}");
            }
        }
        else{
            if(map.get("place") != null){
                tempSql=new SQL().SELECT("movie_id").FROM("place")
                        .WHERE("name=#{place}");
            }
        }
        //判断排序方式

        SQL finalTempSql = tempSql;
        return new SQL(){
            {
                SELECT("m.id,m.name,m.score,m.url");
                FROM("movie m");
                if(finalTempSql !=null)
                    WHERE("m.id in("+finalTempSql+")");
                if(map.get("beginyear") != null)
                    WHERE("YEAR(m.time)>=#{beginyear}");
                if(map.get("endyear") != null)
                    WHERE("YEAR(m.time)<=#{endyear}");
                //排序
                if(map.get("sort").equals("1")){
                    ORDER_BY("m.time desc");
                }
                else if(map.get("sort").equals("2")){
                    ORDER_BY("m.score desc");
                }
                else{
                    ORDER_BY("m.comment_num desc");
                }
            }
        }.toString();
    }
}

好啦,单元测试自己写喽。还有注意写异常处理。(我暂时还不会写)

相关阅读:

1.MyBatis如何防止SQL注入

2.mybatis动态SQL防止SQL注入

3.Mybatis插件原理

4.Springboot+gradle+Mybatis-Generator

5.简介如何使用MyBatis generator生成的Example文件

逆向工程可用可不用,对我工程来说是没必要的。

3.前端发送请求来获取数据。

直接通过点按钮击触发参数,通过参数发送get请求,参数放在里面。

不过因为不太清楚get请求,我直接使用的拼接url来发送get请求。

然后前端通过js进行数据处理,我听说前端也有数据处理插件,不过没有查询资料。感兴趣的可以查一查。

//请求json
function sendurl() {
    //先构造基础部分
    baseUrl="/page?size="+size+"&start="+start+"&sort="+sort;
    if(genres!=""){
        baseUrl+="&genres="+genres;
    }
    if(place!=""){
        baseUrl+="&place="+place;
    }
    if(beginyear!=""){
        baseUrl+="&beginyear="+beginyear;
    }
    if(endyear!=""){
        baseUrl+="&endyear="+endyear;
    }
    $.ajax({
        //访问地址
        url: baseUrl,
        //访问方式,一般有GET或POST两种
        type: 'GET',
        //返回的数据格式,这个是可选参数,jquery回默认判断返回参数的类型
        dataType: 'json'
        /*//发送的数据
        data: {
            param1: 'value1',
            param2: 'value2'
        },*/
    })
    //成功后的处理函数,res为服务器返回的数据
        .done(function(data) {
            //删除当前页面显示:
            $(".want_remove").remove();

            toastr.success('获取数据成功:ヾ(o◕∀◕)ノヾ');
            //因为设置了type,返回的就是json对象


            //构造电影模块
            //背景变量
            var myback=["bg-danger","bg-primary","bg-success","bg-info","bg-secondary"]
            var lineNum=size/2;

            $.each(data.list,function(idx,item) {
                myscore=Math.round(item.score/2);
                var myn=idx%5;
                var star="";
                for(i=0;i<myscore;i++){
                    star+="⭐";
                }
                if(idx<lineNum){
                    $("#first_row").append(
                    "<div class=\"flex-fill justify-content-center text-center want_remove\">\n" +
                        "                        <div class=\"card "+myback[myn]+" text-white\" style=\"width:117px;margin: auto;cursor:pointer;\" onclick=\"window.open('https://movie.douban.com/subject/"+item.id+"/','_blank');\">\n" +
                        "                            <img class=\"card-img-top\" src=\""+item.url+"\"\n" +
                        "                                 alt=\"Card image\" style=\"width:100%\">\n" +
                        "                            <div style=\"font-size: 14px ;\">\n" +
                        "                                <p class=\"shenglue\">"+item.name+"</p>\n" +
                        "                                <p class=\"shenglue\" style=\"color: yellow; font-size: 13px;\">"+star+item.score+"</p>\n" +
                        "                            </div>\n" +
                        "                        </div>\n" +
                        "                    </div>"
                );
                }
                else{
                    $("#second_row").append(
                        "<div class=\"flex-fill justify-content-center text-center want_remove\">\n" +
                        "                        <div class=\"card "+myback[myn]+" text-white\" style=\"width:117px;margin: auto;cursor:pointer;\"onclick=\"window.open('https://movie.douban.com/subject/"+item.id+"/','_blank');\">\n" +
                        "                            <img class=\"card-img-top\" src=\""+item.url+"\"\n" +
                        "                                 alt=\"Card image\" style=\"width:100%\">\n" +
                        "                            <div style=\"font-size: 14px ;\">\n" +
                        "                                <p class=\"shenglue\">"+item.name+"</p>\n" +
                        "                                <p class=\"shenglue\" style=\"color: yellow; font-size: 13px;\">"+star+item.score+"</p>\n" +
                        "                            </div>\n" +
                        "                        </div>\n" +
                        "                    </div>"
                    );
                }
            })
            // $(".want_remove").fadeIn("slow");

            //构造分页栏
            //显示基本信息
            $("#myresult").text("共"+data.total+"部;"+data.pages+"页");
            //删除之前的标签
            $(".can_remove").remove();
            //设置标签页
            /*for(i=0;i<data.navigatePages;i++){*/
            $.each(data.navigatepageNums,function(i,item) {
                if (item==data.pageNum) {
                    $("#list_next").before(
                        "<li id=\"mylist_" + i + "\" class=\"page-item active can_remove\" onclick='clickList(this.id)'><a class=\"page-link\" href=\"javascript:void(0)\">" + item + "</a></li>"
                    );
                }
                else{
                    $("#list_next").before(
                        "<li id=\"mylist_" + i + "\" class=\"page-item can_remove\" onclick='clickList(this.id)'><a class=\"page-link\" href=\"javascript:void(0)\">" + item+ "</a></li>"
                    );
                }
            })
            //如果前一页不存在
            if(!data.hasPreviousPage)
                $("#list_pre").addClass("disabled");
            else{
                $("#list_pre").removeClass("disabled");
            }
            //如果后一页不存在
            if(!data.hasNextPage)
                $("#list_next").addClass("disabled");
            else{
                $("#list_next").removeClass("disabled");
            }
            //分别是数组
            console.log("success");
        })
        //失败后的处理函数
        .fail(function() {
            toastr.error('Error:肯定不是服务器炸了ヾノ≧∀≦)o死开!');
            console.log("error");
        })
        //结束后的处理函数,不管成功失败都执行
        /*.always(function(res) {
            console.log("complete");
        });*/
    //get不能判断是否失败
    /*$.get(baseUrl,function(result){
        alert(result);
    });*/
}

最终完成喽。


版权声明:本文为qq_40493277原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。