springboot_demo

本文最后更新于:2 年前

springboot demo

使用springboot整合SSMP

1.项目需求

项目最终实现的页面

image-20220417232613669

​ 整体案例中需要采用的技术如下:

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

2.创建springboot的web项目

新建模块

File->new->module

image-20220417214418622

这里没什么需要改的,只需要修改使用的java版本

image-20220417214706594

选择所需要的依赖,选择后springboot就可以自动导入相关坐标,无需手动导入

image-20220417214851951

==注意将配置文件改为yml类型==

application.yml

# 修改配置将tomcat的端口改为80
server: 
	port: 80

3.开发流程

实体类开发

数据库资源

-- ----------------------------
-- Table structure for tbl_book
-- ----------------------------
DROP TABLE IF EXISTS `tbl_book`;
CREATE TABLE `tbl_book`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 51 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of tbl_book
-- ----------------------------
INSERT INTO `tbl_book` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');
INSERT INTO `tbl_book` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想');
INSERT INTO `tbl_book` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');
INSERT INTO `tbl_book` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');
INSERT INTO `tbl_book` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');
INSERT INTO `tbl_book` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新');
INSERT INTO `tbl_book` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖');
INSERT INTO `tbl_book` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉');
INSERT INTO `tbl_book` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术');
INSERT INTO `tbl_book` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO `tbl_book` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');
INSERT INTO `tbl_book` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');

tbl_book对应的实体类如下

import lombok.Data;
@Data
// Data注解可以可以完成所有属性的setter getter toString,equals,hashCode方法
public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;
}

为了方便开发使用lombok注解,在pom.xml添加坐标

<dependencies>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

数据层开发-CRUD

(1) 导入mybatisplus、数据源、mysql驱动的坐标

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

(2)在application.xml中配置数据源信息


spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/javastudy?serverTimezone=UTC
      username: root
      password: root

==若连接数据库出现时区错误时可以加上参数?serverTimezone=UTC==

(3)使用MyBatisPlus的标准通用接口BaseMapper加速开发

@Mapper
public interface BookDao extends BaseMapper<Book> {
}

==注意==

对数据库操作时需要指定数据表的前缀,并设置主键增加的策略,这里使用自增

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_		#设置表名通用前缀
      id-type: auto				#设置主键id字段的生成策略为参照数据库设定的策略,当前数据库设置id生成策略为自增

设置mybatisPlus的日志显示格式

在进行数据层测试的时候,因为基础的CRUD操作均由MyBatisPlus给我们提供了,所以就出现了一个局面,开发者不需要书写SQL语句了,这样程序运行的时候总有一种感觉,一切的一切都是黑盒的,作为开发者我们啥也不知道就完了。如果程序正常运行还好,如果报错了,这个时候就很崩溃,你甚至都不知道从何下手,因为传递参数、封装SQL语句这些操作完全不是你开发出来的,所以查看执行期运行的SQL语句就成为当务之急。

可以添加日志配置

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_
      id-type: auto
  configuration: # 设置mybatisplus的日志为标准输入格式 可以显示执行的sql语句、携带的参数与查询结果
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

数据层开发-分页功能

Mybatis-plus中已经实现了分页功能的API,具体使用方法如下

  • 创建Page对象,利用构造方法实例化所需的参数currentPage,pageSize
  • 使用Mapper.selectPage()使用分页查询功能
IPage page = new Page(2,5);
IPage page = bookDao.selectPage(page, null);

使用分页查询返回的结果仍是Page类型,其中包含多条数据,并且使用返回的结果可以获取到分页的详细信息,包括当前页,总记录数,当前页记录数,前一页等

@Test
void testGetPage(){
    IPage page = new Page(2,5);
    bookDao.selectPage(page, null);
    System.out.println(page.getCurrent());		//当前页码值
    System.out.println(page.getSize());			//每页显示数
    System.out.println(page.getTotal());		//数据总量
    System.out.println(page.getPages());		//总页数
    System.out.println(page.getRecords());		//详细数据
}

但是在使用分页功能时需要设置一个拦截器,基础操作中有查询全部的功能,而在这个基础上只需要升级一下(PLUS)就可以得到分页操作。所以MyBatisPlus将分页操作做成了一个开关,你用分页功能就把开关开启,不用就不需要开启这个开关。而我们现在没有开启这个开关,所以分页操作是没有的。这个开关是通过MyBatisPlus的拦截器的形式存在的。

@Configuration
public class MPConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        
       // 当需要其他的拦截器时继续addInnerInterceptor即可
       // 如果后期开发其他功能,需要添加全新的拦截器,按照第二行的格式继续add进去新的拦截器就可以了。
        return interceptor;
    }
}

数据层开发-条件查询

MyBatisPlus将这些操作都制作成API接口,调用一个又一个的方法就可以实现各种条件的拼装。使用方法如下

@Test
void testGetBy(){
    QueryWrapper<Book> qw = new QueryWrapper<>();
    qw.like("name","Spring");
    bookDao.selectList(qw);
}

其中第一句QueryWrapper对象是一个用于封装查询条件的对象,该对象可以动态使用API调用的方法添加条件,最终转化成对应的SQL语句。第二句就是一个条件了,需要什么条件,使用QueryWapper对象直接调用对应操作即可。上述代码中执行的SQL语句为select * from tbl_book where name like '%spring%',MP自动对查询条件进行处理,省去了手动处理的一步。

MyBatisPlus针对字段检查进行了功能升级,全面支持Lambda表达式,就有了下面这组API。由QueryWrapper对象升级为LambdaQueryWrapper对象,其使用方法如下:

@Test
void testGetBy2(){
    String name = "1";
    LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
    lqw.like(Book::getName,name);
    bookDao.selectList(lqw);
}

为了便于开发者动态拼写SQL,防止将null数据作为条件使用,MyBatisPlus还提供了动态拼装SQL的快捷书写方式。

@Test
void testGetBy2(){
    String name = "1";
    LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
    //if(name != null) lqw.like(Book::getName,name);		//方式一:JAVA代码控制
    lqw.like(name != null,Book::getName,name);				//方式二:API接口提供控制开关
    bookDao.selectList(lqw);
}

业务层开发

定义业务层的接口BookService

package com.sunzy.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.sunzy.domain.Book;

import java.util.List;

public interface BookService {
    boolean save(Book book);
    boolean update(Book book);
    boolean delete(Integer id);

    List<Book> getAll();
    Book getById(Integer id);

    IPage<Book> getPage(int currentPage, int pageSize);

}

业务层接口的实现类

package com.sunzy.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunzy.dao.BookDao;
import com.sunzy.domain.Book;
import com.sunzy.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

// 注册为数据逻辑层的bean
@Service
public class BookServiceImpl2 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 List<Book> getAll() {
        return bookDao.selectList(null);
    }

    @Override
    public Book getById(Integer id) {
        return bookDao.selectById(id);
    }

    @Override
    public IPage<Book> getPage(int currentPage, int pageSize) {
        IPage<Book> page = new Page<>(currentPage, pageSize);
        return bookDao.selectPage(page, null);
    }
}

使用mybatisplus的自带接口快速开发

业务层接口快速开发

public interface IBookService extends IService<Book> {
    //添加非通用操作API接口
}

​ 业务层接口实现类快速开发,关注继承的类需要传入两个泛型,一个是数据层接口,另一个是实体类。

@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
    @Autowired
    private BookDao bookDao;
	//添加非通用操作API
}

​ 如果感觉MyBatisPlus提供的功能不足以支撑你的使用需要(其实是一定不能支撑的,因为需求不可能是通用的),在原始接口基础上接着定义新的API接口就行了,此处不再说太多了,就是自定义自己的操作了,但是不要和已有的API接口名冲突即可。

总结

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

表现层开发

表现层接口如下:

package com.sunzy.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunzy.domain.Book;
import com.sunzy.service.IBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;

//使用restful接口约束
@RestController
@RequestMapping("/books")
public class BookController2 {

    @Autowired
    private IBookService service;

    @GetMapping
    public List<Book> getAll(){
        return service.list();
    }


    @PostMapping
    public boolean save(@RequestBody Book book){

        System.out.println(book);
        return service.save(book);
    }

    @PutMapping
    public boolean update(@RequestBody Book book){
        LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Book::getId, book.getId());
        return service.update(book,wrapper);
    }

    @DeleteMapping("{id}")
    public boolean delete(@PathVariable int id){
        return service.removeById(id);
    }

    @GetMapping("{id}")
    public Book getById(@PathVariable int id ){
        return service.getById(id);
    }

    @GetMapping("{currentPage}/{pageSize}")
    public IPage<Book> getPage(@PathVariable int currentPage, @PathVariable int pageSize){
        Page<Book> page = new Page<>(currentPage, pageSize);
        return service.page(page);
    }
}

使用postman测试各个接口是否可以正常工作

getById功能

image-20220418231257883

分页功能

image-20220418231337888

save()功能

image-20220418231557326

删除功能delete()

image-20220418232140801

==消息一致性处理==

@Data
public class R {
    private Boolean flag;   //表示本次请求是否成功
    private Object data;    //本次请求返回的数据
}

使用消息一致处理后获取的数据格式

{
    "flag"true,
    "data":{
        "id"1,
        "type""计算机理论",
        "name""Spring实战 第5版",
        "description""Spring入门经典教程"
    }
}

修改表现层代码

package com.sunzy.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunzy.controller.utils.R;
import com.sunzy.domain.Book;
import com.sunzy.service.IBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private IBookService service;

    @GetMapping("{currentPage}/{pageSize}")
    public R getAll(@PathVariable int currentPage, @PathVariable int pageSize, Book book){
        IPage<Book> bookIPage = service.getAll(currentPage, pageSize, book);
        if(currentPage > bookIPage.getPages()) {
            bookIPage = service.getAll((int) bookIPage.getPages(), pageSize, book);
        }
        return new R(null != bookIPage, bookIPage);
    }


    @PostMapping
    public R save(@RequestBody Book book){
        return new R(service.save(book));
    }

    @PutMapping
    public R update(@RequestBody Book book){
        LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Book::getId, book.getId());
        return new R(service.update(book,wrapper));
    }

    @DeleteMapping("{id}")
    public R delete(@PathVariable int id){
        return new R(service.removeById(id));
    }

    @GetMapping("{id}")
    public R getById(@PathVariable int id ){
        return new R(true, service.getById(id));
    }

//    @GetMapping("{currentPage}/{pageSize}")
//    public R getPage(@PathVariable int currentPage, @PathVariable int pageSize){
//        Page<Book> ipage = new Page<>(currentPage, pageSize);
//        Page<Book> page = service.page(ipage);
//
//        if(currentPage > page.getCurrent()){
//            ipage.setCurrent(page.getPages());
//            page = service.page(ipage);
//        }
//        return new R(true, page);
//    }
}

前端页面代码

分页查询所有数据和条件查询

页面数据模型定义

data:{
	dataList: [],              //当前页要展示的列表数据
	pagination: {              //分页相关模型数据
                currentPage: 1,//当前页码
                pageSize:10,   //每页显示的记录数
                total:0,       //总记录数
                name:'',
                type:'',
                description:''
    }
}

异步请求代码

getAll() {
    param = "?name=" + this.pagination.name;
    param = param + "&type=" + this.pagination.type;
    param = param + "&description=" + this.pagination.description;

    // console.log(param);
    axios.get("/books/"+this.pagination.currentPage + "/" + this.pagination.pageSize + param).then((res)=>{
        this.dataList = res.data.data.records;
        this.pagination.total = res.data.data.total;
        this.pagination.currentPage = res.data.data.current;
        this.pagination.pagesize = res.data.data.size;
    });
}

在钩子函数中执行该方法

//钩子函数,VUE对象初始化完成后自动执行
created() {
    this.getAll();
},

添加数据

添加数据的窗口弹出控制

数据模型

dialogFormVisible: false,//添加表单是否可见
formData

对话框控制函数

//弹出添加窗口
handleCreate() {
    this.dialogFormVisible = true;
    this.resetForm();
},

重置表单功能

//重置表单
resetForm() {
    this.formData = {};
},

添加功能

handleAdd () {
    axios.post("/books", this.formData).then((res) => {
        if(res.data.flag){
            this.dialogFormVisible = false;
            this.$message.success("添加成功!");
        }else{
            this.$message.error("添加失败!");
        }
    }).finally(() => {
        this.getAll();
    })
}

删除数据

// 删除
handleDelete(row) {
    axios.delete("/books/" + row.id).then((res)=>{
        if(res.data.flag){
            this.$message.success("删除成功!");
            this.getAll();
        }else{
            this.$message.error("删除失败!");
        }
    })
},

修改数据

//弹出编辑窗口
handleUpdate(row) {
    this.dialogFormVisible4Edit = true;
    this.formData = row;
},

//修改
handleEdit() {
    axios.put("/books", this.formData).then((res) => {
        if(res.data.flag){
            this.dialogFormVisible4Edit = false;
            this.$message.success("修改成功!");
        }else{
            this.$message.error("修改失败!");
        }
    }).finally(() => {
        this.getAll();
    })
},

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

 目录

Copyright © 2020 my blog
载入天数... 载入时分秒...