NewJavaWeb

NewJavaWeb
Silence单元测试
在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-表结构-数据类型
字符串类型
| 分类 | 类型 | 大小 | 描述 | 示例 | 优势 |
|---|---|---|---|---|---|
| 字符串类型 | 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排序条件
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>标签遍历参数列表。
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能够处理复杂的业务场景,满足各种数据查询需求。






