创建项目,引入依赖

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0</modelVersion>
    6. <groupId>com.zh</groupId>
    7. <artifactId>bill-manager-tk</artifactId>
    8. <version>1.0-SNAPSHOT</version>
    9. <parent>
    10. <groupId>org.springframework.boot</groupId>
    11. <artifactId>spring-boot-starter-parent</artifactId>
    12. <version>2.0.0.RELEASE</version>
    13. </parent>
    14. <properties>
    15. <java.version>1.8</java.version>
    16. </properties>
    17. <dependencies>
    18. <dependency>
    19. <groupId>org.springframework.boot</groupId>
    20. <artifactId>spring-boot-starter-web</artifactId>
    21. </dependency>
    22. <dependency>
    23. <groupId>org.springframework.boot</groupId>
    24. <artifactId>spring-boot-starter-test</artifactId>
    25. </dependency>
    26. <dependency>
    27. <groupId>mysql</groupId>
    28. <artifactId>mysql-connector-java</artifactId>
    29. <version>5.1.46</version>
    30. </dependency>
    31. <!-- 通用mapper -->
    32. <dependency>
    33. <groupId>tk.mybatis</groupId>
    34. <artifactId>mapper-spring-boot-starter</artifactId>
    35. <version>2.0.2</version>
    36. </dependency>
    37. <dependency>
    38. <groupId>org.springframework.boot</groupId>
    39. <artifactId>spring-boot-starter-thymeleaf</artifactId>
    40. </dependency>
    41. <dependency>
    42. <groupId>org.projectlombok</groupId>
    43. <artifactId>lombok</artifactId>
    44. <scope>provided</scope>
    45. </dependency>
    46. </dependencies>
    47. </project>

    spring boot 启动器
    注意使用tk-mybatis 的MapperScan

    @SpringBootApplication
    @MapperScan("com.zh.dao")
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    application.yml:springboot全局配置文件

    # 连接池
    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password:
        url: jdbc:mysql://localhost:3306/bill-manager
    
      thymeleaf:
        cache: false #关闭thymeleaf缓存
    
    #整合mybatis
    mybatis:
      type-aliases-package: com.zh.bean #别名搜索
      mapper-locations: classpath:/mappers/*.xml
    

    实体类
    账单类型

    @Table(name = "bill_type_")
    @Data
    public class BillType implements Serializable {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id_")
        private Long id;
        @Column(name = "name_")
        private String name;
    }
    

    账单

    @Table(name = "bill_")
    @Data
    public class Bill implements Serializable {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id_")
        private Long id;
        @Column(name = "title_")
        private String title;
        @Column(name = "bill_time_")
        private Date billTime;
        @Column(name = "type_id_")
        private Long typeId;
        @Column(name = "price_")
        private Double price;
        @Column(name = "explain_")
        private String explain;
        /**
         * 类别名称
         */
        @Transient
        private String typeName;
        /**
         * 开始时间:用于查询
         */
        @Transient
        private Date date1;
        /**
         * 结束时间:用于查询
         */
        @Transient
        private Date date2;
    }
    

    注意 @Transient注解表示当前属性为瞬时属性,跟字段没有映射,上面实体类中date1和date2只作为查询条件存
    在,跟表中的字段没有关联

    dao

    public interface BillMapper extends Mapper<Bill> {
        List<Bill> select(Bill b);
    }
    
    public interface TypeMapper extends Mapper<BillType> {
    }
    

    查询修改的映射文件
    tk mybatis内置查询比较单一固定,不灵活,不能实现动态查询,动态修改语句, 所以特定需求的查询需要写映射
    文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zh.dao.BillMapper">
        <sql id="selectSql">
            SELECT
            b.id_ as id,
            b.title_ as title,
            b.bill_time_ as billTime,
            b.type_id_ as typeId,
            b.price_ as price,
            b.explain_ as `explain`,
            t.name_ as typeName
            FROM
            bill_ as b
            left join
            bill_type_ as t
            on
            b.type_id_ = t.id_
        </sql>
        <select id="select" resultType="bill">
            <include refid="selectSql"/>
            <where>
                <if test="typeId !=null">
                    b.type_id_ = #{typeId}
                </if>
                <if test="title !=null">
                    and b.title_ like '%${title}%'
                </if>
                <if test="date1 !=null">
                    and b.bill_time_ &gt;= #{date1}
                </if>
                <if test="date2 !=null">
                    and b.bill_time_ &lt;= #{date2}
                </if>
            </where>
        </select>
    </mapper>
    

    注意:explain字段名在mysql 中是关键词必须使用``包裹

    service

    @Service
    public class BillService {
    
        @Resource
        private BillMapper billMapper;
    
        public List<Bill> list(Bill b) {
            return billMapper.select(b);
        }
    
        public int add(Bill b) {
            return billMapper.insert(b);
        }
    
        public Bill get(Long id) {
            return billMapper.selectByPrimaryKey(id);
        }
    
        public int update(Bill b) {
            return billMapper.updateByPrimaryKey(b);
        }
    
        public int delete(Long id) {
            return billMapper.deleteByPrimaryKey(id);
        }
    }
    
    @Service
    public class TypeService {
        @Resource
        private TypeMapper typeMapper;
    
        public List<BillType> list() {
            return typeMapper.selectAll();
        }
    }
    

    测试

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class BillServiceTest {
        @Autowired
        private BillService billService;
        @Test
        public void list() {
            Bill b = new Bill();
            b.setTitle("a");
            List<Bill> list = billService.list(b);
            for (Bill bill : list) {
                System.out.println(bill.getTitle());
            }
        }
    }
    

    controller

    package com.zh.controller;
    
    import com.zh.bean.Bill;
    import com.zh.bean.BillType;
    import com.zh.service.BillService;
    import com.zh.service.TypeService;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * @Author: deemoHui
     * @Description:
     * @Date Created in 2020-10-19 13:05
     * @Modified By:
     */
    @Controller
    @RequestMapping("/bill")
    public class BillController {
        @Resource
        private TypeService typeService;
        @Resource
        private BillService billService;
    
        /**
         * 查询
         *
         * @param b
         * @param model
         * @return
         */
        @RequestMapping("/list")
        public String list(Bill b, Model model) {
            List<BillType> types = typeService.list();
            model.addAttribute("types", types);
            List<Bill> list = billService.list(b);
            model.addAttribute("list", list);
            model.addAttribute("bill", b);
            return "/bill/list";
        }
    
        /**
         * 调到添加页面
         *
         * @return
         */
        @RequestMapping("/toAdd")
        public String toAdd(Model model) {
            List<BillType> types = typeService.list();
            model.addAttribute("types", types);
            return "/bill/add";
        }
    
        /**
         * 添加
         *
         * @param b
         * @return
         */
        @RequestMapping("/add")
        public String add(Bill b) {
            billService.add(b);
            return "redirect:/bill/list";
        }
    
        /**
         * 删除
         *
         * @param id
         * @return
         */
        @RequestMapping("/delete/{id}")
        public String delete(@PathVariable("id") Long id) {
            billService.delete(id);
            return "redirect:/bill/list";
        }
    
        /**
         * 修改
         *
         * @param id
         * @return
         */
        @RequestMapping("/toUpdate/{id}")
        public String toUpdate(@PathVariable("id") Long id, Model model) {
            List<BillType> types = typeService.list();
            model.addAttribute("types", types);
            Bill bill = billService.get(id);
            model.addAttribute("bill", bill);
            return "/bill/update";
        }
    
        /**
         * 修改
         *
         * @param b
         * @return
         */
        @RequestMapping("/update")
        public String update(Bill b) {
            billService.update(b);
            return "redirect:/bill/list";
        }
    }
    

    页面
    拷贝素材中我们用到的bootstrap.css样式和jquery和my97日历控件相关静态资源到/resources/static目录
    image.png
    list.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>userList</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
        <script type="text/javascript" th:src="@{/js/My97DatePicker/WdatePicker.js}"></script>
        <script type="text/javascript" src="/js/My97DatePicker/lang/zh-cn.js"></script>
        <script type="text/javascript" th:src="@{/js/jquery/jquery-1.10.2.min.js}"></script>
    </head>
    <body class="container">
    <br/>
    <h1>账单列表</h1>
    <br/><br/>
    
    <div class="with:80%">
        <!-- 查询数据-->
        <form class="form-inline" id="qf" th:action="@{/bill/list}" method="post">
            <div class="form-group">
                <label for="typeId" class="control-label">类型</label>
                <select name="typeId" id="typeId" class="form-control">
                    <option value="">全部</option>
                    <option th:each="t:${types}" th:value="${t.id}" th:text="${t.name}"
                            th:selected="(${bill.typeId} == ${t.id})"></option>
                </select>
            </div>
            <div class="form-group">
                <label for="date1" class="control-label">开始时间</label>
                <input type="text" class="form-control" name="date1" id="date1" placeholder="开始时间" th:value="${bill.date1} ? ${#dates.format(bill.date1, 'yyyy-MM-dd')}"
                       onclick="WdatePicker()"/>
            </div>
            <div class="form-group">
                <label for="date2" class="control-label">结束时间</label>
                <input type="text" class="form-control" name="date2" id="date2" placeholder="结束时间" th:value="${bill.date2} ? ${#dates.format(bill.date2, 'yyyy-MM-dd')}"
                       onclick="WdatePicker()"/>
            </div>
            <div class="form-group">
                <input type="submit" value="查询" class="btn btn-info"/>
                &nbsp; &nbsp;
                <input type="reset" value="重置" class="btn btn-info"/>
                &nbsp; &nbsp;
                <a href="/bill/toAdd" th:href="@{/bill/toAdd}" class="btn btn-info">添加</a>
            </div>
        </form>
    </div>
    <br/>
    
    
    <div class="with:80%">
        <table class="table table-striped table-bordered">
            <thead>
            <tr>
                <th>#</th>
                <th>标题</th>
                <th>时间</th>
                <th>金额</th>
                <th>类别</th>
                <th>说明</th>
                <th>操作</th>
            </tr>
            </thead>
            <!--        数据展示-->
            <tbody>
            <tr th:each="b, bstatus : ${list}" th:style="${bstatus.odd} ? 'background-color:#A3C6C8'">
                <th scope="row" th:text="${bill.id}">id</th>
                <td th:text="${b.title}">name</td>
                <td th:text="${b.billTime} ? ${#dates.format(b.billTime, 'yyyy-MM-dd')}">time</td>
                <td th:text="${b.price}">price</td>
                <td th:text="${b.typeName}">typeName</td>
                <td th:text="${b.explain}">explain</td>
                <td>
                    <a th:href="|/bill/delete/*{b.id}|">删除</a>
                    <a th:href="|/bill/toUpdate/*{b.id}|">修改</a>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
    
    </body>
    </html>
    

    add.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>user</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
        <script type="text/javascript" th:src="@{/js/My97DatePicker/WdatePicker.js}"></script>
        <script type="text/javascript" src="/js/My97DatePicker/lang/zh-cn.js"></script>
    
    
    </head>
    <body class="container">
    <br/>
    <h1>添加账单</h1>
    <br/><br/>
    <div class="with:80%">
        <form class="form-horizontal" action="/bill/add" method="post">
            <div class="form-group">
                <label for="typeId" class="col-sm-2 control-label">类型</label>
                <div class="col-sm-10">
                    <select name="typeId" id="typeId" class="form-control">
                        <option th:each="t:${types}" th:value="${t.id}" th:text="${t.name}"></option>
                    </select>
                </div>
            </div>
            <div class="form-group">
                <label for="title" class="col-sm-2 control-label">标题</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="title" id="title" placeholder="标题"/>
                </div>
            </div>
            <div class="form-group">
                <label for="billTime" class="col-sm-2 control-label">日期</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="billTime" id="billTime" placeholder="日期"
                           onclick="WdatePicker()"/>
                </div>
            </div>
            <div class="form-group">
                <label for="price" class="col-sm-2 control-label">金额</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="price" id="price" placeholder="金额"/>
                </div>
            </div>
            <div class="form-group">
                <label for="explain" class="col-sm-2 control-label">说明</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="explain" id="explain" placeholder="说明"/>
                </div>
            </div>
    
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <input type="submit" value="保存" class="btn btn-info"/>
                    &nbsp; &nbsp; &nbsp;
                    <input type="reset" value="重置" class="btn btn-info"/>
                </div>
    
            </div>
        </form>
    </div>
    </body>
    </html>
    

    update.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>user</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
        <script type="text/javascript" th:src="@{/js/My97DatePicker/WdatePicker.js}"></script>
        <script type="text/javascript" src="/js/My97DatePicker/lang/zh-cn.js"></script>
    </head>
    <body class="container">
    <br/>
    <h1>修改账单</h1>
    <br/><br/>
    <div class="with:80%">
        <form class="form-horizontal" th:action="@{/bill/update}" method="post"
              th:object="${bill}">
            <input type="hidden" name="id" th:value="*{id}"/>
            <div class="form-group">
                <label for="typeId" class="col-sm-2 control-label">类型</label>
                <div class="col-sm-10">
                    <select name="typeId" id="typeId" class="form-control">
                        <option th:each="t:${types}" th:value="${t.id}" th:text="${t.name}" 
                                th:selected="${t.id} == *{typeId}"></option>
                    </select>
                </div>
            </div>
            <div class="form-group">
                <label for="title" class="col-sm-2 control-label">标题</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="title" id="title" th:value="*{title}" placeholder="标题"/>
                </div>
            </div>
            <div class="form-group">
                <label for="billTime" class="col-sm-2 control-label">日期</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="billTime" id="billTime"
                           th:value="*{billTime} ? *{#dates.format(billTime, 'yyyy-MM-dd')}" placeholder="日期"
                           onclick="WdatePicker()"/>
                </div>
            </div>
            <div class="form-group">
                <label for="price" class="col-sm-2 control-label">金额</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="price" id="price" th:value="*{price}" placeholder="金额"/>
                </div>
            </div>
            <div class="form-group">
                <label for="explain" class="col-sm-2 control-label">说明</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" name="explain" id="explain" th:value="*{explain}" placeholder="说明"/>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <input type="submit" value="保存" class="btn btn-info"/>
                    &nbsp; &nbsp; &nbsp;
                    <input type="reset" value="重置" class="btn btn-info"/>
                </div>
            </div>
        </form>
    </div>
    </body>
    </html>
    

    注意在springboot中配置日期类型转换器

    package com.zh.config;
    
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.stereotype.Component;
    
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    @Component
    public class DateConverterConfig implements Converter<String, Date> {
        private static final List<String> formarts = new ArrayList<>(4);
    
        static {
            formarts.add("yyyy-MM");
            formarts.add("yyyy-MM-dd");
            formarts.add("yyyy-MM-dd hh:mm");
            formarts.add("yyyy-MM-dd hh:mm:ss");
        }
    
        @Override
        public Date convert(String source) {
            String value = source.trim();
            if ("".equals(value)) {
                return null;
            }
            if (source.matches("^\\d{4}-\\d{1,2}$")) {
                return parseDate(source, formarts.get(0));
            } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
                return parseDate(source, formarts.get(1));
            } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) {
                return parseDate(source, formarts.get(2));
            } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
                return parseDate(source, formarts.get(3));
            } else {
                throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
            }
        }
    
        /**
         * 格式化日期
         *
         * @param dateStr String 字符型日期
         * @param format  String 格式
         * @return Date 日期
         */
        public Date parseDate(String dateStr, String format) {
            Date date = null;
            try {
                DateFormat dateFormat = new SimpleDateFormat(format);
                date = dateFormat.parse(dateStr);
            } catch (Exception e) {
            }
            return date;
        }
    }
    

    删除

    <a th:href="|/bill/delete/*{b.id}|">删除</a>
    

    分页
    引入pageHelper分页插件的starter,注意:必须引入pagehelper的启动器,不能直接引入pagehelper

            <!-- 分页插件-->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>1.2.3</version>
            </dependency>
    

    分页方法的测试

        @Test
        public void findPage() {
            Bill b = new Bill();
            b.setTitle("a");
            PageInfo<Bill> page = PageHelper.startPage(2, 10).doSelectPageInfo(() -> {
                billService.list(b);
            });
            page.getList().forEach(bill -> {
                System.out.println(bill.getId() + " " + bill.getTitle());
            });
            System.out.println("总行数=" + page.getTotal());
            System.out.println("当前页=" + page.getPageNum());
            System.out.println("每页行数=" + page.getPageSize());
            System.out.println("总页数=" + page.getPages());
            System.out.println("起始行数=" + page.getStartRow());
            System.out.println("是第一页=" + page.isIsFirstPage());
            System.out.println("是最后页=" + page.isIsLastPage());
            System.out.println("还有下一页=" + page.isHasNextPage());
            System.out.println("还有上一页=" + page.isHasPreviousPage());
            System.out.println("页码列表" + Arrays.toString(page.getNavigatepageNums()));
        }
    

    image.png
    service中的分页方法

        public PageInfo<Bill> listPage(Bill b, int pageNum, int pageSize) {
            return PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> {
                billMapper.select(b);
            });
        }
    

    controller中的方法

        /**
         * 查询
         *
         * @param b
         * @param model
         * @return
         */
        @RequestMapping("/list-page")
        public String listPage(
                @RequestParam(defaultValue = "1") int pageNum,
                @RequestParam(defaultValue = "10") int pageSize,
                Bill b,
                Model model) {
            List<BillType> types = typeService.list();
            model.addAttribute("types", types);
            PageInfo<Bill> pageInfo = billService.listPage(b, pageNum, pageSize);
            model.addAttribute("page", pageInfo);
            model.addAttribute("bill", b);
            return "/bill/list-page";
        }
    

    前端分页
    页面部分分页逻辑示意图
    image.png
    list-page.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <title>userList</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.css}"></link>
        <script type="text/javascript" th:src="@{/js/My97DatePicker/WdatePicker.js}"></script>
        <script type="text/javascript" th:src="@{/js/jquery/jquery-1.10.2.min.js}"></script>
    </head>
    <body class="container">
    <br/>
    <h1>账单列表</h1>
    <br/><br/>
    <div class="with:80%">
        <form class="form-inline" id="qf" th:action="@{/bill/list-page}" method="post">
            <input type="hidden" name="pageNum" id="pageNum" th:value="${page.pageNum }"/>
            <input type="hidden" name="pageSize" id="pageSize" th:value="${page.pageSize }"/>
            <div class="form-group">
                <label for="typeId" class="control-label">类型</label>
                <select name="typeId" id="typeId" class="form-control">
                    <option value="">全部</option>
                    <option th:each="t:${types}" th:value="${t.id}" th:text="${t.name}"
                            th:selected="(${bill.typeId} == ${t.id})"></option>
                </select>
            </div>
            <div class="form-group">
                <label for="date1" class="control-label">开始时间</label>
                <input type="text" class="form-control" name="date1" id="date1" placeholder="开始时间" th:value="${bill.date1} ? ${#dates.format(bill.date1, 'yyyy-MM-dd')}"
                       onclick="WdatePicker()"/>
            </div>
            <div class="form-group">
                <label for="date2" class="control-label">结束时间</label>
                <input type="text" class="form-control" name="date2" id="date2" placeholder="结束时间" th:value="${bill.date2} ? ${#dates.format(bill.date2, 'yyyy-MM-dd')}"
                       onclick="WdatePicker()"/>
            </div>
            <div class="form-group">
                <input type="submit" value="查询" class="btn btn-info"/>
                &nbsp; &nbsp;
                <input type="reset" value="重置" class="btn btn-info"/>
                &nbsp; &nbsp;
                <a href="/bill/toAdd" th:href="@{/bill/toAdd}" class="btn btn-info">添加</a>
            </div>
        </form>
    </div>
    <br/>
    <div class="with:80%">
        <table class="table table-striped table-bordered">
            <thead>
            <tr>
                <th>#</th>
                <th>标题</th>
                <th>时间</th>
                <th>金额</th>
                <th>类别</th>
                <th>说明</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="b, bstatus : ${page.list}" th:style="${bstatus.odd} ? 'background-color:#A3C6C8'">
                <th scope="row" th:text="${bill.id}">id</th>
                <td th:text="${b.title}">name</td>
                <td th:text="${b.billTime} ? ${#dates.format(b.billTime, 'yyyy-MM-dd')}">time</td>
                <td th:text="${b.price}">price</td>
                <td th:text="${b.typeName}">typeName</td>
                <td th:text="${b.explain}">explain</td>
                <td>
                    <a th:href="|/bill/toUpdate/${b.id}|">修改</a>
                    <a th:href="|/bill/download/${b.id}|">下载</a>
                </td>
            </tr>
            </tbody>
        </table>
    </div>
    <ul class="pagination">
        <li>
            <button class="btn btn-default" id="first">第一页</button>
        </li>
        <li>
            <button class="btn btn-default" id="prev">上一页</button>
        </li>
        <li th:each="p:${page.navigatepageNums}">
            <button class="btn btn-default" name="pn" th:text="${p}" th:disabled="(${p} == ${page.pageNum})"></button>
        </li>
        <li>
            <button class="btn btn-default" id="next">下一页</button>
        </li>
        <li>
            <button class="btn btn-default" id="last">最后页</button>
        </li>
    </ul>
    <script th:inline="javascript">
        /*<![CDATA[*/
        $(function () {
            //初始化变量
            var pageNum = [[${page.pageNum}]]; //当前页
            var pageCount = [[${page.pages}]];//最后页
            var hasNextPage = [[${page.hasNextPage}]];//还有下一页
            var hasPreviousPage = [[${page.hasPreviousPage}]];//还有上一页
            $("#next").click(function () {
                $("#pageNum").val(pageNum + 1);
                $("#qf").submit();
            });
            $("#prev").click(function () {
                $("#pageNum").val(pageNum - 1);
                $("#qf").submit();
            });
            $("#first").click(function () {
                $("#pageNum").val(1);
                $("#qf").submit();
            });
            $("#last").click(function () {
                $("#pageNum").val(pageCount);
                $("#qf").submit();
            });
            //没有上一页
            if (!hasPreviousPage) {
                $("#prev").prop("disabled", true);
                $("#first").prop("disabled", true);
            }
    
            //没有下一页
            if (!hasNextPage) {
                $("#next").prop("disabled", true);
                $("#last").prop("disabled", true);
            }
    
            $("button[name='pn']").click(function () {
                $("#pageNum").val($(this).html());
                $("#qf").submit();
            });
        });
        /*]]>*/
    </script>
    </body>
    </html>
    

    几个注意点:
    1、如果不引入lombok那么实体类需要get和set,否则会报错,而引入lombok则需要添加@Data注解
    2、如果使用通用的mapper方法,也就是说要使用tk-mybatis,那么application的mapperScan则需要import tk.mybatis.spring.annotation.MapperScan; 引入错误的话,会在那么通用方法调用哪里报错
    3、项目JDK依赖修改
    项目Maven修改为本地
    image.png
    image.png
    image.png