# swpu-spring-projects
**Repository Path**: swpu-projects/swpu-spring-projects
## Basic Information
- **Project Name**: swpu-spring-projects
- **Description**: 西南石油大学 - Spring Boot 项目培训
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 39
- **Created**: 2021-07-15
- **Last Updated**: 2022-09-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Spring Boot + MyBatis 完善 CRUD 四大接口
[TOC]
------
## 一、概念
通过 Spring Boot + MyBatis 快速整合完善 CRUD (Create、Retrieve、Update、Delete) 四大基础且核心的接口操作类,并基于 Linux + Docker 完善前置工作内容的准备,通过后续案例测试完善实际的阶段性项目应用。
## 二、项目的构建
项目的构建分为主要基于 Apache 下的 Maven 做项目构建,并由拆分为多模块作为全新单体架构的应用级项目开发。
项目的构建中,框架由 Spring Boot + MyBatis 构成,则在项目中仅仅只需要配置基于 Spring Boot 的 MyBatis 和 MySQL 相关配置文件即可完成项目的基础环境构建。
### 2.1. 依赖的引入
由于项目使用 Spring Boot + MyBatis 作为基础单体架构项目,则我们只需要引入其 Spring Web、MyBatis、MySQL 相关依赖即可。我们在项目根目录的 **pom.xml** 文件,如下代码片段所示:
```xml
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.0
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
```
其中,所有的 Java 项目依赖都可以在 https://mvnrepository.com/ 中找到。
### 2.2. 项目的配置
项目的配置主要分为 **Spring Boot** 和 **MyBatis** 配置,所有的配置都需要在 **application.yml** 中进行配置。
#### 2.2.1. Spring Boot 的配置
做一个可有可无的配置,声明当前项目的名称、修改当前项目的端口,如下代码片段:
```yaml
# 配置项目的名称
spring:
application:
name: swpu-spring-projects
# 项目端口
server:
port: 20000
```
#### 2.2.2. 数据库的配置
数据库的配置分成两个部分,第一个是数据库本身的配置,诸如数据库地址、数据库用户名、数据库密码。
##### 2.2.2.1. MySQL 的配置
因为项目是基于 Spring Boot 的,所有我们相关的配置需要放在 Spring 配置层级下,如下代码片段所示:
```yaml
spring:
application:
name: swpu-spring-projects
# 数据库连接信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/swpu_db
username: swpu_user
password: '123456789'
```
除了数据库本身以为,为提高数据库的整体性能,我们需要额外配置一个高性能的连接池 - **Hikari** ,如下配置文件所示:
```yaml
spring:
application:
name: swpu-spring-projects
# 数据库连接信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/swpu_db
username: swpu_user
password: '123456789'
# 数据库连接池 Hikari
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
# 空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
# 连接池最大连接数,默认是10
maximum-pool-size: 10
# 此属性控制从池返回的连接的默认自动提交行为,默认值:true
auto-commit: true
# 连接池名称
pool-name: SWPU-DB
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
max-lifetime: 1800000
# 数据库连接超时时间,默认30秒,即30000
connection-timeout: 30000
```
##### 2.2.2.2. MyBatis 的配置
MyBatis 的配置主要是需要告诉 Spring Boot,MyBatis 所需要的 XML SQL 配置文件在哪个文件里,如下配置文件所示:
```yaml
# MyBatis XML 配置
mybatis:
mapper-locations: classpath:mapper/**.xml
# 开启 驼峰支持
configuration:
map-underscore-to-camel-case: true
```
**注意:因为 MyBatis 配置文件没有统一集成在 Spring Boot 中,所以 MyBatis 相关的配置文件需要放在 yaml 配置文件的第一层也就是和 Spring 同级 !**
##### 2.2.2.3. yaml 整体配置例子
YAML 整体配置文件如下所示:
```yaml
spring:
application:
name: swpu-spring-projects
# 数据库连接信息
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/swpu_db
username: swpu_user
password: '123456789'
# 数据库连接池
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
# 空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
# 连接池最大连接数,默认是10
maximum-pool-size: 10
# 此属性控制从池返回的连接的默认自动提交行为,默认值:true
auto-commit: true
# 连接池名称
pool-name: SWPU-DB
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
max-lifetime: 1800000
# 数据库连接超时时间,默认30秒,即30000
connection-timeout: 30000
# 项目端口
server:
port: 20000
# MyBatis XML 配置
mybatis:
mapper-locations: classpath:mapper/**.xml
# 开启 驼峰支持
configuration:
map-underscore-to-camel-case: true
```
### 2.3. 项目的启动
要让项目跑起来,那么就需要一个 **主函数** ,在 Spring Boot 中的主函数,比起我们之前的 **Java 五大基本语法** 中的主函数的唯一区别就是 Spring 在主函数类上标识了这是一个 Spring Boot 的项目启动类。如下代码所示:
```java
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author wales
*
* 主函数
*
*
* - 1. @SpringBootApplication 注解 代表当前的项目是一个 Spring Boot 项目
* - 2. SpringApplication.run(); 代表着 Spring Boot 可运行/可执行 本项目
* - 3. MapperScan 是 MyBatis 整合进 SpringBoot 后,告诉 Spring Boot DAO 存放的位置,这样才能用 Resource 注解来 new 我们的 DAO
*
*/
@MapperScan({"com.swpu.dao.**"})
@SpringBootApplication
public class SwpuSpringProjectsApplication {
/**
* 主函数 仅仅只比我们的 Java-Base-Projects 项目多了一个 Spring 自己的 方法调用
* @param args args
*/
public static void main(String[] args) {
SpringApplication.run(SwpuSpringProjectsApplication.class, args);
}
}
```
### 2.4. 接口化的开发
网址 = 接口地址 = 请求地址,三个叫法都是一个概念。请求地址主要为4个部分组成,如 https://www.baidu.com/page1/method2?id=123&name=swpu
- 其中 https://www.baidu.com/ 是一级请求
- /page1/method2 是二级请求
- ?id=123 是跟随的参数,其中多个参数通过 & 符号连接
- 请求方式有:Get、Post、Put、Delete 常用的四种 RESTful 类型的请求方式
#### 2.4.1. 项目开发流程
项目开发的流程我们分成了三个阶段:
- **数据持久层:** Entity -> DAO -> SQLMapper.xml 是指和数据库打交道的且最为核心的一层
- **业务逻辑层:** Service -> ServiceImpl -> DAO 是指实际业务开发所需要的多个 DAO 组合而成的层级
- **请求接口层:** Controller -> Service 将通过 **业务逻辑层** 和 **数据持久层** 所处理过的数据暴露给第三方
##### 2.4.1.1. 第一阶段
###### a. Entity
拿到数据库,搞到表之后,项目开发则直接来到第一个阶段。第一个要做的事情就是建立 entity 相关类,如下代码所示:
```java
/**
* @author wales
*
* 一个 Entity 对应一个数据库表和表中的字段
*/
@Data
public class SwpuUserEntity {
private String id;
private int departmentId;
private int jobLevelId;
private int posId;
private String engageForm;
private String specialty;
private String school;
}
```
Entity 就等同于 数据库中的一张表,所以一般的表的名称也是 Entity 名称,只不过后缀加上了 Entity 做标识。其中我们使用 **@Data** 注解来简化诸如 get()、set()、toString() 等繁琐的方法。
###### b. Dao
Entity 等同于准备好了与数据库表的映射,这个时候我们就需要带上 Entity 去操作数据库,由于我们引入了 MyBatis 作为数据库基本框架且 MyBatis 以 XML 的方式存在,所以我们需要在 Java 中通过 **interface** 的形式来实现 MyBatis 与 Java 的自动打通,如下代码所示:
```java
import com.swpu.entity.SwpuUserEntity;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author wales
*
* DAO 是 MyBatis 用作打通 与 XML 中的 SQL 的桥梁
*
* - 1. 类名是与 XML 打通的 ID 标识
* - 2. 方法名是定位到 XML 中的 SQL
* - 3. 方法参数 是 XML 中 SQL 所需的条件参数
* - 4. 方法名前的返回值: 查询(单个返回是 Entity、多个查询是 List); 增加/修改/删除 返回值均为 int ,代表着 增加了多少条数据、修改了多少条数据、删除了多少条数据
*
*/
public interface SwpuUserDao {
/**
* 根据 单个参数 查询
*
* @param id id
* @return {@link SwpuUserEntity}
*/
SwpuUserEntity findById(@Param("id") String id);
/**
* 无条件查询,可能会产生多个数据,所以需要用 List 包裹起来
*
* @return {@link SwpuUserEntity}
*/
List findAll();
/**
* 增加,增加的是表的数据,所以需要传递 Entity
*
* @param entity {@link SwpuUserEntity}
* @return {@link Integer}
*/
int insert(SwpuUserEntity entity);
/**
* 修改,修改的是表的数据,所以需要传递 Entity
*
* @param entity {@link SwpuUserEntity}
* @return {@link Integer}
*/
int update(SwpuUserEntity entity);
/**
* 删除,通过特定的参数、条件 来删除表中的数据
*
* @param id id
* @return {@link Integer}
*/
int delete(@Param("id") String id);
}
```
###### c. XML
Dao,是一个方法,是一个接口类型的方式,也就是没有 {} 的方法,其中 {} 中具体的方法实现,在 MyBatis 中是通过 XML 来实现的,如下代码所示:
```xml
insert into swpu_user (id, department_id, job_level_id, pos_id, engage_form, specialty, school)
values (#{id, javaType = String}, #{departmentId, javaType = Integer}, #{jobLevelId, javaType = Integer},
#{posId, javaType = Integer}, #{engageForm, javaType = String},
#{specialty, javaType = String}, #{school, javaType = String})
insert into swpu_user (
id,
department_id, job_level_id, pos_id, engage_form, specialty, school)
values (
#{id, javaType = String},
#{departmentId, javaType = Integer}, #{jobLevelId, javaType = Integer},
#{posId, javaType = Integer}, #{engageForm, javaType = String},
#{specialty, javaType = String}, #{school, javaType = String}
)
update swpu_user
set department_id = #{departmentId, javaType = Integer},
job_level_id = #{jobLevelId, javaType = Integer},
pos_id = #{posId, javaType = Integer},
engage_form = #{engageForm, javaType = String},
specialty = #{specialty, javaType = String},
school = #{school, javaType = String}
where id = #{id, javaType = String}
update swpu_user
set (
department_id = #{departmentId, javaType = Integer},
job_level_id = #{jobLevelId, javaType = Integer},
pos_id = #{posId, javaType = Integer}, engage_form = #{engageForm, javaType = String},
specialty = #{specialty, javaType = String}, school = #{school, javaType = String}
) where id = #{id, javaType = String}
delete
from swpu_user su
where 1 = 1
and su.id = #{id, javaType = String}
```
##### 2.4.1.2. 第二阶段
待第一阶段初始数据准备完成后,我们就要进入到 **业务逻辑层** 进行开发。所谓的业务逻辑层,其实就是 **组装多个 DAO**的地方。
业务逻辑层的写法,需要先定义一个 Service 的接口类以及接口方法,再通过 **implements** 去实现具体的接口方法。
###### a. Service 层
同 DAO 层一样,只不过这一层是用作组装 DAO,但也是没有 {} 的接口方法:
```java
import com.swpu.entity.SwpuUserEntity;
/**
* @author wales
*
* Service 业务逻辑层
*/
public interface SwpuUserService {
/**
* 学生信息保存功能
*
* @param entity 学生信息
* @return {@link Integer}
*/
int change(SwpuUserEntity entity);
}
```
###### b. Service 实现层
DAO 接口层的实现是通过 MyBatis 的 XML 来自动完成的,而 Spring 的 Service 接口层实现 是通过 **implements** 来实现的,如下代码所示:
```java
import com.swpu.dao.SwpuUserDao;
import com.swpu.entity.SwpuUserEntity;
import com.swpu.service.SwpuUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author wales
*
* Service 业务层的具体实现
*
* Serivce 其实就是组装多个 DAO 的代码层 - 业务逻辑层
*/
@Service
public class SwpuUserServiceImpl implements SwpuUserService {
@Resource
private SwpuUserDao swpuUserDao;
/**
* 学生信息保存功能
*
* @param entity 学生信息
* @return {@link Integer}
*/
@Override
public int change(SwpuUserEntity entity) {
// 先判断该学生信息是否存在,如果存在,则是增加,否则是修改
int status = 0;
SwpuUserEntity info = swpuUserDao.findById(entity.getId());
if (info != null) {
status = swpuUserDao.update(entity);
} else {
status = swpuUserDao.insert(entity);
}
return status;
}
}
```
所谓的 **业务逻辑层**,如上代码所示,只是组装多个 DAO 仅此而已。
- **业务:** 指的是我们要做的具体功能,比如 学生信息保存功能
- **逻辑:** 指的就是为了实现 学生信息保存的功能 而要思考如何组装 多个 DAO
##### 2.4.1.3. 第三阶段
业务逻辑层中,将我们的数据处理好之后,需要将处理结果返回给第三方所有者 *(也就是调用我们接口的人)* 。我们通过 Controller 层中调用 Service 业务逻辑层来实现相关操作。
**业务逻辑层的注入**
业务逻辑层的注入,其实就是 Spring 帮我们完成 Object o = new Object() 的操作,但仅仅只有 Service 业务逻辑层 和 DAO 数据持久层 可以用 @Resource 方式来注入。如下代码所示:
```java
/**
* 当且仅当 需要 New DAO 或者 Service 的时候,我们才需要用 Spring 的 Resource 注解来代替我们 New
*/
@Resource
private SwpuUserService swpuUserService;
```
**完整的 Controller 示例**
完整 Controller 如下代码所示:
```java
import com.swpu.entity.SwpuUserEntity;
import com.swpu.service.SwpuUserService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author wales
*
* 对外暴露 学生信息保存功能 相关接口
*/
@RestController
@RequestMapping(value = "/stu")
public class SwpuStuController {
/**
* 当且仅当 需要 New DAO 或者 Service 的时候,我们才需要用 Spring 的 Resource 注解来代替我们 New
*/
@Resource
private SwpuUserService swpuUserService;
/**
* Change 接口
*
* @param entity {@link SwpuUserEntity}
* @return {@link Object}
*/
@PostMapping(value = "/change")
public Object change(@RequestBody SwpuUserEntity entity) {
int flag = swpuUserService.change(entity);
if (flag > 0) {
return "操作成功";
} else {
return "操作失败";
}
}
}
```
#### 2.4.2. 开发流程
一般来说,整套开发流程需要 1-3 年的时间来进行 **技术沉淀** ,整个沉淀的过程其实就是在总结 **开发顺序**、**开发套路**、**开发模板** 。
##### 2.4.2.1. 以开发者角度
以开发者角度来说,我们的开发顺序是:
- **Entity** - 创建实体,对应着数据库中的表 - **第一阶段**
- **DAO** - 创建数据持久层,操作数据库所需的相关接口,**第一阶段**
- **MapperXML** - 创建 MyBatis,通过结合 DAO 来实现具体的 SQL 操作, **第一阶段**
- **Service** - 业务逻辑层,为组装多个 DAO 所提供的接口层,**第二阶段**
- **ServiceImpl** - 业务逻辑具体实现层,具体去实现 Service 层中组装多个 DAO 的类、方法,**第二阶段**
- **Controller** - 接口请求控制层,通过绑定 *网址* 的形式让第三方开发者能访问我们所绑定 *网址* 对应的方法,**第三个阶段**
##### 2.4.2.2. 以第三方角度
第三方包含用户、其他开发者、WEB、APP、小程序、公众号 等其他的非本项目编写者。其中,第三方开发者的角度来说,我们的顺序将完全颠倒:
- **Controller** - 通过请求、其他第三方形式请求到当前的项目,第一个经过的地方必定是 Controller
- **Service** - 经过 Controller 主要是处理接口地址对应的方法,方法中会调用 Service,Serivce 接口层会自动调用 ServiceImpl 层
- **ServiceImpl** - ServiceImpl 层主要是组装 DAO 的地方,所以该主要就是调用 DAO 层的方法
- **DAO** - DAO 层主要是提供与数据库打交道的工具,但本质只是一个接口方法,具体的实现是通过自动调用 MyBatis 中的 XML 实现
- **MapperXML** - XML 这一层是 MyBatis 层,主要是由 DAO 主动/自动 所调用的方法,以 XML 的形式来实现具体的 DAO 方法,其中主要是 SQL 的具体实现
### 2.5. 需求驱动开发
需求驱动开发以大部分公司来说是指: **符合业务所需才是优质的技术架构**。对于开发者来说:**要以技术来驱动业务**。这是一个矛盾点。
而真正所谓优秀的架构是需要两者兼顾,在不影响现有业务成本的情况下提高整体技术架构则是较为优秀的架构。