[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| Same | adj. | /seɪm/ | 相同的(常用于“Same Origin Policy”,指同源策略,限制不同源的文档或脚本交互) |
| Origin | n. | /ˈɒrɪdʒɪn/ | 源(指URL的协议、域名、端口组合,同源即这三者完全一致;Origin请求头标识请求来源) |
| Policy | n. | /ˈpɒləsi/ | 策略(如“Same Origin Policy”即同源策略,是浏览器的安全机制;也指配置策略) |
| Cross | adj. | /krɒs/ | 跨域的(指不同源的交互,如“Cross-Origin”即跨域,常涉及跨域资源共享问题) |
| resource | n. | /ˈriːsɔːs/ | 资源(指网络中的文件、数据等,如“Cross-Origin Resource Sharing”中的资源) |
| sharing | n. | /ˈʃeərɪŋ/ | 共享(“CORS”即“Cross-Origin Resource Sharing”,指跨域资源共享机制,允许跨域请求) |
| Allow | v. | /əˈlaʊ/ | 允许(CORS响应头Access-Control-Allow-*用于指定允许的跨域操作,如允许的源、方法等) |
| Credentials | n. | /krəˈdenʃlz/ | 凭据(指Cookie、HTTP认证信息等;CORS中withCredentials控制是否发送凭据,需服务端允许) |
| registry | n. | /ˈredʒɪstri/ | 注册中心(存储和管理注册信息的组件,如服务注册中心、Bean定义注册中心等) |
| Configurer | n. | /kənˈfɪɡərə(r)/ | 配置器(用于配置组件的类,如Spring中的WebMvcConfigurer用于配置MVC相关功能) |
总目标:单表增删改查
一、物理建模
# 创建数据库
create database db_demo_crud CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
use db_demo_crud;
# 创建数据库表
create table t_tiger(
tiger_id int auto_increment primary key,
tiger_name char(100),
tiger_age int,
tiger_salary double
);
# 插入测试数据
INSERT INTO t_tiger (tiger_name, tiger_age, tiger_salary)
VALUES ('王虎', 5, 500.00),
('李虎', 3, 800.50),
('张虎', 7, 200.75),
('赵虎', 4, 200.25),
('刘虎', 6, 900.00);

二、后端工程
1、创建工程
①pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 根据需要加入 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
②主启动类

@SpringBootApplication
public class Module33DemoMainType {
public static void main(String[] args) {
SpringApplication.run(Module33DemoMainType.class, args);
}
}
③创建主配置文件

spring.application.name=module33-spring-mvc-demo
spring.datasource.username=atguigu
spring.datasource.password=atguigu
spring.datasource.url=jdbc:mysql://192.168.100.100/db_demo_crud
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
④逻辑建模

@Data
public class Tiger {
private Integer tigerId;
private String tigerName;
private Integer tigerAge;
private Double tigerSalary;
}
⑤创建组件

⑥加入Result类
package com.atguigu.demo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 我们封装 Result 这个类,让后端所有响应数据都按照这个格式返回
* 为什么能涵盖所有响应结果的数据结构?
* 因为响应无非是成功和失败:
* 失败:不管增删改查什么操作,都返回 code 和 message
* 成功:
* 增删改成功,返回 code 和 message(OK)
* 查询成功,返回 code 和 message(OK)还有数据
* 友情提示:这个类不需要自己写,用的时候可以复制粘贴,理解就行
* 泛型参数:指定当前响应结果要返回给前端的具体数据类型
* @param <T>
*
* code 属性:使用编码的方式来精确定义返回结果是什么情况
* 注意:这个 code 不是 HTTP 响应状态码,而是根据业务情况定义的『结果码』
* 举例:登录失败的结果码是 8800;注册时指定的用户名已存在结果码是 8531;订单超时未支付被取消结果码是 9800
* 核心关键特点:结果码是团队内部人为约定的,是和业务相关的一个编码,不是语法层面的编码
* 区分语法错误和业务错误(逻辑错误),以登录失败为例:
* HTTP 响应状态码 200,语法层面处理请求成功
* 结果码 8800,逻辑层面用户登录并没有成功
* message 属性:当前请求处理结果的说明信息
* data 属性:要返回给前端的查询结果
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
public static final String RESULT_SUCCESS = "SUCCESS";
public static final Integer CODE_SUCCESS = 10000;
private Integer code;
private String message = RESULT_SUCCESS;
private T data;
// 增删改操作(没有查询结果)成功
public static Result success() {
// 创建当前场景下 Result 对象返回
return new Result<>(CODE_SUCCESS, RESULT_SUCCESS, null);
}
// 查询操作成功
public static <QueryData> Result<QueryData> success(QueryData data) {
// 创建当前场景下 Result 对象返回
return new Result<>(CODE_SUCCESS, RESULT_SUCCESS, data);
}
// 任何一种操作失败
public static Result failed(String message) {
return new Result(55555, message, null);
}
}
2、测试数据库连接
①创建测试类

@SpringBootTest
public class DemoTest {
}
需要注意以下三点:
- 引入spring-boot-starter-test依赖
- 测试类所在的包,必须是主启动类所在包或子包
- 测试类上标记@SpringBootTest注解
使用过程中,需要哪个bean,自动装配进来即可
②测试数据库连接
@SpringBootTest
public class DemoTest {
@Autowired
private DataSource dataSource;
@Test
public void test01Conn() throws SQLException {
Connection connection = dataSource.getConnection();
System.out.println("connection = " + connection);
}
}
3、各个功能URL地址设置
| 功能 | URL地址 | 请求方式 |
|---|---|---|
| 查询列表 | /tiger/list | GET |
| 删除记录 | /tiger/{tigerId} | DELETE |
| 新增记录(通过请求参数接收) | /tiger?tigerName=xxx&tigerAge=xxx&tigerSalary=xxx | GET |
| 查询单个对象 | /tiger/{tigerId} | GET |
| 更新记录 | /tiger | PUT |
这里为了练习“通过请求参数方式接收数据”新增记录没有遵循 REST 风格要求
4、业务:返回数据列表
①需求
没有查询条件,不考虑分页,给前端返回全部数据组成的 List 集合
②代码
[1]Controller方法
@Autowired
private TigerService tigerService;
@GetMapping("/tiger/list")
public Result<List<Tiger>> showList() {
try {
List<Tiger> tigerList = tigerService.getList();
return Result.ok(tigerList);
} catch (Exception e) {
return Result.failed(500, e.getMessage());
}
}
[2]Service方法
@Override
public List<Tiger> getList() {
return tigerDao.selectList();
}
[3]Dao方法
@Override
public List<Tiger> selectList() {
String sql = "select tiger_id tigerId, tiger_name tigerName, tiger_age tigerAge, tiger_salary tigerSalary from t_tiger";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Tiger.class));
}
③测试

@Autowired
private TigerService tigerService;
@Test
public void test02GetList() {
List<Tiger> tigerList = tigerService.getList();
for (Tiger tiger : tigerList) {
System.out.println("tiger = " + tiger);
}
}
注:Controller方法通过浏览器测试
5、业务:删除记录
①需求
根据id执行删除
在数据库中执行delete from ... SQL语句是物理删除
实际项目中更多采用逻辑删除:
update xxx set del_status=0 from where id=?
②代码
[1]Controller方法
@DeleteMapping("/tiger/{tigerId}")
public Result<Void> doRemove(@PathVariable("tigerId") Integer tigerId) {
try {
tigerService.removeById(tigerId);
return Result.ok();
} catch (Exception e) {
return Result.failed(500, e.getMessage());
}
}
[2]Service方法
@Override
public void removeById(Integer tigerId) {
tigerDao.deleteById(tigerId);
}
[3]Dao方法
@Override
public void deleteById(Integer tigerId) {
String sql = "delete from t_tiger where tiger_id=?";
jdbcTemplate.update(sql, tigerId);
}
6、业务:新增记录
①需求
前端以请求参数的方式发送数据,后端也是基于请求参数来接收
②代码
[1]Controller方法
@GetMapping("/tiger")
public Result<Void> saveTiger(Tiger tiger) {
try {
tigerService.saveTiger(tiger);
return Result.ok();
} catch (Exception e) {
return Result.failed(DemoConstant.GLOBAL_ERROR_CODE, e.getMessage());
}
}
[2]Service方法
@Override
public void saveTiger(Tiger tiger) {
tigerDao.insertTiger(tiger);
}
[3]Dao方法
@Override
public void insertTiger(Tiger tiger) {
String sql = """
insert into t_tiger(tiger_name,
tiger_age,
tiger_salary) values(?, ?, ?)
""";
jdbcTemplate.update(sql, tiger.getTigerName(), tiger.getTigerAge(), tiger.getTigerSalary());
}
7、业务:查询单个对象
①需求
根据前端提供的id,查询单个Tiger对象返回给前端
②代码
[1]Controller方法
@GetMapping("/tiger/{tigerId}")
public Result<Tiger> getTigerById(@PathVariable("tigerId") String tigerId) {
try {
Tiger tiger = tigerService.getTigerById(tigerId);
return Result.ok(tiger);
} catch (Exception e) {
return Result.failed(500, e.getMessage());
}
}
[2]Service方法
@Override
public Tiger getTigerById(String tigerId) {
return tigerDao.selectTigerById(tigerId);
}
[3]Dao方法
@Override
public Tiger selectTigerById(String tigerId) {
String sql = """
select tiger_id tigerId, tiger_name tigerName, tiger_age tigerAge, tiger_salary tigerSalary
from t_tiger
where tiger_id=?
""";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Tiger.class), tigerId);
}
8、业务:执行更新
①需求
前端提交一个Tiger对象,后端根据这个Tiger对象执行更新
②代码
[1]Controller方法
@PutMapping("/tiger")
public Result<Void> doUpdate(@RequestBody Tiger tiger) {
try {
tigerService.updateTiger(tiger);
return Result.ok();
} catch (Exception e) {
return Result.failed(500, e.getMessage());
}
}
[2]Service方法
@Override
public void updateTiger(Tiger tiger) {
tigerDao.updateTiger(tiger);
}
[3]Dao方法
@Override
public void updateTiger(Tiger tiger) {
String sql = "update t_tiger set tiger_name=?, tiger_age=?, tiger_salary=? where tiger_id=?";
jdbcTemplate.update(sql,
tiger.getTigerName(),
tiger.getTigerAge(),
tiger.getTigerSalary(),
tiger.getTigerId());
}