• SpringBoot2基础篇(四)—— 基于SpringBoot的SSMP整合案例


    04、基于SpringBoot的SSMP整合案例

    4.1、案例分析

    1、案例实现方案分析

    • 实体类开发————使用Lombok快速制作实体类
    • Dao开发————整合MyBatisPlus,制作数据层测试类
    • Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
    • Controller开发————基于Restful开发,使用PostMan测试接口功能
    • Controller开发————前后端开发协议制作
    • 页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
      • 列表、新增、修改、删除、分页、查询
    • 项目异常处理
    • 按条件查询————页面功能调整、Controller修正功能、Service修正功能

    2、SSMP案例制作流程解析

    • 先开发基础CRUD功能,做一层测一层
    • 调通页面,确认异步提交成功后,制作所有功能
    • 添加分页功能与查询功能

    4.2、模块创建

    1. 勾选SpringMVC与MySQL坐标

    2. 添加依赖

      <dependency>
          <groupId>com.baomidougroupId>
          <artifactId>mybatis-plus-boot-starterartifactId>
          <version>3.4.3version>
      dependency>
      
      <dependency>
          <groupId>com.alibabagroupId>
          <artifactId>druid-spring-boot-starterartifactId>
          <version>1.2.6version>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    3. 修改配置文件为yml格式

    4. 设置端口为80方便访问

      server:
        port: 80
      
      • 1
      • 2

    4.3、实体类开发

    • Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发
    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:

    lombok版本由SpringBoot提供,无需指定版本

    • 常用注解:@Data
    @Data
    public class Book {
        private Integer id;
        private String type;
        private String name;
        private String description;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:

    • @Data为当前实体类在编译期设置对应的get/set方法,toString方法,hashCode方法,equals方法等
    • 但是不提供构造器方法

    4.4、数据层开发

    1、技术实现方案

    • MyBatis-Plus
    • Druid

    2、导入MyBatis-PlusDruid对应的starter

    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-boot-starterartifactId>
        <version>3.4.3version>
    dependency>
    
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>druid-spring-boot-starterartifactId>
        <version>1.2.6version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3、配置数据源与MyBatisPlus对应的基础配置(id生成策略使用数据库自增策略)

    server:
      port: 80
      
    spring:
      datasource:
        druid:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
          username: root
          password: root
    
    mybatis-plus:
      global-config:
        db-config:
          table-prefix: tbl_
          id-type: auto
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4、继承BaseMapper并指定泛型

    @Mapper
    public interface BookDao extends BaseMapper<Book> {
    
    }
    
    • 1
    • 2
    • 3
    • 4

    5、制作测试类测试结果(基本CRUD)

    @SpringBootTest
    public class BookDaoTestCase {
        @Autowired
        private BookDao bookDao;
    
        /**
         * 查询单个数据
         */
        @Test
        void testGetById() {
            System.out.println(bookDao.selectById(1));
        }
    
        /**
         * 添加一条数据
         */
        @Test
        void testSave() {
            Book book = new Book();
            book.setType("测试数据123");
            book.setName("测试数据123");
            book.setDescription("测试数据123");
            bookDao.insert(book);
        }
    
        /**
         *更新一条数据
         */
        @Test
        void testUpdate() {
            Book book = new Book();
            book.setId(6);
            book.setType("测试数据abc");
            book.setName("测试数据abc");
            book.setDescription("测试数据abc");
            bookDao.updateById(book);
        }
    
        /**
         * 删除一条数据
         */
        @Test
        void testDelete() {
            bookDao.deleteById(7);
        }
    
        /**
         * 查询所有数据
         */
        @Test
        void testGetAll() {
            List<Book> books = bookDao.selectList(null);
            books.forEach(System.out::println);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    6、开启MyBatis-Plus运行日志

    • 为方便调试可以开启MyBatis-Plus的日志
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3

    使用配置方式开启日志,设置日志输出方式为标准输出

    7、分页功能

    ①分页操作需要设定分页对象IPage

    @Test
    void testPage() {
        IPage page = new Page(2,2);
        bookDao.selectPage(page, null);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    IPage对象中封装了分页操作中的所有数据

    在这里插入图片描述

    ③分页操作是在MyBatis-Plus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能, 使用MyBatis-Plus拦截器实现

    @Configuration
    public class MPConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            //定义Mp拦截器
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //添加用于分页的拦截器
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    8、按条件查询

    使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用

    @Test
    void testGetBy() {
        //SELECT id,type,name,description FROM tbl_book WHERE (name LIKE ?)
        QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name", "Spring");
        bookDao.selectList(queryWrapper);
    }
    
    @Test
    void testGetBy2() {
        String name = "1";
        LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
        /*if (name != null) {
            queryWrapper.like(Book::getName, name);
        }*/
        queryWrapper.le(name != null, Book::getName, name);
        bookDao.selectList(queryWrapper);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    小结:

    ①使用QueryWrapper对象封装查询条件

    ②推荐使用LambdaQueryWrapper对象

    ③所有查询操作封装成方法调用

    ④查询条件支持动态条件拼装

    4.5、业务层开发

    1、Service层接口定义与数据层接口定义具有较大区别,不要混用

    • selectByUserNameAndPassword(String username,String password);
    • login(String username,String password);

    2、接口定义

    public interface BookService {
        Boolean save(Book book);
        Boolean update(Book book);
        Boolean delete(Integer id);
        Book getById(Integer id);
        List<Book> getAll();
        IPage<Book> getPage(Integer currentPage, int pageSize);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、实现类定义

    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
    
        @Override
        public Boolean save(Book book) {
            return bookDao.insert(book) > 0;
        }
    
        @Override
        public Boolean update(Book book) {
            return bookDao.updateById(book) > 0;
        }
    
        @Override
        public Boolean delete(Integer id) {
            return bookDao.deleteById(id) > 0;
        }
    
        @Override
        public Book getById(Integer id) {
            return bookDao.selectById(id);
        }
    
        @Override
        public List<Book> getAll() {
            return bookDao.selectList(null);
        }
    
        @Override
        public IPage<Book> getPage(Integer currentPage, int pageSize) {
            IPage page = new Page(currentPage, pageSize);
            return bookDao.selectPage(page, null);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    4、测试类定义

    @SpringBootTest
    public class BookServiceTestCase {
        @Autowired
        private BookService bookService;
    
        @Test
        void testGetById() {
            System.out.println(bookService.getById(3));
        }
    
        @Test
        void testSave() {
            Book book = new Book();
            book.setType("测试数据123");
            book.setName("测试数据123");
            book.setDescription("测试数据123");
            bookService.save(book);
        }
        
        @Test
        void testUpdate() {
            Book book = new Book();
            book.setId(6);
            book.setType("测试数据update");
            book.setName("测试数据abc");
            book.setDescription("测试数据abc");
            bookService.update(book);
        }
    
        @Test
        void testDelete() {
            bookService.delete(8);
        }
        
        @Test
        void testGetAll() {
            List<Book> books = bookService.getAll();
            books.forEach(System.out::println);
        }
    
        @Test
        void testPage() {
            IPage<Book> page = bookService.getPage(2, 2);
            System.out.println(page.getCurrent());
            System.out.println(page.getPages());
            System.out.println(page.getRecords());
            System.out.println(page.getTotal());
            System.out.println(page.getSize());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    5、快速开发

    快速开发方案:

    • 使用MyBatis-Plus提供有业务层通用接口(ISerivce)与业务层通用实现类(ServiceImpl
    • 在通用类基础上做功能重载或功能追加
    • 注意重载时不要覆盖原始操作,避免原始提供的功能丢失

    在这里插入图片描述

    ①接口定义

    public interface IBookService extends IService<Book> {
    }
    
    • 1
    • 2

    ②实现类定义

    @Service
    public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
    }
    
    • 1
    • 2
    • 3

    ③实现类追加功能

    @Service
    public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
    
        @Autowired
        private BookDao bookDao;
    
        @Override
        public boolean saveBook(Book book) {
            return bookDao.insert(book) > 0;
        }
    
        @Override
        public boolean modifyBook(Book book) {
            return bookDao.updateById(book) > 0;
        }
    
        @Override
        public boolean deleteBook(Integer id) {
            return bookDao.deleteById(id) > 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    小结:

    1. 使用通用接口(ISerivce)快速开发Service
    2. 使用通用实现类(ServiceImpl)快速开发ServiceImpl
    3. 可以在通用接口基础上做功能重载或功能追加
    4. 注意重载时不要覆盖原始操作,避免原始提供的功能丢失

    4.6、表现层开发

    • 基于Restful进行表现层接口开发
    • 使用Postman测试表现层接口功能

    1、功能测试

    @RestController
    @RequestMapping("/books")
    public class BookController {
        @Autowired
        private IBookService bookService;
    
        @GetMapping
        public List<Book> getAll() {
            return bookService.list();
        }
    
        @PostMapping
        public Boolean save(@RequestBody Book book) {
            return bookService.save(book);
        }
    
        @PutMapping
        public Boolean update(@RequestBody Book book) {
            return bookService.modifyBook(book);
        }
    
        @DeleteMapping("{id}")
        public Boolean delete(@PathVariable Integer id) {
            return bookService.deleteBook(id);
        }
    
        @GetMapping("{id}")
        public Book getById(@PathVariable Integer id) {
            return bookService.getById(id);
        }
    
        @GetMapping("{currentPage}/{pageSize}")
        public IPage<Book> getPage(@PathVariable int currentPage, @PathVariable int pageSize) {
            return bookService.getPage(currentPage, pageSize);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    小结

    ①基于Restful制作表现层接口

    • 新增:POST

    • 删除:DELETE

    • 修改:PUT

    • 查询:GET

    ②接收参数

    • 实体数据:@RequestBody
    • 路径变量:@PathVariable

    2、表现层消息一致性处理

    在这里插入图片描述

    • 设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
    @Data
    public class R {
        private Boolean flag;
        private Object data;
    
        public R() {
    
        }
    
        public R(Boolean flag) {
            this.flag = flag;
        }
    
        public R(Boolean flag, Object data) {
            this.flag = flag;
            this.data = data;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 表现层接口统一返回值类型结果
    @RestController
    @RequestMapping("/books")
    public class BookController {
        @Autowired
        private IBookService bookService;
    
        @GetMapping
        public R getAll() {
            return new R(true, bookService.list());
        }
    
        @PostMapping
        public R save(@RequestBody Book book) {
            return new R(bookService.save(book));
        }
    
        @PutMapping
        public R update(@RequestBody Book book) {
            return new R(bookService.modifyBook(book));
        }
    
        @DeleteMapping("{id}")
        public R delete(@PathVariable Integer id) {
            return new R(bookService.deleteBook(id));
        }
    
        @GetMapping("{id}")
        public R getById(@PathVariable Integer id) {
            return new R(true, bookService.getById(id));
        }
    
        @GetMapping("{currentPage}/{pageSize}")
        public R getPage(@PathVariable int currentPage, @PathVariable int pageSize) {
            return new R(true, bookService.getPage(currentPage, pageSize));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    小结:

    1. 设计统一的返回值结果类型便于前端开发读取数据
    2. 返回值结果类型可以根据需求自行设定,没有固定格式
    3. 返回值结果模型类用于后端与前端进行数据格式统一,也称为前后端数据协议

    4.7、前后端协议联调

    • 前后端分离结构设计中页面归属前端服务器
    • 单体工程中页面放置在resources目录下的static目录中(建议执行clean

    4.7.1、前后端联通

    • 前端发送异步请求,调用后端接口
    //列表
    getAll() {
        //发送异步请求
        axios.get("/books").then((res)=>{
            console.log(res.data);
        })
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    小结:

    ①单体项目中页面放置在resources/static目录下

    created钩子函数用于初始化页面时发起调用

    ③页面使用axios发送异步请求获取数据后确认前后端是否联通

    4.7.2、列表功能

    //列表
    getAll() {
        //发送异步请求
        axios.get("/books").then((res)=>{
            //console.log(res.data);
            this.dataList = res.data.data;
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    将查询数据返回到页面,利用前端数据双向绑定进行数据展示

    4.7.3、添加功能

    ① 弹出添加窗口

    //弹出添加窗口
    handleCreate() {
        this.dialogFormVisible = true;
        this.resetForm();
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ② 清除数据

    //重置表单
    resetForm() {
        this.formData = {};
    },
    
    • 1
    • 2
    • 3
    • 4

    ③添加

    //添加
    handleAdd () {
        axios.post("/books",this.formData).then((res)=>{
            //判断当前操作是否成功
            if (res.data.flag) {
                //1.关闭弹层
                this.dialogFormVisible = false;
                this.$message.success("添加成功");
            } else {
                this.$message.error("添加失败");
            }
        }).finally(()=>{
            //2.重新加载数据
            this.getAll();
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ④取消添加

    //取消
    cancel(){
        this.dialogFormVisible = false;
        this.$message.info("当前操作取消");
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5

    小结:

    1. 请求方式使用POST调用后台对应操作
    2. 添加操作结束后动态刷新页面加载数据
    3. 根据操作结果不同,显示对应的提示信息
    4. 弹出添加Div时清除表单数据

    4.7.4、删除功能

    • 删除
    // 删除
    handleDelete(row) {
        //console.log(row);
        this.$confirm("此操作永久删除当前信息,是否继续?","提示",{type:"info"}).then(()=>{
            axios.delete("/books/"+row.id).then((res)=>{
                //判断当前操作是否成功
                if (res.data.flag) {
                    this.$message.success("删除成功");
                } else {
                    this.$message.error("数据同步失败,自动刷新");
                }
            }).finally(()=>{
                //2.重新加载数据
                this.getAll();
            });
        }).catch(()=>{
            this.$message.info("取消操作");
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    小结:

    1. 请求方式使用Delete调用后台对应操作
    2. 删除操作需要传递当前行数据对应的id值到后台
    3. 删除操作结束后动态刷新页面加载数据
    4. 根据操作结果不同,显示对应的提示信息
    5. 删除操作前弹出提示框避免误操作

    4.7.5、修改功能

    1、弹出修改窗口

    //弹出编辑窗口
    handleUpdate(row) {
        axios.get("/books/" + row.id).then((res)=>{
            if (res.data.flag && res.data.data != null) {
                this.dialogFormVisible4Edit = true;
                this.formData = res.data.data;
            } else {
                this.$message.error("数据同步失败,自动刷新");
            }
        }).finally(()=>{
            this.getAll();
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    小结:

    1. 加载要修改数据通过传递当前行数据对应的id值到后台查询数据
    2. 利用前端数据双向绑定将查询到的数据进行回显

    2、修改

    //修改
    handleEdit() {
        axios.put("/books",this.formData).then((res)=>{
            //判断当前操作是否成功
            if (res.data.flag) {
                //1.关闭弹层
                this.dialogFormVisible4Edit = false;
                this.$message.success("修改成功");
            } else {
                this.$message.error("修改失败");
            }
        }).finally(()=>{
            //2.重新加载数据
            this.getAll();
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3、取消添加和修改

    //取消
    cancel(){
        this.dialogFormVisible = false;
        this.dialogFormVisible4Edit = false;
        this.$message.info("当前操作取消");
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    小结:

    1. 请求方式使用PUT调用后台对应操作
    2. 修改操作结束后动态刷新页面加载数据(同新增)
    3. 根据操作结果不同,显示对应的提示信息(同新增)

    4.7.6、异常消息处理(业务消息一致性处理)

    • 业务操作成功或失败返回数据格式

    在这里插入图片描述

    • 后台代码Bug导致数据格式不统一性

    在这里插入图片描述

    • 对异常进行统一处理,出现异常后,返回指定信息
    //作为SpringMVC的异常处理
    //@ControllerAdvice
    @RestControllerAdvice
    public class ProjectExceptionAdvice {
    
        //拦截所有的异常信息
        @ExceptionHandler(Exception.class)
        public R doException(Exception ex) {
            //记录日志
            //通知运维
            //通知开发
            ex.printStackTrace();
            return new R("服务器故障,请稍后再试");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 修改表现层返回结果的模型类,封装出现异常后对应的信息
      • flag:false
      • Data: null
      • 消息(message): 要显示信息
    @Data
    public class R {
        private Boolean flag;
        private Object data;
        private String message;
    
        public R() {
    
        }
    
        public R(Boolean flag) {
            this.flag = flag;
        }
    
        public R(Boolean flag, Object data) {
            this.flag = flag;
            this.data = data;
        }
    
        public R(Boolean flag, String message) {
            this.flag = flag;
            this.message = message;
        }
    
        public R(String message) {
            this.flag = false;
            this.message = message;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 页面消息处理,没有传递消息加载默认消息,传递消息后加载指定消息
    //添加
    handleAdd () {
        axios.post("/books",this.formData).then((res)=>{
            //判断当前操作是否成功
            if (res.data.flag) {
                //1.关闭弹层
                this.dialogFormVisible = false;
                this.$message.success(res.data.message);
            } else {
                this.$message.error(res.data.message);
            }
        }).finally(()=>{
            //2.重新加载数据
            this.getAll();
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 可以在表现层Controller中进行消息统一处理
      • 目的:国际化
    @PostMapping
    public R save(@RequestBody Book book)  throws IOException{
        if (book.getName().equals("123")) throw new IOException();
        boolean flag = bookService.save(book);
        return new R(flag, flag ? "添加成功^_^":"添加失败-_-!");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    小结:

    1. 使用注解@RestControllerAdvice定义SpringMVC异常处理器用来处理异常的
    2. 异常处理器必须被扫描加载,否则无法生效
    3. 表现层返回结果的模型类中添加消息属性用来传递消息到页 面

    4.7.7、分页

    1、页面使用el分页组件添加分页功能

    
    <div class="pagination-container">
        <el-pagination
                class="pagiantion"
    
                @current-change="handleCurrentChange"
    
                :current-page="pagination.currentPage"
    
                :page-size="pagination.pageSize"
    
                layout="total, prev, pager, next, jumper"
    
                :total="pagination.total">
    
        el-pagination>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2、定义分页组件需要使用的数据并将数据绑定到分页组件

    data:{
        pagination: {//分页相关模型数据
            currentPage: 1,//当前页码
            pageSize:4,//每页显示的记录数
            total:0,//总记录数
        }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、分页查询

    @GetMapping("{currentPage}/{pageSize}")
    public R getPage(@PathVariable int currentPage, @PathVariable int pageSize) {
        return new R(true, bookService.getPage(currentPage, pageSize));
    }
    
    • 1
    • 2
    • 3
    • 4

    4、替换查询全部功能为分页功能,加载分页数据

    //分页
    getAll() {
        axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res)=>{
            this.pagination.pageSize = res.data.data.size;
            this.pagination.currentPage = res.data.data.current;
            this.pagination.total = res.data.data.total;
    
            this.dataList = res.data.data.records;
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5、切换页码

    //切换页码
    handleCurrentChange(currentPage) {
        //修改页码值为当前选中页码值
        this.pagination.currentPage = currentPage;
        //执行查询
        this.getAll();
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    小结:

    1. 使用el分页组件
    2. 定义分页组件绑定的数据模型
    3. 异步调用获取分页数据
    4. 分页数据页面回显

    6、删除最后一页的唯一一条数据,会出现以下Bug

    在这里插入图片描述

    删除功能维护:

    • 对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询
    @GetMapping("{currentPage}/{pageSize}")
    public R getPage(@PathVariable int currentPage, @PathVariable int pageSize) {
        IPage<Book> page = bookService.getPage(currentPage, pageSize);
        //如果当前页码值大于总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
        if (currentPage > page.getPages()) {
            page = bookService.getPage((int)page.getPages(), pageSize);
        }
        return new R(true, page);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.7.8、条件查询

    1、查询条件数据封装

    • 单独封装
    • 与分页操作混合封装
    pagination: {//分页相关模型数据
        currentPage: 1,//当前页码
        pageSize:4,//每页显示的记录数
        total:0,//总记录数
        type: "",
        name: "",
        description: ""
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、页面数据模型绑定

    <div class="filter-container">
        <el-input placeholder="图书类别" v-model="pagination.type" style="width: 200px;" class="filter-item">el-input>
        <el-input placeholder="图书名称" v-model="pagination.name" style="width: 200px;" class="filter-item">el-input>
        <el-input placeholder="图书描述" v-model="pagination.description" style="width: 200px;" class="filter-item">el-input>
        <el-button @click="getAll()" class="dalfBut">查询el-button>
        <el-button type="primary" class="butT" @click="handleCreate()">新建el-button>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、组织数据成为get请求发送的数据

    //分页
    getAll() {
        //组织参数,拼接url请求地址
        param = "?type=" + this.pagination.type;
        param += "&name=" + this.pagination.name;
        param += "&description=" + this.pagination.description;
        console.log(param);
        axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize + param).then((res)=>{
            this.pagination.pageSize = res.data.data.size;
            this.pagination.currentPage = res.data.data.current;
            this.pagination.total = res.data.data.total;
    
            this.dataList = res.data.data.records;
        });
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4、Controller接收参数

    @GetMapping("{currentPage}/{pageSize}")
    public R getPage(@PathVariable int currentPage, @PathVariable int pageSize, Book book) {
        System.out.println("参数==>" + book);
        IPage<Book> page = bookService.getPage(currentPage, pageSize, book);
        //如果当前页码值大于总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值
        if (currentPage > page.getPages()) {
            page = bookService.getPage((int)page.getPages(), pageSize, book);
        }
        return new R(true, page);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5、业务层接口功能开发

    @Override
    public IPage<Book> getPage(int currentPage, int pageSize, Book book) {
        LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<Book>();
        queryWrapper.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType())
                .like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName())
                .like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
        IPage page = new Page(currentPage, pageSize);
        bookDao.selectPage(page, queryWrapper);
        return page;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

  • 相关阅读:
    【JQuery】JQuery入门——JQuery 插件-validation
    【MYSQL】表的内外连接
    清新毕业答辩PPT模板
    基于AM335X开发板 (ARM Cortex-A8)——Linux系统使用手册 (下)
    基于阿里云GPU云服务器的AIACC助力UC搜索业务性能提效380%,每年节省数千万成本
    C++11新特性② | 左值、左值引用、右值与右值引用
    Pyecharts教程(二):使用pyecharts绘制3D散点图——以营养元素为例
    Acwing第 67 场周赛
    mvc-servlet
    从0开始学习JavaScript--深入探究JavaScript类型化数组
  • 原文地址:https://blog.csdn.net/kuaixiao0217/article/details/126232235