回顾javaweb全流程上

回顾javaweb全流程上
Silence1 | #固定配置文件直接复制 |
1 | <!-- 文件名:logback.xml info级别不那么乱,debug级别会打印很多信息 --> |
1 | /** |
单元测试
在pom.xml中添加JUnit依赖
1 | <dependency> |
- Junit单元测试规范:类名以Test结尾,方法名以test开头,每个方法都是一个测试用例。
- 方法上添加@Test注解,标识这是一个测试方法。 规定:
- 测试方法必须是public void类型。
- 测试方法不能有参数。
- 测试方法不能抛出异常。
断言
断言是单元测试中常用的一种机制,用于验证代码的行为是否符合预期。在JUnit中,断言通常用于测试方法中,用于检查测试结果是否与预期结果一致。
- 常用的断言方法Assertions:
- assertEquals(expected, actual, String message->这里选填有对应的重载方法):检查预期值与实际值是否相等。
- assertTrue(condition):检查条件是否为真。
- assertFalse(condition):检查条件是否为假。
- assertNotNull(object):检查对象是否不为空。
- assertNull(object):检查对象是否为空。
常见注解
- @Test:标识这是一个测试方法。
- @ParameterizedTest:标识这是一个参数化测试方法。(可以在一个测试方法中使用多个不同的参数进行测试)
- @ValueSource:为参数化测试方法提供参数值。(可以指定多个参数值,每个值之间用逗号隔开)与上面@ParameterizedTest进行搭配使用
- @BeforeEach:在每个测试方法执行前执行。
- @AfterEach:在每个测试方法执行后执行。
- @BeforeAll:在所有测试方法执行前执行。在每个测试方法执行前执行,用于初始化测试环境所以应该加上static关键字。
- @AfterAll:在所有测试方法执行后执行。
- @DisplayName:为测试类或测试方法指定自定义的显示名称。
JUnit 常见问题总结
1. JUnit单元测试的方法,是否可以声明方法形参?
- 可以的,通过参数化测试实现
- 使用
@ParameterizedTest+@ValueSource组合
2. 如何实现在单元测试方法运行之前,做一些初始化操作?
- 使用
@BeforeEach注解:在每个测试方法执行前执行 - 使用
@BeforeAll注解:在所有测试方法执行前执行(需要声明为static)
3. 如何实现在单元测试方法运行之后,释放对应的资源?
- 使用
@AfterEach注解:在每个测试方法执行后执行 - 使用
@AfterAll注解:在所有测试方法执行后执行(需要声明为static)
单元测试-Maven依赖范围
- 测试范围(test scope):
- 仅在测试阶段有效,不会被包含在最终的可执行文件(如JAR或WAR)中。
- 常用的测试范围依赖包括JUnit、Mockito、AssertJ等。
- 编译范围(compile scope):
- 编译时需要,运行时也需要。
- 常用的编译范围依赖包括Servlet API、JSP API等。
- 运行时范围(runtime scope):
- 运行时需要,编译时不需要。
- 常用的运行时范围依赖包括数据库驱动、日志框架等。
- 提供范围(provided scope):
- 编译时需要,运行时由容器提供。
- 常用的提供范围依赖包括Servlet容器、JSP容器等。
1
2
3
4
5
6
7<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.1</version>
<scope>test</scope>
<!-- 测试范围依赖,仅在测试阶段有效 -->
</dependency>
SpringBoot快速入门
项目创建
- 项目创建:
- 打开Spring Initializr(https://start.spring.io/)。
- 填写项目元数据(Group、Artifact、Name、Description、Package Name等)。
- 选择项目类型(Maven Project或Gradle Project)。
- 选择Java版本(如Java 17)。
- 选择Spring Boot版本(如2.7.13)。
- 添加所需的依赖(如Web、Data JPA、MySQL Driver等)。
- 点击“Generate”按钮,下载项目压缩包。
- 项目目录结构:
- src/main/java:包含应用程序的Java源文件。
- src/main/resources:包含应用程序的资源文件(如配置文件、静态资源等)。
- src/test/java:包含测试用的Java源文件。
- src/test/resources:包含测试用的资源文件。
- pom.xml(或build.gradle):项目的构建配置文件,包含项目依赖、插件等。
- 项目依赖管理:
- pom.xml(或build.gradle):项目的构建配置文件,包含项目依赖、插件等。
- 依赖管理:
- 直接依赖:在项目的构建配置文件中直接声明的依赖,会被包含在最终的可执行文件中。
- 传递依赖:项目依赖的其他库,会被自动下载并包含在项目中。
- 依赖范围:
- 编译范围(compile scope):编译时需要,运行时也需要。
- 测试范围(test scope):仅在测试阶段有效,不会被包含在最终的可执行文件中。
- 运行时范围(runtime scope):运行时需要,编译时不需要。
- 提供范围(provided scope):编译时需要,运行时由容器提供。
HTTP协议
HTTP请求协议
- 请求行:包含请求方法、请求URL和HTTP版本。
- 请求头:包含请求的元数据,如请求的客户端信息、请求的内容类型等。常见的请求头字段如下:
| 请求头字段 | 说明 |
|---|---|
| Host | 请求的主机名 |
| User-Agent | 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 … Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT …) like Gecko |
| Accept | 表示浏览器能接收的资源类型,如text/*, image/或者/*表示所有 |
| Accept-Language | 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页 |
| Accept-Encoding | 表示浏览器可以支持的压缩类型,例如gzip, deflate等 |
| Content-Type | 请求主体的数据类型 |
| Content-Length | 请求主体的大小(单位:字节) |
- 空行:用于分隔请求头和请求体。
- 请求体:包含请求的数据,如表单数据、JSON数据等。
请求数据获取
Web服务器(Tomcat)对HTTP协议的请求数据进行解析,并进行了封装(HttpServletRequest),在调用Controller方法的时候传递给了该方法。这样,就使得程序员不必直接对协议进行操作,让Web开发更加便捷。
HTTP请求示例:
1 | GET /brand/findAll?name=OPPO&status=1 HTTP/1.1 |
Tomcat会将这些请求数据解析并封装到HttpServletRequest对象中,开发者可以通过该对象方便地获取请求信息。
1 |
|
响应数据格式
HTTP响应示例:
1 | HTTP/1.1 200 OK |
HTTP响应协议由以下几部分组成:
响应行:响应数据第一行(协议、状态码、描述)
- 示例:
HTTP/1.1 200 OK - HTTP版本:如HTTP/1.1
- 状态码:三位数字,表示请求的处理结果(如200表示成功,404表示资源未找到)
- 状态消息:对状态码的简短描述
- 示例:
响应头:第二行开始,格式为key: value
- 示例:
Content-Type: application/json - 包含响应的元数据,如内容类型、内容长度、服务器信息等
- 常见的响应头字段:
- Content-Type:响应内容的类型(如text/html、application/json)
- Content-Length:响应内容的长度
- Server:服务器的名称和版本
- Date:响应的日期和时间
- Cache-Control:缓存控制策略
- 示例:
空行:用于分隔响应头和响应体
响应体:最后一部分,存放响应数据
- 示例:
[{"id": 1, "brandName": "阿里巴巴", "companyName": "腾讯计算机系统有限公司", "description": "玩玩玩"}] - 包含响应的实际数据,如HTML页面、JSON数据、图片等
- 示例:
响应状态码
HTTP状态码分为五大类:
| 类别 | 范围 | 描述 |
|---|---|---|
| 1xx | 100-199 | 信息性状态码,表示请求已接收,继续处理 |
| 2xx | 200-299 | 成功状态码,表示请求已成功处理 |
| 3xx | 300-399 | 重定向状态码,表示需要进一步操作才能完成请求 |
| 4xx | 400-499 | 客户端错误状态码,表示请求有错误 |
| 5xx | 500-599 | 服务器错误状态码,表示服务器处理请求时出错 |
常见的状态码:
- 200 OK:请求成功
- 400 Bad Request:请求参数错误
- 401 Unauthorized:未授权
- 403 Forbidden:禁止访问
- 404 Not Found:资源未找到
- 500 Internal Server Error:服务器内部错误
- 503 Service Unavailable:服务器暂时不可用
响应数据设置
在Controller方法中,可以通过HttpServletResponse对象来设置响应数据。
操作案例中的核心
1 | // 标记为Spring MVC的控制器,负责处理HTTP请求 |
- 问题解决
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<properties>
<!-- Source: https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
<!-- 这里要加版本号不加版本号要报错 -->
</dependency>
<!-- Source: https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
<!-- 这里要加版本号不加版本号要报错 -->
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
<!-- 这里要加版本号不加版本号要报错 -->
</exclude> - response 作用将其转换为 JSON 格式并返回给前端
Web分层解耦
控制器(Controller):负责处理HTTP请求,调用服务层(Service)处理业务逻辑,最后将结果返回给前端。
服务层(Service):负责业务逻辑的处理,调用数据访问层(Repository)进行数据操作。
数据访问层(Dao):负责与数据库或其他数据源交互,执行CRUD操作。
调用的时候使用多态的调用形式: UserService userService = new UserServiceImpl();
控制反转(IoC):
- 控制反转是一种设计模式,通过将对象的创建和依赖关系的管理交给容器来实现。
- 容器负责创建对象并管理它们的生命周期,开发人员只需要关注业务逻辑的实现。
- 控制反转的实现方式有两种:基于XML配置文件的方式和基于注解的方式。
依赖注入(DI):
- 依赖注入是控制反转的一种实现方式,通过容器将依赖的对象注入到目标对象中。
- 依赖注入可以分为构造函数注入、Setter方法注入和接口注入等方式。
- 构造函数注入:通过构造函数参数来注入依赖的对象。
- Setter方法注入:通过Setter方法来注入依赖的对象。
- 接口注入:通过接口方法来注入依赖的对象。
Bean对象:
- Bean对象是指在IoC容器中管理的对象,通常是业务逻辑组件、数据访问组件等。
- Bean对象的创建和管理由IoC容器负责,开发人员只需要关注业务逻辑的实现。
- Bean对象的生命周期包括:实例化、依赖注入、初始化、使用、销毁。
Spring IOC & DI 实际应用
Spring IOC和依赖注入在实际开发中的核心应用包括:
- 将Dao及Service层的实现类,交给IOC容器管理
- 为Controller及Service注入运行时所依赖的对象
代码示例
UserController(控制器层)
1 |
|
UserServiceImpl(业务逻辑层)
1 |
|
UserDaoImpl(数据访问层)
1 |
|
实现原理
在这个示例中:
- 使用
@Component注解将UserDaoImpl和UserServiceImpl注册到IOC容器中 - 使用
@RestController注解(特殊的@Component)将UserController注册到IOC容器中 - 使用
@Autowired注解自动注入依赖对象:- 为
UserController注入UserService - 为
UserServiceImpl注入UserDao
- 为
这样,Spring容器会自动管理这些Bean的生命周期和依赖关系,简化了代码开发和维护。
IOC详解
要把某个对象交给IOC容器管理,需要在对应的类上加上如下注解之一:
| 注解 | 说明 | 位置 |
|---|---|---|
| @Component | 声明bean的基础注解 | 不属于以下三类时,用此注解 |
| @Controller | @Component的衍生注解 | 标注在控制层类上 |
| @Service | @Component的衍生注解 | 标注在业务层类上 |
| @Repository | @Component的衍生注解 | 标注在数据访问层类上(由于与mybatis整合,用的少) |
三层架构与IOC容器
在Web应用中,通常采用三层架构:
- Controller(控制层):接收请求,响应数据
- Service(业务层):处理业务逻辑
- Dao(数据访问层):进行数据访问
Spring IOC容器可以管理这三层中的所有组件,通过不同的注解来区分组件类型,使代码结构更加清晰。
组件扫描机制
前面声明bean的四大注解(@Component、@Controller、@Service、@Repository)要想生效,还需要被组件扫描注解@ComponentScan扫描。
该注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication中,默认扫描的范围是启动类所在包及其子包。
项目结构示例
1 | tlias-management/ |
启动类示例
1 |
|
在这个示例中,@SpringBootApplication注解包含了@ComponentScan,会自动扫描com.itheima包及其子包下的所有带有组件注解的类,将它们注册到IOC容器中。
DI详解
基于@Autowired进行依赖注入的常见方式有如下三种:
1. 属性注入
1 |
|
2. 构造函数注入
1 |
|
3. Setter注入
1 |
|
三种注入方式的对比
| 注入方式 | 优点 | 缺点 |
|---|---|---|
| 属性注入 | 简洁,使用方便 | 1. 无法注入final修饰的属性 2. 容易违反单一职责原则 3. 可能导致循环依赖 |
| 构造函数注入 | 1. 可以注入final修饰的属性 2. 避免循环依赖 3. 保证依赖不为空 |
代码相对复杂 |
| Setter注入 | 1. 可以选择性注入 2. 灵活性更高 |
1. 无法注入final修饰的属性 2. 可能导致循环依赖 |
最佳实践:
- Spring 4.x推荐使用构造函数注入
- Spring 5.x推荐使用构造函数注入或属性注入
- 对于必需的依赖,优先使用构造函数注入
- 对于可选的依赖,可以使用Setter注入
@Autowired的注入规则
@Autowired注解默认是按照类型进行注入的。如果存在多个相同类型的bean,将会报出如下错误:
1 | Field userService in com.itheima.controller.UserController required a single bean, but 2 were found: |
解决方法
有三种方法可以解决多个同类型bean的注入问题:
方案一:@Primary
1 |
|
方案二:@Qualifier
1 |
|
方案三:@Resource
1 |
|
三种方案的对比
- @Primary:在bean定义时指定优先级,适合全局默认选择
- @Qualifier:在注入时指定具体的bean名称,需要与@Autowired配合使用
- @Resource:JDK提供的注解,默认按名称注入,也可以指定name属性
注意:@Resource不是Spring特有的注解,而是JSR-250规范中的注解,功能与@Autowired类似但有一些差异。
mysql数据库
DDL-数据库
操作语法
查询所有数据库
1 | show databases; |
查询当前数据库
1 | select database(); |
使用/切换数据库
1 | use 数据库名; |
创建数据库
1 | create database [if not exists] 数据库名 [default charset utf8mb4]; |
删除数据库
1 | drop database [if exists] 数据库名; |
说明:
[if not exists]:创建数据库时的可选条件,如果数据库不存在则创建[default charset utf8mb4]:设置数据库的默认字符集为utf8mb4,支持表情符号[if exists]:删除数据库时的可选条件,如果数据库存在则删除
DDL-表结构-创建
创建表的语法
1 | create table tablename( |
说明:
tablename:要创建的表名字段1 字段类型:定义表的字段名称和数据类型[约束]:可选的字段约束(如PRIMARY KEY, NOT NULL, UNIQUE等)[comment 字段1注释]:可选的字段注释,用于说明字段的含义[comment 表注释]:可选的表注释,用于说明表的用途
约束
约束的定义
约束是作用于表中字段上的规则,用于限制存储在表中的数据。
约束的目的
保证数据库中数据的正确性、有效性和完整性。
约束类型
| 约束 | 描述 | 关键字 |
|---|---|---|
| 非空约束 | 限制字段取值不能为空 | not null |
| 唯一约束 | 保证字段的所有数据都是唯一、不重复的 | unique |
| 主键约束 | 主键是一行数据的唯一标识,要求非空且唯一 | primary key |
| 默认约束 | 保存数据时,如果未指定该字段值,则采用默认值 | default |
| 外键约束 | 让两张表的数据建立连接,保证数据的一致性和完整性 | foreign key |
约束示例
以一个用户表为例,展示各种约束的应用:
| id | username | name | age | gender |
|---|---|---|---|---|
| 1 | qingyifuwang | 韦一笑 | 45 | 男 |
| 2 | baimeiyingwang | 殷天正 | 55 | 男 |
| 3 | jinmaoshiwang | 谢逊 | 50 | 男 |
| 4 | zixiaonvwang | 黛绮丝 | 38 | 女 |
约束说明:
id:主键约束(唯一标识)username:非空约束 + 唯一约束name:非空约束gender:默认约束(默认值:男)
通过这些约束,可以保证表中数据的完整性和一致性,防止无效或错误的数据被插入到表中。
- 主键自增:auto_increment
DDL-表关系-一对多
一对多关系示例
场景:部门与员工的关系(一个部门下有多个员工)。
部门表(dept):
1 | create table dept( |
员工表(emp):
1 | create table emp( |
DDL-表关系-一对一
一对一关系示例
案例:用户与身份证信息的关系
关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他字段放在另一张表中,以提升操作效率
实现:在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)
用户基本信息表(tb_user):
1 | create table tb_user( |
用户身份证信息表(tb_user_card):
1 | create table tb_user_card( |
DDL-表关系-多对多
多对多关系示例
案例:学生与课程的关系
关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择
实现:建立第三张中间表,中间表至少包含两个外键,分别关联双方主键
学生表(tb_student):
1 | create table tb_student( |
课程表(tb_course):
1 | create table tb_course( |
学生课程关系表(tb_student_course):
1 | create table tb_student_course( |
DDL-外键约束
外键约束的添加方式
可以在创建表时或表结构创建完成后,为字段添加外键约束。具体语法如下:
1. 创建表时添加外键约束
1 | create table 表名( |
2. 创建表后添加外键约束
1 | alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(字段名); |
物理外键与逻辑外键
1. 物理外键
概念:使用
foreign key定义外键关联另外一张表。缺点:
- 影响增、删、改的效率(需要检查外键关系)。
- 仅用于单节点数据库,不适用于分布式、集群场景。
- 容易引发数据库的死锁问题,消耗性能。
2. 逻辑外键
概念:在业务层逻辑中,解决外键关联。
优势:
- 不依赖数据库,灵活性高。
- 适用于分布式、集群环境。
- 不会影响数据库性能。
- 避免数据库死锁问题。
在实际开发中,特别是在分布式系统中,推荐使用逻辑外键而不是物理外键。
DDL-表结构-数据类型
字符串类型
| 分类 | 类型 | 大小 | 描述 | 示例 | 优势 |
|---|---|---|---|---|---|
| 字符串类型 | char | 0-255 bytes | 定长字符串 | idcard char(18)phone char(11) |
性能较高 |
| 字符串类型 | varchar | 0-65535 bytes | 变长字符串 | username varchar(50) |
节省磁盘空间 |
| 字符串类型 | tinyblob | 0-255 bytes | 不超过255个字节的二进制数据 | ||
| 字符串类型 | tinytext | 0-255 bytes | 短文本字符串 | ||
| 字符串类型 | blob | 0-65 535 bytes | 二进制形式的长文本数据 | ||
| 字符串类型 | text | 0-65 535 bytes | 长文本数据 | ||
| 字符串类型 | mediumblob | 0-16 777 215 bytes | 二进制形式的中等长度文本数据 | ||
| 字符串类型 | mediumtext | 0-16 777 215 bytes | 中等长度文本数据 | ||
| 字符串类型 | longblob | 0-4 294 967 295 bytes | 二进制形式的极大文本数据 | ||
| 字符串类型 | longtext | 0-4 294 967 295 bytes | 极大文本数据 |
char与varchar的区别:
char(10):固定占用10个字符空间,存储A占10个空间,存储ABC占10个空间varchar(10):最多占用10个字符空间,存储A占1个空间,存储ABC占3个空间
日期类型
| 分类 | 类型 | 大小(byte) | 范围 | 格式 | 描述 | 示例 |
|---|---|---|---|---|---|---|
| 日期类型 | date | 3 | 1000-01-01 至 9999-12-31 | YYYY-MM-DD | 日期值 | birthday date |
| 日期类型 | time | 3 | -838:59:59 至 838:59:59 | HH:MM:SS | 时间值或持续时间 | |
| 日期类型 | year | 1 | 1901 至 2155 | YYYY | 年份值 | |
| 日期类型 | datetime | 8 | 1000-01-01 00:00:00 至 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 | operateTime datetime |
| 日期类型 | timestamp | 4 | 1970-01-01 00:00:01 至 2038-01-19 03:14:07 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值,时间戳 |
datetime与timestamp的区别:
datetime:存储范围更广,占用8字节,不会自动更新timestamp:存储范围较小,占用4字节,会自动更新为当前时间(当插入或更新数据时)
数值类型
MySQL还支持多种数值类型,包括整数类型和浮点类型:
整数类型:
- tinyint:1字节,-128至127或0至255
- smallint:2字节,-32768至32767或0至65535
- mediumint:3字节,-8388608至8388607或0至16777215
- int:4字节,-2147483648至2147483647或0至4294967295
- bigint:8字节,-9223372036854775808至9223372036854775807或0至18446744073709551615
浮点类型:
- float:4字节,单精度浮点数值
- double:8字节,双精度浮点数值
- decimal:可变长度,精确数值(用于财务数据)
注意:选择数据类型时,应根据实际存储需求选择合适的类型,以节省存储空间并提高查询性能。
DDL-表结构-查询、修改、删除
表结构的查询、修改、删除相关语法如下:
查询表结构
1 | show tables; -- 查询当前数据库的所有表 |
修改表结构
1 | -- 添加字段 |
删除表结构
1 | -- 删除字段 |
DML-数据操作
DML-insert(新增数据)
1 | -- 指定字段添加数据 |
DML-update(修改数据)
1 | -- 修改数据 |
DML-delete(删除数据)
1 | -- 删除数据 |
注意:
- DDL语句(数据定义语言)用于定义数据库对象,如数据库、表、约束等
- DML语句(数据操作语言)用于操作数据库中的数据,如插入、修改、删除数据
- DQL语句(数据查询语言)用于查询数据库中的数据
- 执行DML语句时,应谨慎使用where条件,避免误操作影响大量数据
DQL-数据查询
DQL完整语法
1 | select |
DQL查询类型
DQL查询包括以下五种类型:
- 基本查询 (
select...from...) - 条件查询 (
where) - 分组查询 (
group by) - 排序查询 (
order by) - 分页查询 (
limit)
DQL-基本查询
查询多个字段
1 | select 字段1,字段2,字段3 from 表名; |
查询所有字段(通配符)
1 | select * from 表名; |
为查询字段设置别名
1 | select 字段1 [as 别名1], 字段2 [as 别名2] from 表名; |
去除重复记录
1 | select distinct 字段列表 from 表名; |
注意:
select *虽然方便,但在实际开发中应尽量避免使用,建议明确指定需要查询的字段distinct会去除查询结果中所有字段值都相同的记录- 设置别名可以使查询结果更易读,特别是在进行多表查询或使用函数时
DQL-条件查询
条件查询基本语法
1 | select 字段列表 from 表名 where 条件列表 ; |
比较运算符
| 比较运算符 | 功能 |
|---|---|
| > | 大于 |
| >= | 大于等于 |
| < | 小于 |
| <= | 小于等于 |
| = | 等于 |
| <> 或 != | 不等于 |
| between … and … | 在某个范围之内(含最小、最大值) |
| in(…) | 在in之后的列表中的值,多选一 |
| Like 占位符 | 模糊匹配(_匹配单个字符,%匹配任意个字符) |
| is null | 是null |
逻辑运算符
| 逻辑运算符 | 功能 |
|---|---|
| and 或 && | 并且 (多个条件同时成立) |
| or 或 || | 或者 (多个条件任意一个成立) |
| not 或 ! | 非 ,不是 |
条件查询示例
1 | -- 查询年龄大于等于18的用户 |
注意:
- 在SQL中,字符串和日期类型的数据需要使用单引号括起来
like运算符中,_表示匹配单个字符,%表示匹配任意个字符null值不能使用=或!=进行比较,必须使用is null或is not null
5. 聚合函数
5.1 聚合函数概述
聚合函数是将一列数据作为一个整体,进行纵向计算的函数。
5.2 常用聚合函数
| 函数 | 功能 |
|---|---|
| count | 统计数量 |
| max | 最大值 |
| min | 最小值 |
| avg | 平均值 |
| sum | 求和 |
5.3 注意事项
- 所有的聚合函数不参与null的统计
5.4 聚合函数示例
1 | -- 统计员工数量 |
6. 分组查询
6.1 分组查询语法
1 | select 字段列表 from 表名 [where 条件列表] group by 分组字段名 [having 分组后过滤条件]; |
6.2 where与having的区别
| 区别 | where | having |
|---|---|---|
| 执行时机 | 分组之前进行过滤 | 分组之后对结果进行过滤 |
| 判断条件 | 不能对聚合函数进行判断 | 可以对聚合函数进行判断 |
6.3 分组查询示例
1 | -- 根据性别分组,统计男性和女性员工的数量 |
6.4 分组查询注意事项
- 分组查询的查询列表中,只能包含分组字段和聚合函数,不能包含其他字段(除非这些字段在所有分组内的取值都相同)
- 如果需要对分组后的结果进行过滤,必须使用
having子句,而不能使用where子句 where子句用于在分组前对数据进行过滤,having子句用于在分组后对数据进行过滤- 可以对多个字段进行分组,分组字段之间用逗号分隔
1 | -- 根据部门和性别分组,统计各部门各性别的员工数量 |
7. 排序查询
7.1 排序查询语法
1 | select 字段列表 from 表名 [where 条件列表] [group by 分组字段名 having 分组后过滤条件] order by 排序字段 排序方式; |
7.2 排序方式
- 升序:
asc(默认值,可以省略) - 降序:
desc
7.3 排序查询示例
1 | -- 按照工资从低到高排序(升序) |
8. 分页查询
8.1 分页查询语法
1 | select 字段列表 from 表名 [where 条件列表] [group by 分组字段名 having 分组后过滤条件] [order by 排序字段 排序方式] limit 起始索引, 查询记录数; |
8.2 分页查询说明
- 起始索引:从0开始,即第一页的起始索引为0
- 查询记录数:每页显示的记录条数
- 分页查询是数据库方言:不同的数据库实现方式不同,MySQL中使用
LIMIT关键字
8.3 分页查询示例
1 | -- 查询第1页,每页显示5条记录 |
8.4 注意事项
- 如果起始索引为0,可以省略起始索引,直接写
limit 记录数1
2-- 查询第1页,每页显示5条记录(简写形式)
select * from emp limit 5; - 分页查询在实际开发中非常常用,通常用于数据列表的分页显示
- 为了保证分页结果的稳定性,建议在分页查询时添加
order by排序条件
9. 多表查询
9.1 多表查询概述
多表查询:指从多张表中查询数据。
笛卡尔积:指在数学中,两个集合(A集合和B集合)的所有组合情况。(在多表查询时,需要消除无效的笛卡尔积)
9.2 连接查询
连接查询是多表查询的一种重要方式,通过表之间的关联关系查询数据。
9.2.1 内连接
内连接查询的是两张表交集部分的数据。
语法:
隐式内连接:
1
select 字段列表 from 表1, 表2 where 连接条件 ...;
显式内连接:
1
select 字段列表 from 表1 [inner] join 表2 on 连接条件 ...;
示例:
1 | -- A. 查询所有员工的ID,姓名,及所属的部门名称(隐式、显式内连接实现) |
9.2.2 外连接
外连接分为左外连接和右外连接,用于查询一张表的所有数据以及另一张表与之匹配的数据。
外连接语法:
左外连接:
1
select 字段列表 from 表1 left [outer] join 表2 on 连接条件 ...;
右外连接:
1
select 字段列表 from 表1 right [outer] join 表2 on 连接条件 ...;
外连接说明:
左外连接:查询左表所有数据(包括两张表交集部分数据)
- 如果左表中的某条记录在右表中没有匹配的数据,则右表字段显示为
NULL
- 如果左表中的某条记录在右表中没有匹配的数据,则右表字段显示为
右外连接:查询右表所有数据(包括两张表交集部分数据)
- 如果右表中的某条记录在左表中没有匹配的数据,则左表字段显示为
NULL
- 如果右表中的某条记录在左表中没有匹配的数据,则左表字段显示为
示例:
1 | -- 查询所有部门的信息,以及每个部门下的员工信息(如果没有员工,则显示为NULL) |
9.3 子查询
9.3.1 子查询概述
子查询是SQL语句中嵌套的select语句,称为嵌套查询,又称子查询。
形式:
1 | select * from t1 where column1 = (select column1 from t2 ...); |
说明:
- 子查询外部的语句可以是insert / update / delete / select的任何一个,最常见的是select
- 子查询可以嵌套多层使用
9.3.2 子查询的分类
根据子查询返回结果的类型,子查询可以分为:
- 标量子查询:子查询返回的结果为单个值
- 列子查询:子查询返回的结果为一列
- 行子查询:子查询返回的结果为一行
- 表子查询:子查询返回的结果为多行多列
9.3.3 子查询示例
1. 标量子查询
示例:
1 | -- A. 查询最早入职的员工信息 |
2. 列子查询
示例:
1 | -- A. 查询"教研部"和"咨询部"的所有员工信息 |
3. 行子查询
示例:
1 | -- A. 查询与"李华"的薪资及职位位都相同的员工信息: |
4. 表子查询
示例1:
1 | -- 查询每个部门的平均工资,并找出平均工资高于5000的部门 |
示例2:
1 | -- 获取每个部门中薪资最高的员工信息 |
说明:
- 这个示例使用表子查询获取每个部门的最高薪资信息
- 然后将原表与子查询结果进行连接,找出每个部门中薪资等于最高薪资的员工
- 这种方式可以同时获取每个部门的最高薪资和对应的员工详细信息
9.3.4 子查询的使用场景
- 作为查询条件:用于过滤需要的数据
- 作为数据源:用于提供新的查询数据集
- 作为计算字段:用于计算或转换数据
- 作为更新目标:用于更新或删除数据
9.3.5 子查询的注意事项
- 子查询必须用括号括起来
- 标量子查询可以使用比较运算符(=、>、<、>=、<=、<>)
- 列子查询通常使用IN、ANY、ALL等操作符
- 表子查询可以作为FROM子句的数据源,需要使用别名
JDBC
1. JDBC入门程序
1.1 需求
基于JDBC程序,执行update语句:
1 | update user set age = 25 where id = 1 |
1.2 实现步骤
1.2.1 准备工作
- 创建一个Maven项目
- 引入MySQL驱动依赖
- 准备数据库表
user
1.2.2 代码实现
编写JDBC程序,操作数据库
1.3 Maven依赖配置
在pom.xml文件中添加MySQL驱动依赖:
1 | <dependency> |
1.4 JDBC程序实现
1 | import java.sql.Connection; |
1.5 代码详解
注册驱动:
1
Class.forName("com.mysql.cj.jdbc.Driver");
加载并注册MySQL驱动类。在JDBC 4.0之后,这一步可以省略,因为会自动加载驱动。
获取连接:
1
2
3
4String url = "jdbc:mysql://localhost:3306/web01";
String username = "root";
String password = "1234";
Connection connection = DriverManager.getConnection(url, username, password);url:数据库连接地址,格式为jdbc:mysql://主机:端口/数据库名username:数据库用户名password:数据库密码Connection:数据库连接对象,用于建立与数据库的连接
获取SQL执行对象:
1
Statement statement = connection.createStatement();
Statement是SQL语句执行对象,用于发送SQL语句到数据库执行。执行SQL:
1
2String sql = "update user set age = 25 where id = 1";
int i = statement.executeUpdate(sql);executeUpdate()方法用于执行DML语句(INSERT、UPDATE、DELETE)- 返回值是受影响的行数
释放资源:
1
2statement.close();
connection.close();释放数据库资源,先关闭Statement,再关闭Connection。
2. JDBC核心API
2.1 DriverManager
DriverManager是JDBC驱动程序的管理类,主要负责加载、注册JDBC驱动,并根据连接字符串创建数据库连接。
2.1.1 主要功能
- 加载驱动:
Class.forName("com.mysql.cj.jdbc.Driver") - 获取连接:
DriverManager.getConnection(url, username, password)
2.1.2 示例
1 | // 注册驱动(JDBC 4.0后可省略) |
2.2 Connection
Connection是数据库连接对象,负责建立与数据库的连接,并提供创建SQL执行对象的方法。
2.2.1 主要功能
- 创建Statement对象:
connection.createStatement() - 创建PreparedStatement对象:
connection.prepareStatement(sql) - 创建CallableStatement对象:
connection.prepareCall(sql) - 管理事务:提交、回滚、设置事务隔离级别
2.2.2 示例
1 | // 创建Statement对象 |
2.3 Statement
Statement是SQL语句执行对象,用于发送SQL语句到数据库执行。
2.3.1 主要功能
- 执行DML语句:
executeUpdate(sql)- 返回受影响的行数 - 执行DQL语句:
executeQuery(sql)- 返回ResultSet结果集 - 执行任意SQL语句:
execute(sql)- 返回boolean值,表示是否有结果集
2.3.2 示例
1 | // 执行DML语句 |
2.4 ResultSet
ResultSet是查询结果集对象,用于存储和处理查询结果。
2.4.1 主要功能
- 遍历结果集:
next()- 移动到下一行记录 - 获取字段值:
getXxx(字段名)或getXxx(字段索引) - 释放资源:
close()
2.4.2 示例
1 | String sql = "select id, name, age from user"; |
2.5 PreparedStatement
PreparedStatement是Statement的子类,用于执行预编译的SQL语句。它比Statement更加安全和高效,可以防止SQL注入攻击。
2.5.1 主要功能
- 预编译SQL语句:提高执行效率
- 防止SQL注入:通过参数化查询
- 设置参数:
setXxx(参数索引, 参数值) - 执行SQL语句:
executeUpdate()、executeQuery()
2.5.2 PreparedStatement与Statement的区别
| 特性 | Statement | PreparedStatement |
|---|---|---|
| SQL语句 | 静态SQL,每次执行都需要重新解析 | 预编译SQL,只需要解析一次 |
| 性能 | 较低 | 较高 |
| 安全性 | 容易受到SQL注入攻击 | 可以防止SQL注入攻击 |
| 参数处理 | 直接拼接字符串 | 使用参数占位符(?) |
2.5.3 示例
1 | // 创建PreparedStatement对象 |
2.5.4 SQL注入防护
使用Statement时,SQL语句是通过字符串拼接形成的,容易受到SQL注入攻击:
1 | // 不安全的做法 |
使用PreparedStatement可以防止SQL注入:
1 | // 安全的做法 |
3. JDBC事务管理
3.1 事务的概念
事务是一组SQL语句的集合,这些语句要么全部执行成功,要么全部执行失败,是数据库操作的最小工作单元。
3.2 事务的四大特性(ACID)
| 特性 | 描述 |
|---|---|
| 原子性(Atomicity) | 事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败 |
| 一致性(Consistency) | 事务执行前后,数据库的完整性约束没有被破坏 |
| 隔离性(Isolation) | 多个事务并发执行时,一个事务的执行不影响其他事务的执行 |
| 持久性(Durability) | 事务一旦提交,它对数据库的改变就是永久性的 |
3.3 JDBC事务管理API
JDBC通过Connection对象提供事务管理功能:
- 设置自动提交:
connection.setAutoCommit(false)- 关闭自动提交,开启事务 - 提交事务:
connection.commit()- 提交事务,使所有已执行的SQL语句生效 - 回滚事务:
connection.rollback()- 回滚事务,取消所有已执行的SQL语句 - 设置保存点:
connection.setSavepoint()- 设置事务保存点,允许部分回滚 - 回滚到保存点:
connection.rollback(savepoint)- 回滚到指定的保存点
3.4 事务管理示例
1 | Connection connection = null; |
3.5 事务隔离级别
事务隔离级别决定了事务之间的隔离程度,JDBC提供了以下隔离级别:
| 隔离级别 | 描述 | 可能产生的问题 |
|---|---|---|
| TRANSACTION_READ_UNCOMMITTED(读未提交) | 允许读取未提交的数据 | 脏读、不可重复读、幻读 |
| TRANSACTION_READ_COMMITTED(读已提交) | 只能读取已提交的数据 | 不可重复读、幻读 |
| TRANSACTION_REPEATABLE_READ(可重复读) | 保证同一事务中多次读取同一数据的结果一致 | 幻读 |
| TRANSACTION_SERIALIZABLE(串行化) | 最高隔离级别,事务串行执行 | 性能较差 |
3.6 设置事务隔离级别
1 | // 获取当前隔离级别 |
MyBatis
1. 使用MyBatis查询所有用户数据
1.1 准备工作
创建SpringBoot工程、引入MyBatis相关依赖
- 创建SpringBoot工程时,选择MyBatis Framework和MySQL Driver依赖
准备数据库表user和实体类User
数据库表结构:
id username password name age 1 dqiaodai 1234567890 大乔 22 2 xiaqiao 123456 小乔 17 3 luban 123456 鲁班 18 4 zhaoyun 12345678 赵云 27 实体类User:
1
2
3
4
5
6
7
8
9
10public class User {
private Integer id; // ID
private String username; // 用户名
private String password; // 密码
private String name; // 姓名
private Integer age; // 年龄
// getter和setter方法
// ...
}
配置MyBatis
在application.properties文件中配置数据库连接信息:1
2
3
4spring.datasource.url=jdbc:mysql://localhost:3306/web
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=1234
1.2 编写MyBatis程序
编写MyBatis的持久层接口,定义SQL
1 |
|
1.3 辅助配置-配置MyBatis的日志输出
默认情况下,在MyBatis中,SQL语句执行时,我们并看不到SQL语句的执行日志。加入如下配置,即可查看日志:
1 | # mybatis的配置 |
数据库连接池
1. 数据库连接池概述
数据库连接池是一种创建和管理数据库连接的技术,它允许应用程序从一个预创建的连接池中获取连接,使用完毕后将连接归还到池中,而不是每次都创建和销毁连接。
1.1 数据库连接池的优势
- 提高性能:避免频繁创建和销毁数据库连接的开销
- 资源管理:控制数据库连接的数量,防止资源耗尽
- 连接复用:通过连接复用,减少系统开销
- 统一配置:集中管理数据库连接参数
2. 切换到Druid数据库连接池
Druid是阿里巴巴开源的数据库连接池,具有强大的监控功能和扩展性。
2.1 引入Druid依赖
在pom.xml文件中添加Druid依赖:
1 | <dependency> |
2.2 配置Druid数据源
在application.properties文件中配置数据源类型为Druid:
1 | # 数据源类型配置为Druid |
2.3 常用Druid配置参数
可以根据需要添加更多Druid配置参数:
1 | # 初始化时建立物理连接的个数 |
Mybatis-增删改查-新增用户
1. 新增用户需求
需求:添加一个用户到数据库中。
SQL语句示例:
1 | insert into user(username,password,name,age) values('zhouyu','123456','周瑜',20); |
2. Mapper接口实现
2.1 错误示例
不要使用硬编码的SQL语句,这样无法接收动态参数:
1 | // 错误的做法 |
2.2 正确示例
应该使用#{}占位符接收User对象的属性值:
1 | // 正确的做法 |
3. 实现说明
- 参数类型:使用User对象作为方法参数,这样可以一次性传递多个字段的值
- 占位符:使用
#{属性名}的形式引用User对象的属性值,MyBatis会自动从对象中获取对应属性的值 - 返回值:可以使用void,也可以使用Integer表示受影响的行数
4. 测试插入功能
可以在Controller中添加插入接口,或者使用单元测试来测试插入功能:
4.1 添加插入接口
1 |
|
4.2 使用Postman测试插入接口
- 启动SpringBoot应用程序
- 打开Postman,选择POST请求方式
- 输入请求URL:
http://localhost:8080/user/insert - 在Body中选择JSON格式,输入用户信息:
1
2
3
4
5
6{
"username": "zhouyu",
"password": "123456",
"name": "周瑜",
"age": 20
} - 点击发送按钮
- 查看响应结果:”添加成功”
Mybatis-增删改查-修改用户
1. 修改用户需求
需求:根据ID更新用户信息。
SQL语句示例:
1 | update user set username = 'zhouyu', password = '123456', name = '周瑜', age = 20 where id = 1; |
2. Mapper接口实现
1 |
|
3. 实现说明
- 参数类型:使用User对象作为方法参数,包含所有需要更新的字段和ID
- 占位符:使用
#{属性名}引用User对象的属性值 - where条件:必须包含ID条件,否则会更新所有记录
4. 测试修改功能
1 |
|
Mybatis-增删改查-查询用户
1. 查询用户需求
需求:根据用户名和密码查询用户信息。
SQL语句示例:
1 | select * from user where username = 'zhouyu' and password = '666888'; |
2. Mapper接口实现
2.1 直接参数传递
1 |
|
2.2 使用@Param注解
如果方法参数名与SQL中的占位符不一致,可以使用@Param注解为参数起别名:
1 |
|
3. @Param注解说明
@Param注解的作用是为接口的方法形参起名字,便于在SQL语句中引用参数值。
4. 测试查询功能
1 |
|
Mybatis-增删改查-删除操作
1. 使用MyBatis完成删除操作
使用MyBatis完成删除操作的步骤与查询操作类似,但需要使用@Delete注解来定义删除SQL语句。
2. 编写删除操作的Mapper接口方法
在之前创建的UserMapper接口中添加删除操作的方法:
1 |
|
4. 执行删除操作的注意事项
参数传递:删除操作通常需要传递一个或多个参数来确定要删除的记录。在MyBatis中,可以使用
#{}占位符来接收参数。返回值:删除操作的返回值通常是一个整数,表示受影响的行数。如果返回值大于0,则表示删除成功;否则表示删除失败。
事务处理:删除操作是一个DML操作,默认情况下会自动提交事务。如果需要手动控制事务,可以在方法上添加
@Transactional注解。SQL注入防护:MyBatis的
#{}占位符会自动进行参数类型转换和SQL注入防护,因此不需要手动处理。
5. 执行删除操作的示例
5.1 使用Postman测试删除接口
- 启动SpringBoot应用程序
- 打开Postman,选择DELETE请求方式
- 输入请求URL:
http://localhost:8080/user/deleteById?id=1 - 点击发送按钮
- 查看响应结果:”删除成功”
5.2 查看数据库记录变化
执行删除操作后,可以查看数据库中的记录,确认ID为1的用户是否已被删除。
6. 批量删除操作
如果需要同时删除多条记录,可以使用MyBatis的批量删除功能。例如:
1 |
|
注意:使用批量删除时,需要使用<script>标签包裹SQL语句,并使用<foreach>标签遍历参数列表。
6. MyBatis高级特性补充
6.1 获取插入数据后的主键值
在插入数据之后,可以通过以下方式获取到自动生成的主键值:
1 |
|
参数说明:
useGeneratedKeys = true:开启自动生成主键keyProperty = "id":指定主键对应的实体类属性名
执行插入操作后,MyBatis会自动将生成的主键值设置到传入的User对象的id属性中。
6.2 <foreach>动态SQL标签
作用
遍历集合/数组,用于构建动态SQL语句。
属性含义
| 属性 | 描述 | 是否必选 |
|---|---|---|
collection |
集合名称 | 是 |
item |
集合遍历出来的元素/项 | 是 |
separator |
每一次遍历使用的分隔符 | 否 |
open |
遍历开始前拼接的片段 | 否 |
close |
遍历结束后拼接的片段 | 否 |
使用示例
批量删除:
1 |
|
批量插入:
1 |
|
XML映射文件中的批量删除:
1 | <!--根据id删除员工基本信息--> |
注意事项:
- 在使用
<foreach>标签构建IN条件时,open="("和close=")"属性非常重要,它们会在遍历开始和结束时分别拼接左括号和右括号,确保生成的SQL语句语法正确。 - 如果缺少这些属性,生成的SQL语句会变成
WHERE id IN 1,2,3而不是WHERE id IN (1,2,3),导致SQL语法错误。
7. MyBatis中的#和$符号
在MyBatis中,#{}和${}`是两种不同的参数传递方式,它们在实现原理、安全性和使用场景上存在明显区别。
7.1 #{}和${}的区别
| 符号 | 说明 | 场景 | 优缺点 |
|---|---|---|---|
| #{} | 占位符。执行时,会将#{…}替换为?,生成预编译SQL | 参数值传递 | 安全、性能高(推荐) |
| ${} | 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 | 表名、字段名动态设置时使用 | 不安全、性能低 |
7.2 使用示例
7.2.1 #{}的使用示例
1 | // 根据ID删除部门 |
执行时,MyBatis会将#{id}替换为?,生成预编译SQL:
1 | delete from dept where id = ? |
然后将参数值传递给预编译的SQL语句,这样可以防止SQL注入攻击。
7.2.2 ${}的使用示例
1 | // 根据动态表名和排序字段查询数据 |
执行时,MyBatis会直接将参数值拼接到SQL语句中:
1 | select id, name, score from student order by score |
这种方式适用于表名或字段名需要动态设置的场景,但存在SQL注入风险,因此不推荐用于普通参数传递。
7.3 如何选择
- **优先使用#{}**:对于普通参数传递,优先使用
#{},因为它更安全、性能更高 - **谨慎使用${}**:只有在需要动态设置表名、字段名等场景下才使用
${},并且需要确保参数值的安全性
7.4 防止SQL注入
使用${}时,需要特别注意防止SQL注入攻击:
- 参数验证:对传入的参数进行严格验证,确保只包含允许的字符
- 白名单机制:只允许特定的表名或字段名
- 使用预编译:如果可能,尽量使用
#{}代替${}
示例:
1 | // 安全的动态表名查询 |
8. XML映射配置
在MyBatis中,既可以通过注解配置SQL语句,也可以通过XML配置文件配置SQL语句。
8.1 默认规则
文件命名与位置:XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。
命名空间配置:XML映射文件的namespace属性与Mapper接口全限定名一致。
SQL语句配置:XML映射文件中SQL语句的id与Mapper接口中的方法名一致,并保持返回类型一致。
8.2 项目结构示例
1 | mybatis-demo |
8.3 接口与XML示例
**Mapper接口 (UserMapper.java)**:
1 | public interface UserMapper { |
**XML映射文件 (UserMapper.xml)**:
1 | <mapper namespace="com.itheima.mapper.UserMapper"> |
8.4 XML映射的优势
- 复杂SQL支持:对于复杂的SQL语句(如多表关联查询、动态SQL等),XML配置方式更加清晰和易于维护
- SQL语句集中管理:所有SQL语句都集中在XML文件中,便于统一管理和优化
- 代码与SQL分离:业务代码与SQL语句完全分离,提高代码的可读性和可维护性
- 更好的格式化支持:XML文件支持SQL语句的格式化,便于阅读和调试
8.5 注解与XML的选择
- 注解方式:适用于简单的SQL语句,配置简单,开发效率高
- XML方式:适用于复杂的SQL语句,维护性好,可读性强
在实际开发中,可以根据具体情况选择合适的配置方式,也可以混合使用两种方式。
8.6 XML映射文件位置配置
在Spring Boot项目中,可以在application.properties文件中指定XML映射配置文件的位置:
1 | # 指定XML映射配置文件的位置 |
项目结构示例:
1 | spring-boot-mybatis-quickstart |
8.7 MyBatisX插件
MyBatisX是一款基于IDEA的快速开发MyBatis的插件,为效率而生。
主要功能:
- mapper.xml和mapper.java可以互相跳转
- mybatis.xml文件支持代码提示
- mapper接口和xml映射文件支持类似JPA的自动提示和引用
安装方式:
- 打开IDEA的Settings(设置)
- 选择Plugins(插件)
- 在Marketplace中搜索”mybatisx”
- 点击Install(安装)按钮进行安装
- 安装完成后重启IDEA即可使用
9. resultMap
9.1 问题描述
MyBatis在查询时,当实体类的属性名与表的字段名不一致时,会出现无法自动封装数据的问题。
例如:
- 实体类属性:id、username、password、name、age
- 表字段名:id、username、password、real_name、age
此时,由于name与real_name不一致,导致查询结果中name属性为null。
9.2 解决方案
方案一:使用别名
1 | <select id="findAll" resultType="com.itheima.pojo.User"> |
方案二:使用resultMap
resultMap是MyBatis提供的用于解决实体类属性名与表字段名不一致问题的一种方式,它可以定义映射规则,将查询结果中的列映射到实体类的属性中。
使用步骤:
在XML映射文件中定义resultMap
1
2
3
4
5
6
7
8
9
10
11<!-- 定义resultMap,id为映射规则的唯一标识,type为实体类的全限定名 -->
<resultMap id="userResultMap" type="com.itheima.pojo.User">
<!-- id元素用于映射主键,column为表的主键字段,property为实体类的主键属性 -->
<id column="id" property="id"/>
<!-- result元素用于映射普通字段,column为表的字段名,property为实体类的属性名 -->
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="real_name" property="name"/>
<result column="age" property="age"/>
</resultMap>在查询语句中使用resultMap
1
2
3
4<!-- 使用resultMap属性引用定义好的映射规则,而不是使用resultType -->
<select id="findAll" resultMap="userResultMap">
select id, username, password, real_name, age from user
</select>
9.3 resultMap的优势
- 可复用性:定义一次映射规则,可以在多个查询语句中复用
- 灵活性:支持复杂的映射关系(如一对一、一对多、多对多关联查询)
- 可读性:将映射规则集中管理,提高代码的可读性和可维护性
- 性能:比使用别名的方式性能更好,因为别名需要在每次查询时都进行解析
9.4 自动映射
MyBatis默认支持自动映射,即当实体类的属性名与表的字段名一致时,会自动将查询结果封装到实体类中。
自动映射的条件:
- 实体类的属性名与表的字段名一致(不区分大小写)
- 开启自动映射功能(默认开启)
开启自动映射的配置:
1 | # 开启自动映射 |
该配置会将表中的下划线命名(如real_name)自动映射到实体类的驼峰命名(如realName)。
9.5 复杂映射
resultMap还支持复杂的映射关系,如:
- 一对一关联:使用
<association>元素 - 一对多关联:使用
<collection>元素 - 多对多关联:通过中间表实现,结合
<collection>元素
这些高级特性使得MyBatis能够处理复杂的业务场景,满足各种数据查询需求。
9.6 基于注解的结果映射
除了XML映射文件外,MyBatis也支持使用注解进行结果映射,适用于简单的映射场景。
9.6.1 手动结果映射(@Results和@Result)
使用@Results和@Result注解可以实现手动结果映射,用于解决实体类属性名与表字段名不一致的问题。
示例:
1 |
|
9.6.2 使用别名
在SQL语句中,对不一致的列名起别名,使别名与实体类属性名一致。
示例:
1 |
|
9.6.3 开启驼峰命名转换
如果字段名与属性名符合驼峰命名规则,MyBatis可以自动通过驼峰命名规则进行映射。
配置方式(application.yml):
1 | mybatis: |
工作原理:
- 表字段名:
create_time→ 实体类属性名:createTime - 表字段名:
update_time→ 实体类属性名:updateTime - 表字段名:
user_name→ 实体类属性名:userName
9.6.4 三种方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| @Results/@Result | 配置灵活,无需修改SQL | 代码较长,重复配置 | 字段映射关系较少的情况 |
| 别名 | SQL清晰,易于理解 | SQL语句冗余,修改麻烦 | 简单映射场景 |
| 驼峰命名转换 | 配置一次,全局生效 | 仅适用于驼峰命名规则 | 符合驼峰命名规范的项目 |
9.7 最佳实践
- 简单映射:优先使用驼峰命名转换
- 少量特殊映射:使用别名或@Results注解
- 复杂映射:使用XML格式的resultMap
- 统一风格:在项目中保持一致的映射方式
10. YAML配置文件
YAML(YAML Ain’t Markup Language)是一种人类可读的数据序列化语言,常用于配置文件。在Spring Boot项目中,YAML配置文件是一种常用的配置方式。
10.1 YAML格式规则
- 数值格式:数值前边必须有空格,作为分隔符
- 层级关系:使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(IDEA中会自动将Tab转换为空格)
- 缩进要求:缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- 注释格式:使用
#表示注释,从这个字符一直到行尾,都会被解析器忽略
10.2 YAML数据结构
10.2.1 定义对象/Map集合
1 | user: |
10.2.2 定义数组/List/Set集合
1 | hobby: |
10.3 特殊注意事项
在YAML格式的配置文件中,如果配置项的值是以 0 开头的,值需要使用 '' 引起来,因为以0开头在YAML中表示8进制的数据。
10.4 完整示例
application.yml:
1 | # 配置服务相关信息 |
11. 前后端分离开发
11.1 概述
当前最为主流的开发模式:前后端分离
11.2 工作原理
前后端分离开发模式下,前端和后端是两个独立的系统,它们通过接口进行通信:
前端系统:
- 负责用户界面的展示和用户交互
- 技术栈:HTML、CSS、JavaScript(Vue、React、Angular等框架)
- 通常通过NGINX等Web服务器进行部署
后端系统:
- 负责业务逻辑处理、数据存储和管理
- 技术栈:Java(Spring Boot等框架)、数据库等
- 提供RESTful API或GraphQL接口供前端调用
通信方式:
- 前端通过HTTP请求向后端发送数据请求
- 后端处理请求后,返回JSON或XML格式的数据响应
- 前后端通过接口文档约定通信格式和参数
11.3 核心优势
- 职责清晰:前端专注于用户体验,后端专注于业务逻辑
- 并行开发:前后端团队可以同时进行开发,提高开发效率
- 技术栈独立:前后端可以选择适合各自需求的技术栈
- 可维护性高:接口文档清晰记录每一个功能,便于后续维护和扩展
- 易于扩展:前后端可以独立扩展和部署,适应不同的业务需求
11.4 关键角色
- 前端开发者:负责构建用户界面和用户交互逻辑
- 后端开发者:负责实现业务逻辑和数据处理
- 接口设计师:负责设计和维护接口文档
- 测试工程师:负责测试前端功能、后端接口和整个系统的集成
11.5 RESTful API设计
REST (REpresentational State Transfer),表述性状态转换,它是一种软件架构风格。
11.5.1 RESTful设计规范
| REST风格URL | 请求方式 | 含义 | 备注 |
|---|---|---|---|
| http://localhost:8080/users/1 | GET | 查询id为1的用户 | |
| http://localhost:8080/users/1 | DELETE | 删除id为1的用户 | |
| http://localhost:8080/users | POST | 新增用户 | |
| http://localhost:8080/users | PUT | 修改用户 |
11.5.2 设计原则
- URL定位资源:使用URL来唯一标识资源
- HTTP动词描述操作:使用GET/POST/PUT/DELETE等HTTP方法描述对资源的操作
- 简洁、规范、优雅:保持API的简洁性和一致性
11.5.3 重要注意事项
- REST是风格,是约定方式,约定不是规定,可以打破
- 描述功能模块通常使用复数形式(加s),表示此类资源,而非单个资源。如:users、books…






