SpringBoot

本文档详细介绍了使用SpringBoot和MyBatisPlus构建讲师管理模块的全过程,涵盖MyBatisPlus介绍、工程搭建、Swagger2整合、主键策略、统一返回结果集、乐观锁、逻辑删除、分页查询以及统一异常处理等内容,旨在帮助读者掌握前后端分离的开发流程和Git使用。

SpringBoot案例

常见问题:

  • 公司使用的框架(技术)没学过
  • 公司的代码无处着手(顺着一条业务线)
  • 前后端交互的过程不太理解
  • 使用git不熟练

目标:

  • 掌握springboot+mybatisplus框架的使用
  • 了解前端框架vue (前端框架:angularjs、vue)
  • 掌握前后端如何结合,更全面把握开发流程
  • 在idea中使用git

​ 我们通过一个对讲师模块进行增删改查的功能,去实现前端代码编写、后端代码编写、前后端整合,让大家对前后端分离有个整体性的认识,并掌握git的使用方式

一、讲师模块后端

准备数据:

create  table  teacher(
   id varchar(19),# 主键
   name varchar(20), # 讲师姓名
   intro varchar(255), # 介绍
   career text, # 职业成就
   level int(10), # 水平
   sort int(10) #排序
)

1、MyBatisPlus介绍

之前使用mybaits:

​ dao:TeacherMapper接口: 定义增删改查的方法

​ 在xml配置文件中:编写对应的sql

代码的简化:像查询、修改、删除…基本的功能操作,能否有一个框架给咱们提供好了,用时直接使用即可!!

问题:mp功能上相比mybatis有没有增强?? 没有

类似:springboot 相对于spring体系中

官网:http://mp.baomidou.com/

​ 为了方便书写,我们以后简称为mp,我们此处先对mp框架进行简单的了解,在后面讲师功能实现中再结合文档具体去讲解该框架。

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

愿景:我们的愿景是成为 MyBatis 最好的搭档

特性:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

2、工程搭建&环境准备

开发springboot项目:

  • 引入坐标
  • 配置文件的编写
  • 包结构
  • 测试一下

在Idea中创建springboot工程

2.1 引入坐标

<dependencies>
    <!--mybatis-plus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--swagger-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.7.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.7.0</version>
    </dependency>
    <!--lombok用来简化实体类:需要安装lombok插件-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!--开发者工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2.2 application.properties配置文件

# 服务端口
server.port=8001
# 服务名
spring.application.name=teacher_demo
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=1234
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2.3、启动工程进程测试

3、SpringBoot整合Swagger2

使用postman工具测试

	在团队开发中,一个好的 API 文档不但可以减少大量的沟通成本,还可以帮助一位新人快速上手业务。传统的做法是由开发人员创建一份 RESTful API 文档来记录所有的接口细节,并在程序员之间代代相传。
这种做法存在以下几个问题:
	第一:API 接口众多,细节复杂,需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等,想要高质量的完成这份文档需要耗费大量的精力;
	第二:难以维护。随着需求的变更和项目的优化、推进,接口的细节在不断地演	 变,接口描述文档也需要同步修订,可是文档和代码处于两个不同的媒介,	 除非有严格的管理机制,否则很容易出现文档、接口不一致的情况

Swagger2 的出现就是为了从根本上解决上述问题。它作为一个规范和完整的框架,可以用于生成、描述、调用和可视化 RESTful 风格的 Web 服务:

1.接口文档在线自动生成,文档随接口变动实时更新,节省维护成本

2.支持在线接口测试,不依赖第三方工具

package com.ujiuye.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;

import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class Swagger2Config {

    @Bean
    public Docket webApiConfig(){

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .build();

    }

    private ApiInfo webApiInfo(){

        return new ApiInfoBuilder()
                .title("SpringBoot案例")
                .description("本文档描述了讲师服务接口定义")
                .version("1.0")
                .contact(new Contact("db", "http://ujiuye.com", "222222244@qq.com"))
                .build();
    }

}

重启服务器查看接口:

http://localhost:8001/swagger-ui.html

4、讲师添加

如何使用mp做crud操作呢?

​ 让TeacherMapper去继承BaseMapper:包括了常用的增删改查等基本操作

简化:

  • 无需手动写crud方法
  • 无需手动编写xml中的sql

主键策略:

(1) ID_WORKER

MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID

  @TableId(type = IdType.ID_WORKER_STR)
  private String id;

主键策略:

public enum IdType {
    AUTO(0),
    NONE(1),
    INPUT(2),
    ID_WORKER(3),  //默认
    UUID(4),
    ID_WORKER_STR(5);
}

全局设置主键生成策略:

mybatis-plus.global-config.db-config.id-type=xx

5、统一返回结果集

​ 项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更直观、轻松。

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。

例如,我们的系统要求返回的基本数据格式如下:

列表: 查询所有讲师

{
  "success": true,
  "code": 20000, 
  "message": "成功",
  "data": {
    "items": [
      {
        "memberId": "1",
        "mobile": "18911893513",
        "createTime": "2018-05-15 01:20:52"
      },{
        "memberId": "1",
        "mobile": "18911893513",
        "createTime": "2018-05-15 01:20:52"
      }
    ]
  }
}

分页:

{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {
    "total": 17,
    "rows": [
      {
        "memberId": "1",
        "mobile": "18911893513",
        "createTime": "2018-05-15 01:20:52"
      }
    ]
  }
}

没有返回数据:

{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {“”:””,””:””}
}

失败:

{
  "success": false,
  "code": 20001,
  "message": "失败",
  "data": {}
}

因此,我们定义统一结果:

{
 "success": 布尔, //响应是否成功
 "code": 数字, //响应码
 "message": 字符串, //返回消息
 "data": HashMap //返回数据,放在键值对
}

定义类:

// 定义状态码
public interface ResultCode {
    int OK = 20000; //成功
    int ERROR = 20001;//失败
}
//定义返回结果类
@Data
public class Result {
    private boolean success;
    private Integer code;
    private String message;
    private Map<String,Object> data = new HashMap<String,Object>();
    public static Result ok(){
        Result r = new Result();
        r.setSuccess(true);
        r.setCode(ResultCode.OK);
        r.setMessage("成功");
        return r;
    }
    public static Result error(){
        Result r = new Result();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }
    public Result message(String message){
        this.setMessage(message);
        return this;
    }
    public Result code(Integer code){
        this.setCode(code);
        return this;
    }
    public Result data(String key, Object value){
        this.data.put(key, value);
        return this;
    }
    public Result data(Map<String, Object> map){
        this.setData(map);
        return this;
    }
}

6、讲师修改

添加和修改的区别:

添加: Teacher中的信息,不需要id

修改:Teacher中的信息,需要id

6.1、根据id进行修改

6.2、自动填充

​ 之前:teacher.setCreateTime(new Date())

​ 项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。

我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作

  • 数据库中添加自动填充字段

    create_time和update_time

  • 实体类上添加注解

 	 @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
  • 实现元对象处理器接口
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

6.3、乐观锁

乐观锁概述:
      乐观锁是丢失更新问题的解决方案
丢失更新问题:
  对于一条数据,有两个事务要对这个条数据进行修改,第一个事务提交修改后,第二个事务也进行了事务提交修改,结果事务查看结果不是自己修改后的结果,明明修改了但是结果确不是自己修改的内容,这种现象就称为丢失更新问题。

主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新

乐观锁实现方式:

  • 取出记录时,获取当前version

  • 更新时,带上这个version

  • 执行更新时, set version = newVersion where version = oldVersion

  • 如果version不对,就更新失败

实现:

(1) 数据库中添加version字段

(2) 实体类添加version属性并加@Version注解

@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;

(3) 元对象处理器接口添加version的insert默认值

this.setFieldValByName("version",1,metaObject);

(4) 在 TeacherConfig中注册 Bean

/**
     * 乐观锁插件
     */
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}

7、讲师删除

逻辑删除:

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据

  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

实现步骤:

(1)数据库中添加 deleted字段

(2)实体类添加deleted属性

@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;

(3)元对象处理器接口添加deleted的insert默认值

this.setFieldValByName("deleted",0,metaObject);

(4)application.properties 加入配置

此为默认值,如果你的默认值和mp默认的一样,该配置可无

Mp中默认0表示未被删除,可获取

​ 1表示已被删除,无法获取

mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

(5)在 TeacherConfig 中注册 Bean

 @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }

8、讲师查询

8.1、查询所有

8.2、分页查询

引入分页插件

 /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

8.3、分页条件查询

Controller:

 //分页条件查询
    @PostMapping("{page}/{limit}")
    public Result findByPage(@PathVariable Integer page,@PathVariable
                             Integer limit,@RequestBody TeacherQuery query){
        Page<Teacher> pages = new Page<>(page, limit);
        teacherService.pageQuery(pages,query);
        //获取数据
        List<Teacher> list = pages.getRecords();
        //获取总记录数
        long total = pages.getTotal();
        System.out.println("总数:"+total);
        return Result.ok().data("rows",list).data("total",total);
    }

Service:

@Override
    public void pageQuery(Page<Teacher> pageParam, TeacherQuery query){
        QueryWrapper<Teacher> wrapper = new QueryWrapper<>();
        wrapper.orderByAsc("sort");
        //判断
        if(query == null){
            baseMapper.selectPage(pageParam,null);
            return;
        }
        //获取值
        String name = query.getName();
        Integer level = query.getLevel();
        String begin = query.getBegin();
        String end = query.getEnd();

        if(!StringUtils.isEmpty(name)){
            wrapper.like("name",name);
        }
        if(!StringUtils.isEmpty(level)){
            wrapper.eq("level",level);
        }
        if(!StringUtils.isEmpty(begin)){
            wrapper.ge("gmt_create",begin);
        }
        if(!StringUtils.isEmpty(end)){
            wrapper.le("gmt_create",end);
        }
        baseMapper.selectPage(pageParam,wrapper);
    }

9、统一异常处理

面试题会涉及到

9.1、全局异常处理

我们想让异常结果也统一,并且在集中的地方处理系统的异常信息,那么需要统一异常处理

teacher_demo中创建统一异常处理类:

/**
 * 统一异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.error().message("执行出现了异常");
    }

}

注解说明:

@ControllerAdvice ,很多初学者可能都没有听说过这个注解,实际上,这是一个非常有用的注解,顾名思义,这是一个增强的 Controller。使用这个 Controller ,可以实现三个方面的功能:
全局异常处理
全局数据绑定
全局数据预处理
灵活使用这三个功能,可以帮助我们简化很多工作,需要注意的是,这是 SpringMVC 提供的功能,在 Spring Boot 中可以直接使用.
@ExceptionHandler 注解用来指明异常的处理类型,即如果这里指定为NullpointerException,则数组越界异常就不会进到这个方法中来。

9.2、特定异常处理

 //特定的异常    ArithmeticException
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public Result error(ArithmeticException e){
        e.printStackTrace();
        return Result.error().message("算术异常!!!");
    }

9.3、自定义异常处理

  // 自定义异常
    @ExceptionHandler(ZiException.class)
    @ResponseBody
    public Result error(ZiException e){
        e.printStackTrace();
        return Result.error().code(e.getCode()).message(e.getMsg());
    }

10、git在Idea中的使用

参考文档

二、讲师模块前端

重点:关注前端项目工程,看如何和后端进行交互的,以及了解以下两种技术:

介绍两种技术:

  • vue 前端框架
  • ElementUI

前端框架:angulajs

vue:

<div id="app">
  {{ message }}
  
  {{name}}
</div>
// js方式
doucument.getElementById("app").innerHTML = "内容体";
// jq方式
$("#app").html("内容体");

vue:
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!',
    name:"lucy"
  }
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值