小技巧(隐藏指定文件/文件夹)

SpringBoot每次创建时都会携带很多没啥用的文件,可以在设置中进行隐藏,不需要每次都删除

image-20230907143641453

在设置中,可以进行配置

image-20230907144054936

SpringBoot简介

  • SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
    • Spring的缺点
      • 依赖设置繁琐
      • 配置繁琐
    • SpringBoot的优点
      • 起步依赖(简化依赖配置)
      • 自动配置(简化常用工程相关配置)
      • 辅助功能(内置服务器,…)

parent

有时候做项目,可能会导入两个相同的坐标,此时可以将它合并到一个文件中去,直接调用一个文件中的内容即可,但依然不太方便,springboot给出了优化的解决

image-20230907151058582

它在pom.xml文件中继承了一个父类

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

而这里面又继承了一个父类

1
2
3
4
5
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.15</version>
</parent>

点进去查看后,发现里面有很多的依赖,这些依赖也有设置了对应的版本,当我们使用某一个依赖时,它就会自动通过场景启动器里的spring-boot-starter-parent中的spring-boot-dependencies中写入的对应依赖,来自动设置最合适的版本,这样就不会因为版本不同而出现问题

总结:

  1. 开发Springboot程序要继承spring-boot-starter-parent
  2. spring-boot-starter-parent中定义了若干个依赖管理
  3. 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
  4. 继承parent的形式也可以采用引入依赖的形式实现效果

starter

  • SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的

实际开发

  • 使用任意坐标时,仅书写GAV中的G和A,V(version)由SpringBoot提供,除非Springboot未提供对应版本的V
  • 如发生坐标错误,再指定Version(要小心版本冲突)
  1. 开发SpringBoot程序需要导入坐标时,通常导入对应的starter
  2. 每个不同的starter根据功能不同,通常包含多个依赖坐标
  3. 使用starter可以实现快速配置的效果,达到简化配置的目的

引导类

启动方式

1
2
3
4
5
6
7
8
@SpringBootApplication
public class BootDemoApplication {

public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class, args);
}

}
  • SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
  • SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean

内嵌Tomcat

Tomcat其实是依靠一个依赖引入进来的

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

点入该依赖中

这里内嵌了一个

1
2
3
4
5
6
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.7.15</version>
<scope>compile</scope>
</dependency>

tomcat场景启动器的依赖,里面就有相关的tomcat配置

1
2
3
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.79</version>

tomcat-embed-core这个是tomcat的核心嵌入,里面依赖了它,才会有tomcat服务器,而如果,你不想用tomcat,你可以这样做

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除tomcat服务器,引入自己需要的服务器即可-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

内置服务器

  • tomcat(默认):apache出品,应用面广,负载了若干较重的组件
  • jetty:更轻量级,负载性能远不及tomcat
  • undertow:负载性能勉强能跑赢tomcat

总结:

  1. 内嵌Tomcat服务器是SpringBoot辅助功能之一
  2. 内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
  3. 变更内嵌服务器思想是去除现有服务器,添加全新的服务器

REST风格开发

REST简介

REST(Representtational State Transfer),表现形式状态转换

  • 传统风格资源描述形式
    • http://localhost/user/getById?id=1
    • http://localhost/user/saverUser
  • REST风格描述形式
    • http://localhost/user/1
    • http://localhost/user
      • 优点:
        • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
        • 书写简化

按照REST风格访问资源时使用行为动作区分对资源进行了何种操作

  • http://localhost/users 查询全部用户信息 GET (查询)

  • http://localhost/users/1 查询指定用户信息 GET (查询)

  • http://localhost/users 添加用户信息 POST (新增/保存)

  • http://localhost/users 修改用户信息 PUT (修改/更新)

  • ``http://localhost/users/1` 删除用户信息 DELETE (删除)

  • 根据REST风格对资源进行访问称为RESTful

注意事项:

上述行为是约定方式,约定不是规范,可以打破,所以称为REST风格,而不是REST规范

描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,比如:users、books、accounts………

参数接收的注解介绍

@RequestBody:用于接收json数据

@PathVariable:用于接收url地址传参或表单传参

@RequestParam:用于接收路径参数,使用{参数名称}描述路径参数

应用:

  • 后期开发中,发送请求参数超过1个时,以json为主,@RequestBody应用较广
  • 如果发送非json格式数据,选用@RequestParam接收请求参数
  • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值

RSET快速开发

编写测试代码

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
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
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class HelloController {

// 查询全部
@GetMapping("/hellos")
public void getAll(){
System.out.println("getAll");
}

// 查询单个
@GetMapping("/hellos/{id}")
public void getById(@PathVariable Integer id){
System.out.println("getById"+id);
}

// 更新
@PutMapping("/hellos")
public void update(){
System.out.println("更新");
}

// 保存
@PostMapping("/hellos")
public void save(){
System.out.println("保存");
}

// 删除
// 更新
@DeleteMapping("/hellos/{id}")
public void delete(@PathVariable Integer id){
System.out.println("删除");
}

}

优化了之前冗余的代码,并且有了一个相当好的风格

基础配置

一般的文件配置可以在application.properties中修改

修改服务器端口

1
2
3
4
5
6
7
8
9
10
# 服务器端口配置
server.port= 80
# 修改banner,这里off表示是关闭了
spring.main.banner-mode=off
# spring.banner.image.location=识别banner的图片位置

# 控制日志

# 日志级别
logging.level.root = info
  • SpringBoot中导入对应的starter后,提供对应配置属性
  • 书写SpringBoot配置采用关键字+提示形式书写

三种配置文件类型

SpringBoot提供了多种属性配置方式

  • application.properties

    1
    server.port= 80
  • application.yml

    1
    2
    server:
    port:81
  • application.yaml

    1
    2
    server:
    port:81

配置文件之间的加载优先级

  • properties(最高)
  • yml
  • yaml(最低)

不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中的不同配置全部保留

yml或yaml属性消失提示

按照步骤操作即可

image-20230908163529742 image-20230908163648643

yaml数据样式

YAML(YAML Ain’t Markup Language),一种数据序列化格式

优点:

  • 容易阅读
  • 容易与脚本语言交互
  • 以数据为核心,重数据轻格式

YAML文件拓展名

  • .yml
  • .yaml

yaml语法规则

  • 大小写敏感
  • 属性层级关系使用多行描述,每行结尾使用冒号结束
  • 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
  • 属性值前添加空格(属性名与属性值之间使用冒号+空格作为分隔)

核心规则:数据前面要加空格与冒号隔开

字面值的表示方式

image-20230908170754388

数组表示方式:在属性名书写位置的下方使用减号作为数据开始符号,每行书写一个数据,减号与数据空格分隔

image-20230908170943814

读取yaml单一属性数据

application.yml

1
2
3
4
5
6
7
8
9
server:
port: 8080

count: "100"

likes:
- game
- games
- games2

HelloController

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
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class HelloController {

// 读取yaml数据中的单一数据
@Value("${server.port}")
private String port;

// 读取多级数据
@Value("${count}")
private String count;

// 读取数组数据
@Value("${likes[1]}")
private String game;

// 读取对象数组(同样的道理):对象名[第几个对象的索引].字段
@GetMapping("/test")
public String getPort(){
System.out.println("端口号为+"+port);
System.out.println("统计+"+count);
System.out.println("game"+game);
return "测试完成";
}

}

使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名……}

yaml文件中的变量引用

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
baseDir: /var/lib

# 使用${属性名}引用数据
#tempDir: /var/lib/1
tempDir: ${baseDir}/1

# 引用的值为数组的情况
TestDir:
- test1: /var/lib/1
- test2: /var/lib/2
- test3: /var/lib/3

UseDir: ${TestDir[0].test1}/test

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class HelloController {

@Value("${tempDir}")
private String tempDir;

@Value("${UseDir}")
private String useDir;

// 读取对象数组(同样的道理):对象名[第几个对象的索引].字段
@GetMapping("/test")
public String getPort(){
System.out.println("temp"+tempDir);
System.out.println("useDir = " + useDir);
return "测试完成";
}

}

运行测试

image-20230909090455825

此时,这里也获取到了对应的数据

如果需要对代码转义,可以在上面加上字符串,下方的\t就会变为对应的转义字符

1
UseDir: "${TestDir[0].test1}\test"

结果如下

1
useDir = /var/lib/1	est

总结:

  • 在配置文件中可以使用属性名引用方式引用属性
  • 属性值如果出现转义字符,需要使用双引号包裹

读取yaml全部属性数据

创建一个Environment并将全部属性自动装配进去,通过Environment.getProperty来得到环境中对应的属性

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
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class HelloController {

@Value("${tempDir}")
private String tempDir;

@Value("${UseDir}")
private String useDir;

@Autowired
private Environment environment;


// 读取对象数组(同样的道理):对象名[第几个对象的索引].字段
@GetMapping("/test")
public String getPort(){
System.out.println("temp"+tempDir);
System.out.println("useDir = " + useDir);
System.out.println("------------------");
System.out.println(environment.getProperty("tempDir"));
System.out.println(environment.getProperty("UseDir"));
return "测试完成";
}

}

读取yaml引用数据类型数据

application.yaml

1
2
3
4
5
6
7
8
9
# 创建类,用于封装下面的数据
# 由spring帮我们去加载数据到对象中
# 使用时,直接从spring中获取信息使用

datasource:
driver: com.mysql.jdbc.Driver
url: jdbc:mysql//localhost:db
username: root
password: root

模型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

// 定义数据模型,封装yaml文件中对应的数据
// 定义为spring管控的bean
@Data
@Component
// 指定加载的数据
// @ConfigurationProperties("写入对应封装的对象名")
@ConfigurationProperties(prefix = "datasource")
public class MyDataSource {
private String driver;
private String url;

private String username;

private String password;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import fun.eastwind.studyboot.MyDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@Slf4j
public class HelloController {

@Autowired
private MyDataSource myDataSource;

@GetMapping("/test")
public String getPort(){
System.out.println(myDataSource);
return "测试完成";
}

}

测试得出结果

image-20230909095906918

整合第三方技术

整合Junit

步骤:

1、导入测试对应的starter

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

2、测试类使用@SpringBootTest修饰(一般都是默认自带的)

1
2
3
4
5
6
7
8
9
10
11
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class BootDemo2ApplicationTests {

@Test
void contextLoads() {
}

}

3、使用自动装配(@Autowired)的形式添加要测试的对象

如果将测试类切换到其他的包下,不在初始的包下,会报错,原因是与引导类不在同一个包下

此时你需要在@SpringBootTest上添加

1
@SpringBootTest(classes = BootDemo2Application.class)

指定对应的引导类,这样测试类就可以找到对应的引导类了

或者还有一种方法,与上面的方法是一样的效果

1
@ContextConfiguration(classes = BootDemo2Application.class)

注解学习

名称:@SpringBootTest

类型:测试类注解

位置:测试类定义上方

作用:设置Junit加载的SpringBoot启动类

相关属性:

  • classes:设置SpringBoot启动类

注意:如果测试类在SpringBoot启动类的包或子包中,可以省略启动类的设置,也就是省略classes的设定

整合MyBatis

  • 核心配置:数据库连接相关信息(连什么?连谁?什么权限)
  • 映射配置:SQL映射(XML/注解)

创建一个Spring Initializr项目

并勾选需要的MyBatis相关依赖

image-20230909130337593

在application.yml中编写对应配置

1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/study

这里的配置改成自己的
创建实体类

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
public class User {
private Integer id;
private String name;
private String sex;

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}
}

编写Mapper

1
2
3
4
5
6
7
8
9
10
import fun.eastwind.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {

@Select("select * from studytable where id = #{id}")
public User getUser(Integer id);
}

在测试类中进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import fun.eastwind.domain.User;
import fun.eastwind.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
void contextLoads() {
User user = userMapper.getUser(1);
System.out.println("user = " + user);
}

}

测试后,发现没有问题,说明整合完成

整合MyBatis的小问题

  1. MySQL8.x驱动强制要求设置时区
    • 修改url,添加serverTimezone设定
    • 修改MYSQL数据库配置(略)
  2. 驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver

整合MyBatisPlus

MyBtais-Plus与MyBatis的区别

  • 导入坐标不同
  • 数据层实现简化

由于MyBatisPlus未被Spring收录,这里只引入一个mysql的坐标

mybatisplus需要自己引入对应的坐标

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>

导入后,你可以将spring-boot-starter的依赖删除,因为mybatis-plus-boot-starter是包含着它的

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

application.yml

1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/study

实体类

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
public class User {
private Integer id;
private String name;
private String sex;

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}
}

UserMapper

1
2
3
4
5
6
7
8
9
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import fun.eastwind.demo1.domain.User;
import org.apache.ibatis.annotations.Mapper;


@Mapper
public interface UserMapper extends BaseMapper<User> {

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import fun.eastwind.demo1.domain.User;
import fun.eastwind.demo1.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Demo1ApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
void contextLoads() {
User user = userMapper.selectById(1);
System.out.println("user = " + user);
}

}

运行后,会出现一个错误Cause: java.sql.SQLSyntaxErrorException: Table 'study.user' doesn't exist

说是表不存在,为什么呢,因为MP(mybatisplus)会直接将实体类的名称小写后,作为表名

解决方法:为实体类直接添加表名

在实体类上方添加注解@TableName("studytable"),这样就可以指定表名了

此时,就查询得到结果了

user = User{id=1, name='张三', sex='男'}

整合Druid

druid的依赖包需要自己导入,在新建项目时勾选mybatis和mysql,因为druid是给数据库使用的

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>

配置与实体类,同整合mybatis与mybatisplus时相同

这里我使用的mybatisplus,所以引入一下依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>

配置方式:

1、直接在application.yml中编写

1
2
3
4
5
6
7
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/study
type: com.alibaba.druid.pool.DruidDataSource

2、推荐使用第二种,虽然两种都行

1
2
3
4
5
6
7
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/study

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import fun.eastwind.demo2.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Demo2ApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
void contextLoads() {
System.out.println("userMapper.selectById(1) = " + userMapper.selectById(1));
}

}

查看打印结果

这里显示初始化数据源

image-20230909143701462

SSMP整合

SSMP(spring、springmvc、mybatisplus)

整合分析

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

模块创建

导入依赖

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
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>

修改配置

application.yml

1
2
server:
port: 80

实体类开发

1
2
3
4
5
6
7
8
9
10
11
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;

@TableName("studytable")
@Data
@AllArgsConstructor
public class User {
private String name;
private String sex;
}

数据层标准开发

修改配置

1
2
3
4
5
6
7
8
9
10
server:
port: 80

spring:
datasource:
druid:
password: 123456
username: root
url: jdbc:mysql://localhost:3306/study
driver-class-name: com.mysql.cj.jdbc.Driver

编写Mapper接口

1
2
3
4
5
6
7
8
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import fun.eastwind.module_practice.domain.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import fun.eastwind.module_practice.domain.User;
import fun.eastwind.module_practice.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ModulePracticeApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
void contextLoads() {
userMapper.insert(new User("test","女"));
}

}

测试一下,没啥问题

为MP添加一下id自增策略,不然后面可能会有问题,因为表中的id采用的是自增策略

1
2
3
4
mybatis-plus:
global-config:
db-config:
id-type: auto

开启MP的运行日志

1
2
3
4
5
6
7
mybatis-plus:
global-config:
db-config:
id-type: auto
configuration:
# 标准输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

再次运行测试类,就可以在控制台看到打印的日志信息了

分页

创建拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MPConfig {

// 创建一个mybatisPlus的拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页的拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 返回mybatisPlus的拦截器
return interceptor;
}

}

编写测试类

1
2
3
4
5
6
7
@Test
void test2() {
// page对象需要当前页和页码
IPage page = new Page(1,5);
// selectPage里需要一个page对象
userMapper.selectPage(page, null);
}
image-20230909162052949

分页的方法

image-20230909161834629

条件查询

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

编写代码

1
2
3
4
5
6
7
8
@Test
void test3() {
// QueryWrapper是用来进行查询的
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// like是进行模糊匹配
queryWrapper.like("name","张");
List<User> userList = userMapper.selectList(queryWrapper);
}

这里可以很明显的看到下面的语句是模糊查询

image-20230909163349509

也可以使用另一种

1
2
3
4
5
6
7
@Test
void test3() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 提升了安全性,这样不会轻易的写错字段名之类的
queryWrapper.like(User::getName,"张");
List<User> userList = userMapper.selectList(queryWrapper);
}

一般情况下,数据是由外界传递的,万一出现状况,数据传递出来一个Null,此时,就会出现问题,像这样的like匹配,就会将null传递过来,并由null进行模糊匹配(%null%)

queryWrapper这里可以添加一个condition(条件),在这个条件中就可以对null值进行判断了

1
2
3
4
5
6
7
8
9
@Test
void test3() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
String name = "张";
// 提升了安全性,这样不会轻易的写错字段名之类的
// 当name不为空时,才进行模糊匹配
queryWrapper.like(name != null,User::getName,name);
List<User> userList = userMapper.selectList(queryWrapper);
}

业务层开发

  • Service层接口定义与数据层接口定义具有较大区别,不要混用
    • selectByUserNameAndPassword(String username,String password)
    • login(String username,String password)

UserService

1
2
3
4
5
6
7
8
9
10
11
import fun.eastwind.module_practice.domain.User;

import java.util.List;

public interface UserService {
Boolean save(User user);
Boolean update(User user);
Boolean delete(Integer id);
User selectById(Integer id);
List<User> selectAll();
}

进行数据库增删改操作时,会返回一个数值,如果数值为整数,说明操作成功,否则失败

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
import fun.eastwind.module_practice.domain.User;
import fun.eastwind.module_practice.mapper.UserMapper;
import fun.eastwind.module_practice.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;

@Override
public Boolean save(User user) {
return userMapper.insert(user) > 0;
}

@Override
public Boolean update(User user) {
return userMapper.updateById(user) > 0;
}

@Override
public Boolean delete(Integer id) {
return userMapper.deleteById(id) > 0;
}

@Override
public User selectById(Integer id) {
return userMapper.selectById(id);
}

@Override
public List<User> selectAll() {
return userMapper.selectList(null);
}
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import fun.eastwind.module_practice.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserServiceTest {

@Autowired
private UserService userService;

@Test
public void test1(){
userService.save(new User("zhangsan","男"));
}

}

没啥问题

刚刚少写了一个分页,再到service中进行编写
UserService

1
IPage<User> page(int current, int pageSize);

UserServiceImpl

1
2
3
4
5
@Override
public IPage<User> page(int current, int pageSize) {
IPage iPage = new Page<User>(current,pageSize);
return userMapper.selectPage(iPage,null);
}

测试一下

1
2
3
4
@Test
public void test2(){
userService.page(1,5);
}
image-20230910130738572

这里对UserService的方法说明一下

1
2
3
4
@Override
public Boolean save(User user) {
return userMapper.insert(user) > 0;
}

为什么是判断是否大于0呢,因为这里操作成功后都是返回正值,而失败都是负值,测试一下,来看看效果

image-20230910131130122

测试service可能不太明确,直接拿mapper来进行一个测试

1
2
3
4
5
6
7
8
9
@Autowired
private UserMapper userMapper;

@Test
public void test3(){
System.out.println("--------------------------------");
System.out.println(userMapper.insert(new User("zhangsan", "男")));
System.out.println("--------------------------------");
}

image-20230910131317212

这里发现成功后,会返回正值1,插入失败,通常会返回-1

业务层快速开发

快速开发方案

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

IUserService

1
2
3
4
5
import com.baomidou.mybatisplus.extension.service.IService;
import fun.eastwind.module_practice.domain.User;

public interface IUserService extends IService<User> {
}

IUserServiceImpl

1
2
3
4
5
6
7
8
9
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import fun.eastwind.module_practice.domain.User;
import fun.eastwind.module_practice.mapper.UserMapper;
import fun.eastwind.module_practice.service.IUserService;
import org.springframework.stereotype.Service;

@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import fun.eastwind.module_practice.domain.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class IUserServiceTest {

@Autowired
private IUserService userService;

@Test
public void test1(){
List<User> list = userService.list();
for (User user : list) {
System.out.println(user);
}
}

}

没啥问题

表现层标准开发

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

UserController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import fun.eastwind.module_practice.domain.User;
import fun.eastwind.module_practice.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private IUserService iUserService;

@GetMapping
public List<User> getUsers() {
return iUserService.list();
}

}

在postman中测试一下,得到了查询的数据

image-20230910140359295

接着补全一下其他的代码,方法就不测试了,效果都差不多

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
import fun.eastwind.module_practice.domain.User;
import fun.eastwind.module_practice.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private IUserService iUserService;

@GetMapping
public List<User> getUsers() {
return iUserService.list();
}

@PostMapping
public Boolean save(@RequestBody User user) {
return iUserService.save(user);
}

@PutMapping
public Boolean update(@RequestBody User user){
return iUserService.updateById(user);
}

@DeleteMapping("{id}")
public Boolean delete(@PathVariable Integer id){
return iUserService.removeById(id);
}

@GetMapping("{id}")
public User getUser(@PathVariable Integer id){
return iUserService.getById(id);
}

}

总结:

1、基于Restful制作表现层接口

  • 新增:POST
  • 删除:DELETE
  • 修改:PUT
  • 查询:GET

2、接收参数

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

表现层消息的一致性处理

设置表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议

Result(模型类)

模型类中的flag表示是否成功,data接收数据

1
2
3
4
5
6
7
8
import lombok.Data;

@Data
public class Result {
private Boolean flag;
private Object data;

}

但是这样写,每次都需要set里面的flag和data

所以,进行一下优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import lombok.Data;

@Data
public class Result {
private Boolean flag;
private Object data;

public Result() {
}

public Result(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}

public Result(Boolean flag) {
this.flag = flag;
}
}

编写控制类统一结果返回

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
import fun.eastwind.module_practice.domain.User;
import fun.eastwind.module_practice.service.IUserService;
import fun.eastwind.module_practice.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private IUserService iUserService;

@GetMapping
public Result getUsers() {
return new Result(true,iUserService.list());
}

@PostMapping
public Result save(@RequestBody User user) {
return new Result(iUserService.save(user));
}

@PutMapping
public Result update(@RequestBody User user){
return new Result(iUserService.updateById(user));
}

@DeleteMapping("{id}")
public Result delete(@PathVariable Integer id){
return new Result(iUserService.removeById(id));
}

@GetMapping("{id}")
public Result getUser(@PathVariable Integer id){
return new Result(true,iUserService.getById(id));
}

}

统一异常处理

创建一个异常处理器

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class AllException {

@ExceptionHandler
public Result doException(Exception ex){
ex.printStackTrace();
return new Result("出现异常,请联系程序猿小哥为您服务");
}

}

修改之前的Result对象(一致性处理的对象),出现异常被异常处理器拦截后,肯定会有提示信息,所以需要新增一个msg(消息)

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
import lombok.Data;

@Data
public class Result {
private Boolean flag;
private Object data;

private String msg;

public Result(String msg) {
this.flag = false;
this.msg = msg;
}

public Result() {
}

public Result(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}

public Result(Boolean flag) {
this.flag = flag;
}
}

报个异常测试一下

image-20230910165417121

如果想要指定某些异常,可以在@ExceptionHandler后添加对应的异常类

1
2
3
4
5
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
ex.printStackTrace();
return new Result("出现异常,请联系程序猿小哥为您服务");
}

总结:

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

基础篇完结

  • pom.xml:配置起步依赖
  • application.yml:设置数据源、端口、框架技术相关配置等
  • mapper:继承BaseMapper、设置@Mapper注解
  • mapper测试类
  • service:调用数据层接口或MyBatis-Plus提供的接口快速开发
  • service测试类
  • controller:基于Restful开发,使用Postman测试跑通功能
  • 页面:放置在resources目录下的static目录中