文章目录
- 1. 前言
- 2. JDBC
- 2.1 介绍
- 2.2 入门程序
- 2.2.1 DataGrip
- 2.2.2 在IDEA执行sql语句
- 2.3 查询数据案例
- 2.3.1 需求
- 2.3.2 准备工作
- 2.3.3 AI代码实现
- 2.3.4 代码剖析
- 2.3.4.1 ResultSet
- 2.3.4.2 预编译SQL
- 2.3.4.2.1 SQL注入
- 2.3.4.2.2 SQL注入解决
- 2.3.4.2.3 性能更高
- 2.4 增删改数据
- 2.4.1 需求
- 2.4.2 代码实现
- 3. Mybatis
- 3.1 介绍
- 3.1.1 快速入门
- 3.1.2 辅助配置
- 3.1.2.1 配置SQL提示
- 3.1.2.2 配置Mybatis日志输出
- 3.1.3 JDBC VS Mybatis
- 3.1.4 数据库连接池
- 3.1.4.1 介绍
- 3.1.4.2 产品
- 3.1.5 增删改查操作
- 3.1.5.1 删除
- 3.1.5.2 新增
- 3.1.5.3 修改
- 3.1.5.4 查询(带条件)
- 3.1.6 XML映射配置
- 3.1.6.1 XML配置文件规范
- 3.1.6.2 XML配置文件实现
- 3.1.6.3 MybatisX的使用
- 4. SpringBoot配置文件
- 4.1 介绍
- 4.2 语法
- 4.3 案例
1. 前言
在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、datagrip),来操作数据库的。
我们做为后端程序开发人员,通常会使用Java程序来完成对数据库的操作。Java程序操作数据库的技术呢,有很多啊,而最为底层、最为基础的就是JDBC。
JDBC:(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。 【是操作数据库最为基础、底层的技术】
但是使用JDBC来操作数据库,会比较繁琐,所以现在在企业项目开发中呢,一般都会使用基于JDBC的封装的高级框架,比如:Mybatis、MybatisPlus、Hibernate、SpringDataJPA。
而这些技术,目前的市场占有份额如下图所示:
从上图中,我们也可以看到,目前最为主流的就是Mybatis,其次是MybatisPlus。
内容:
JDBC
Mybatis
SpringBoot配置文件
2. JDBC
2.1 介绍
JDBC:(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。
本质:
-
sun公司官方定义的一套操作所有关系型数据库的规范,即接口。
-
各个数据库厂商去实现这套接口,提供数据库驱动jar包。
-
我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
那有了JDBC之后,我们就可以直接在java代码中来操作数据库了,只需要编写这样一段java代码,就可以来操作数据库中的数据。 示例代码如下:
2.2 入门程序
需求:基于JDBC,执行更新数据库数据语句
2.2.1 DataGrip
创建数据表
create table user(id int unsigned primary key auto_increment comment 'ID,主键',username varchar(20) comment '用户名',password varchar(32) comment '密码',name varchar(10) comment '姓名',age tinyint unsigned comment '年龄'
) comment '用户表';insert into user(id, username, password, name, age) values (1, 'daqiao', '123456', '大乔', 22),(2, 'xiaoqiao', '123456', '小乔', 18),(3, 'diaochan', '123456', '貂蝉', 24),(4, 'lvbu', '123456', '吕布', 28),(5, 'zhaoyun', '12345678', '赵云', 27);
2.2.2 在IDEA执行sql语句
导入依赖
<dependencies><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.9.3</version><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency></dependencies>
-
mysql-connector-j
(MySQL 连接器)用途:这是 MySQL 数据库的 JDBC 驱动程序,它允许 Java 程序与 MySQL 数据库进行连接和交互。
功能:通过这个依赖,Java 程序可以使用 JDBC(Java Database Connectivity)接口与 MySQL 数据库进行数据操作(如查询、更新、删除等)。
-
junit-jupiter
(JUnit 测试框架)用途:这是 JUnit 5 的核心库,提供了用于编写和执行单元测试的功能。
junit-jupiter
是 JUnit 5 框架的一部分,包含了新的测试注解和断言方法。功能:它允许开发人员编写单元测试,以确保代码的正确性。JUnit 5 允许使用如
@Test
注解标记测试方法,支持断言(如assertEquals()
、assertTrue()
)来验证预期结果。 -
lombok
(Lombok 库)
用途:Lombok 是一个 Java 库,通过注解自动生成常见的 Java 代码(如 getter、setter、toString 方法等),减少了代码冗余。
功能:它通过注解(如 @Getter
、@Setter
、@ToString
、@EqualsAndHashCode
)自动为类生成常见的方法,简化了 Java 类的编写。使用 Lombok,开发者不需要手动编写大量样板代码,可以让代码更加简洁、清晰。
更新代码
import com.mysql.cj.jdbc.Driver;
import org.junit.jupiter.api.Test;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;public class JdbcTest {/*** Jdbc入门程序*/@Testpublic void testUpdate() throws Exception {//1.注册驱动Class.forName("com.mysql.cj.jdbc.Driver");//2.获取数据库连接String url = "jdbc:mysql://localhost:3306/web01";String username = "root";String password = "0524";Connection connection = DriverManager.getConnection(url, username, password);//3.获取sql语句的执行对象Statement statement = connection.createStatement();//4.执行SQLint i = statement.executeUpdate("update user set age = 25 where id = 1");System.out.println("SQL执行完毕影响记录数为:" + i);//5.释放资源statement.close();connection.close();}
}
-
注册驱动
Class.forName()
:是 Java 反射机制中的一个方法,作用是加载并初始化指定类。Class.forName()
会通过类的全限定名(包括包名)加载指定的类,并且会执行该类的静态初始化块。"com.mysql.cj.jdbc.Driver"
:是 MySQL JDBC 驱动类的全限定类名(包括包名)。它是 MySQL 的驱动程序类,负责将 Java 程序与 MySQL 数据库之间的通信进行处理。获取数据库连接connection = DriverManager.getConnection
使用connection进行数据库连接,需要url地址、用户、密码"jdbc:mysql://localhost:3306/web01";
:jdbc:mysql://
:这是 JDBC (Java Database Connectivity) 的协议部分,表示这是一个 MySQL 数据库连接。localhost
:这是数据库的主机名,表示数据库在本地计算机上运行。3306
:这是 MySQL 数据库的默认端口号,表示数据库服务监听的端口。web01
:这是数据库的名称,表示要连接的数据库是名为web01
的数据库。
-
获取sql语句执行对象
statement
执行更新sql语句,并返回影响几条记录(int) -
执行SQL语句
-
executeUpdate
用于执行修改数据库内容的 SQL 语句(如INSERT
、UPDATE
、DELETE
)。 -
executeQuery
用于执行选择SELECT
语句,查询数据库并返回结果集ResultSet
,下一节会用到ResultSet rs = statement.executeQuery("SELECT * FROM users");
-
execute()
:执行任何 SQL 语句,无论是否返回结果集。该方法适用于SELECT
、INSERT
、UPDATE
、DELETE
等多种语句。返回值:返回一个布尔值,指示执行的 SQL 是否返回一个结果集。
-
-
执行完毕释放资源
statement.close();
和connection.close();
用于关闭数据库连接和声明,以确保资源得到妥善释放。- 如果不关闭
statement
,可能会导致资源泄漏,特别是对于数据库连接的声明,可能会影响性能和稳定性。 - 数据库连接是宝贵的资源,如果不及时关闭
connection
,可能会耗尽数据库连接池的可用连接,导致系统性能下降或连接异常。
- 如果不关闭
最终结果
2.3 查询数据案例
2.3.1 需求
需求:基于JDBC实现用户登录功能。用户登录就是你输入的账号名和密码可以在数据库中使用SELECT
语句查询到,能查到就放你登录
本质:其本质呢,其实就是基于JDBC程序,执行如下select语句,并将查询的结果输出到控制台。SQL语句:
select * from user where username = 'linchong' and password = '123456';
2.3.2 准备工作
1). 创建一个maven项目
2). 创建一个数据库 web,并在该数据库中创建user表
create table user(id int unsigned primary key auto_increment comment 'ID,主键',username varchar(20) comment '用户名',password varchar(32) comment '密码',name varchar(10) comment '姓名',age tinyint unsigned comment '年龄'
) comment '用户表';insert into user(id, username, password, name, age) values (1, 'daqiao', '123456', '大乔', 22),(2, 'xiaoqiao', '123456', '小乔', 18),(3, 'diaochan', '123456', '貂蝉', 24),(4, 'lvbu', '123456', '吕布', 28),(5, 'zhaoyun', '12345678', '赵云', 27);
2.3.3 AI代码实现
AI提示语句
你是一名java开发工程师,帮我基于JDBC程序来操作数据库,执行如下SQL语句:select id,username,password,name,age from user where username = ‘daqiao’ and password = ‘123456’;
并将查询的每一行记录,都封装到实体类User中,然后将User对象的数据输出到控制台中。
User 实体类属性如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id; //ID
private String username; //用户名
private String password; //密码
private String name; //姓名
private Integer age; //年龄
}
具体的代码为:
1). 在 pom.xml 文件中引入依赖
<dependencies><!-- MySQL JDBC driver --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.9.3</version><scope>test</scope></dependency>
</dependencies>
2). 在 src/main/test/java
目录下编写测试类,定义测试方法
从 JDBC 4.0 开始,Java 对 JDBC 驱动程序进行了改进,允许驱动程序自动注册。也就是说,只要你把适当的 JDBC 驱动 JAR 包(例如
mysql-connector-java
)添加到项目的 classpath 中,JDBC 驱动会自动加载,无需显式地调用Class.forName()
来加载驱动。
@Testpublic void testSelect() throws Exception {//获取连接Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/web01","root","0524");//创建预编译的PreparedStatement对象PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM user WHERE username = ? and password = ?");//设置参数pstmt.setString(1,"daqiao");pstmt.setString(2,"123456");//执行查询ResultSet rs = pstmt.executeQuery();//处理解析返回的数据while(rs.next()){int id = rs.getInt("id");String username = rs.getString("username");String password = rs.getString("password");String name = rs.getString("name");int age = rs.getInt("age");System.out.println(id + " " + username + " " + password + " " + name + " " + age);}//释放资源rs.close();pstmt.close();connection.close();}}
下一节会讲解返回值rs处理部分
输出结果

而上述的单元测试中,我们在SQL语句中,将将 用户名 和密码的值都写死了,而这两个值应该是动态的,是将来页面传递到服务端的。 那么,我们可以基于前面所讲解的JUnit中的参数化测试进行单元测试,代码改造如下:
public class JDBCTest {/*** 编写JDBC程序, 查询数据*/@ParameterizedTest@CsvSource({"daqiao,123456"})public void testJdbc(String _username, String _password) throws Exception {// 获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234");// 创建预编译的PreparedStatement对象PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?");// 设置参数pstmt.setString(1, _username); // 第一个问号对应的参数pstmt.setString(2, _password); // 第二个问号对应的参数// 执行查询ResultSet rs = pstmt.executeQuery();// 处理结果集while (rs.next()) {int id = rs.getInt("id");String uName = rs.getString("username");String pwd = rs.getString("password");String name = rs.getString("name");int age = rs.getInt("age");System.out.println("ID: " + id + ", Username: " + uName + ", Password: " + pwd + ", Name: " + name + ", Age: " + age);}// 关闭资源rs.close();pstmt.close();conn.close();}}
如果在测试时,需要传递一组参数,可以使用 @CsvSource
注解。
2.3.4 代码剖析
2.3.4.1 ResultSet
ResultSet(结果集对象):封装了DQL查询语句查询的结果。
-
next():将光标从当前位置向前移动一行,并判断当前行是否为有效行,返回值为boolean。
-
true:有效行,当前行有数据
-
false:无效行,当前行没有数据
-
所以可以while循环逐行读取数据,直到为空结束
-
-
getXxx(…):获取数据,可以根据列的编号获取,也可以根据列名获取(推荐)。
结果解析步骤:
2.3.4.2 预编译SQL
其实我们在编写SQL语句的时候,有两种风格:
- 静态SQL(参数硬编码)
conn.prepareStatement("SELECT * FROM user WHERE username = 'daqiao' AND password = '123456'");
ResultSet resultSet = pstmt.executeQuery();
这种呢,就是参数值,直接拼接在SQL语句中,参数值是写死的。
- 预编译SQL(参数动态传递)
conn.prepareStatement("SELECT * FROM user WHERE username = ? AND password = ?");
pstmt.setString(1, "daqiao");
pstmt.setString(2, "123456");
ResultSet resultSet = pstmt.executeQuery();
这种呢,并未将参数值在SQL语句中写死,而是使用 ? 进行占位,然后再指定每一个占位符对应的值是多少,而最终在执行SQL语句的时候,程序会将SQL语句(SELECT * FROM user WHERE username = ? AND password = ?),以及参数值(“daqiao”, “123456”)都发送给数据库,然后在执行的时候,会使用参数值,将?占位符替换掉。
那这种预编译的SQL,也是在项目开发中推荐使用的SQL语句。主要的作用有两个:
-
防止SQL注入,更加安全
-
性能更高
那接下来,我们就来介绍一下这两点。
2.3.4.2.1 SQL注入
- SQL注入:通过控制输入来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
SQL注入最典型的场景,就是用户登录功能。
注入演示:
1). 打开资料中的文件夹 资料/02. SQL注入演示
,运行其中的jar包 sql_Injection_demo-0.0.1-SNAPSHOT.jar
,进入该目录后,执行命令:
java -jar sql_Injection_demo-0.0.1-SNAPSHOT.jar
2). 打开浏览器访问 http://localhost:9090/ ,必须登录后才能访问到系统。我们先测试正常的用户名和密码
3). 接下来,我们再来测试一下错误的用户名和密码 。
我们看到,如果用户名密码错误,是不能进入到系统中进行访问的,会提示 用户名和密码错误
。
4). 那接下来,我们就要演示一下SQL注入现象,我们可以通过控制表单输入,来修改事先定义好的SQL语句的含义。 从而来攻击服务器。
点击登录后,我们看到居然可以成功进入到系统中。
为什么会出现这种现象呢?
在进行登录操作时,怎么样才算登录成功呢? 如果我们查询到了数据,就说明用户名密码是对的。 如果没有查询到数据,就说明用户名或密码错误。
而出现上述现象,原因就是因为,我们我们编写的SQL语句是基于字符串进行拼接的 。 我们输入的用户名无所谓,比如:shfhsjfhja
,而密码呢,就是我们精心设计的,如:' or '1' = '1
。
那最终拼接的SQL语句,如下所示:
我们知道,or
连接的条件,是或的关系,两者满足其一就可以。 所以,虽然用户名密码输入错误,也是可以查询返回结果的,而只要查询到了数据,就说明用户名和密码是正确的。
2.3.4.2.2 SQL注入解决
而通过预编译SQL(select * from user where username = ? and password = ?),就可以直接解决上述SQL注入的问题。 接下来,我们再来演示一下,通过预编译SQL是否能够解决SQL注入问题。
1). 打开资料中的文件夹 资料/02. SQL注入演示
,运行其中的jar包 sql_prepared_demo-0.0.1-SNAPSHOT.jar
,进入该目录后,执行命令:
java -jar sql_prepared_demo-0.0.1-SNAPSHOT.jar
2). 打开浏览器访问 http://localhost:9090/
,必须登录后才能访问到系统 。我们先测试正常的用户名和密码
3). 那接下来,我们就要演示一下是否可以基于上述的密码 ' or '1' = '1
,来完成SQL注入 。
通过控制台,可以看到输入的SQL语句,是预编译SQL语句。
而在预编译SQL语句中,当我们执行的时候,会把整个' or '1'='1
作为一个完整的参数,赋值给第2个问号(' or '1'='1
进行了转义,只当做字符串使用)
那么此时再查询时,就查询不到对应的数据了,登录失败。
注意:在以后的项目开发中,我们使用的基本全部都是预编译SQL语句。
2.3.4.2.3 性能更高
数据库会把以前用过的sql语句直接到缓存里面拿(数据库内存),不需要在重新进行语法检查 — 编译。
2.4 增删改数据
2.4.1 需求
-
需求:基于JDBC程序,执行如下update语句。
-
SQL:
update user set password = '123456', gender = 2 where id = 1;
2.4.2 代码实现
AI提示词(prompt):
你是一名java开发工程师,帮我基于JDBC程序来操作数据库,执行如下SQL语句:
update user set password = ‘123456’, gender = 2 where id = 1;
代码实现如下:
@ParameterizedTest
@CsvSource({"1,123456,25"})
public void testUpdate(int userId, String newPassword, int newAge) throws Exception {// 建立数据库连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web", "root", "1234");// SQL 更新语句String sql = "UPDATE user SET password = ?, age = ? WHERE id = ?";// 创建预编译的PreparedStatement对象PreparedStatement pstmt = conn.prepareStatement(sql);// 设置参数pstmt.setString(1, newPassword); // 第一个问号对应的参数pstmt.setInt(2, newAge); // 第二个问号对应的参数pstmt.setInt(3, userId); // 第三个问号对应的参数// 执行更新int rowsUpdated = pstmt.executeUpdate();// 输出结果System.out.println(rowsUpdated + " row(s) updated.");// 关闭资源pstmt.close();conn.close();
}
JDBC程序执行DML语句:int rowsUpdated = pstmt.executeUpdate(); //返回值是影响的记录数
JDBC程序执行DQL语句:ResultSet resultSet = pstmt.executeQuery(); //返回值是查询结果集
3. Mybatis
3.1 介绍
什么是MyBatis?
-
MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。
-
MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
-
官网:https://mybatis.org/mybatis-3/zh/index.html
在上面我们提到了两个词:一个是持久层,另一个是框架。
- 持久层:指的是就是数据访问层(dao),是用来操作数据库的。
- 框架:是一个半成品软件,是一套可重用的、通用的、软件基础代码模型。在框架的基础上进行软件开发更加高效、规范、通用、可拓展。
通过Mybatis就可以大大简化原生的JDBC程序的代码编写,比如 通过 select * from user
查询所有的用户数据,通过JDBC程序操作呢,需要大量的代码实现,而如果通过Mybatis实现相同的功能,只需要简单的三四行就可以搞定。
3.1.1 快速入门
需求:使用Mybatis查询所有用户数据 。

1). 创建springboot工程,并导入 mybatis的起步依赖、mysql的驱动包、lombok。
项目工程创建完成后,自动在pom.xml文件中,导入Mybatis依赖和MySQL驱动依赖。如下所示:
2). 数据准备:创建用户表user,并创建对应的实体类User。
//User类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private Integer id;private String username;private String password;private String name;private Integer age;
}
3). 配置Mybatis
在 application.properties
中配置数据库的连接信息,数据库连接四要素,只需定义一次。
#数据库访问的url地址
spring.datasource.url=jdbc:mysql://localhost:3306/web01
#数据库驱动类类名
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#访问数据库-用户名
spring.datasource.username=root
#访问数据库-密码
spring.datasource.password=root@0524
上述的配置,可以直接复制过去,不要敲错了。 全部都是 spring.datasource.xxxx
开头。
4). 编写Mybatis程序:编写Mybatis的持久层接口,定义SQL语句(注解)
在创建出来的springboot工程中,在引导类所在包下,在创建一个包 mapper
。在 mapper
包下创建一个接口 UserMapper
,这是一个持久层接口(Mybatis的持久层接口规范一般都叫 XxxMapper,也叫做Mapper接口)。
UserMapper接口的内容如下:
import com.cyanm.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper // 应用程序在运行时,会自动的为该接口创建一个实现类对象(动态代理对象),并且会自动地将该实现类对象存入IOC容器中 - bean
public interface UserMapper {@Select("select * from user")public List<User> findAll();
}
注解说明:
- @Mapper注解:表示是mybatis中的Mapper接口
程序运行时,框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理
- @Select注解:代表的就是select查询,用于书写select查询语句
5). 单元测试
在创建出来的SpringBoot工程中,在src下的test目录下,已经自动帮我们创建好了测试类 ,并且在测试类上已经添加了注解 @SpringBootTest
,代表该测试类已经与SpringBoot整合。
该测试类在运行时,会自动通过引导类加载Spring的环境(IOC容器)。我们要测试那个bean对象,就可以直接通过@Autowired
注解直接将其注入进行,然后就可以测试了。
测试类代码如下:
import com.cyanm.mapper.UserMapper;
import com.cyanm.pojo.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 // SpringBoot中单元测试的注解 - 当前测试类中的测试方法运行时,会启动springboot项目 - IOC容器就创建好了
class SpringbootMybatisQuickstartApplicationTests {//注入UserMapper对象@Autowiredprivate UserMapper userMapper;@Testvoid contextLoads() {//调用方法List<User> userList = userMapper.findAll();userList.forEach(System.out::println);}
}
运行结果:
注意:测试类所在包,需要与引导类/启动类所在包相同。
Spring Boot的包扫描机制
Spring Boot的核心特性之一是其自动配置和组件扫描机制。在应用启动时,Spring Boot会根据引导类的位置进行包扫描。具体来说:
- 扫描范围:Spring Boot会从引导类所在的包开始,递归扫描该包及其所有子包。
- 扫描目标:扫描过程中,Spring Boot会查找并注册所有带有特定注解的类,例如:
@Component
(及其派生注解,如@Service
、@Repository
、@Controller
等)@Mapper
(MyBatis中用于标记Mapper接口的注解)- 其他Spring支持的注解
- 目的:通过这种机制,Spring Boot能够自动将这些类注册为Spring容器中的Bean,从而在应用运行时可以直接注入和使用。
例如,如果引导类位于
com.example.demo
包中,Spring Boot会扫描com.example.demo
及其子包(如com.example.demo.mapper
、com.example.demo.service
等),但不会扫描com.example.test
这样的外部包。
3.1.2 辅助配置
3.1.2.1 配置SQL提示
默认我们在UserMapper接口上加的 @Select
注解中编写SQL语句是没有提示的。 如果想让idea给我们提示对应的SQL语句,我们需要在IDEA中配置与MySQL数据库的链接。
默认我们在UserMapper接口上的 @Select
注解中编写SQL语句是没有提示的。如果想让idea给出提示,可以做如下配置:
配置完成之后,发现SQL语句中的关键字有提示了,但还存在不识别表名(列名)的情况:
-
产生原因:Idea和数据库没有建立连接,不识别表信息
-
解决方案:在Idea中配置MySQL数据库连接
按照如下方如下方式,来配置当前IDEA关联的MySQL数据库(必须要指定连接的是哪个数据库)。
在配置的时候指定连接那个数据库,如上图所示连接的就是mybatis数据库(自己的数据库名是什么就指定什么)。
注意:该配置的目的,仅仅是为了在编写SQL语句时,有语法提示(写错了会报错),不会影响运行,即使不配置也是可以的。
3.1.2.2 配置Mybatis日志输出
默认情况下,在Mybatis中,SQL语句执行时,我们并看不到SQL语句的执行日志。 在application.properties
加入如下配置,即可查看日志:
#mybatis的配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
打开上述开关之后,再次运行单元测试,就可以看到控制台输出的SQL语句是什么样子的。
3.1.3 JDBC VS Mybatis
JDBC程序的缺点:
-
url、username、password 等相关参数全部硬编码在java代码中。
-
查询结果的解析、封装比较繁琐。
-
每一次操作数据库之前,先获取连接,操作完毕之后,关闭连接。 频繁的获取连接、释放连接造成资源浪费。
分析了JDBC的缺点之后,我们再来看一下在mybatis中,是如何解决这些问题的:
-
数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件 application.properties中,配置文件不用重新编译
-
查询结果的解析及封装,由mybatis自动完成映射封装,我们无需关注
-
在mybatis中使用了数据库连接池技术,从而避免了频繁的创建连接、销毁连接而带来的资源浪费。
使用SpringBoot+Mybatis的方式操作数据库,能够提升开发效率、降低资源浪费
而对于Mybatis来说,我们在开发持久层程序操作数据库时,需要重点关注以下两个方面:
- application.properties
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/web01
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234-
- Mapper接口(编写SQL语句)
@Mapper
public interface UserMapper {@Select("select * from user")public List<User> list();
}
3.1.4 数据库连接池
在前面我们所讲解的mybatis中,使用了数据库连接池技术,避免频繁的创建连接、销毁连接而带来的资源浪费。
下面我们就具体的了解下数据库连接池。
3.1.4.1 介绍
1). 没有数据库连接池的情况
客户端执行SQL语句:要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行后又需要关闭连接对象从而释放资源,每次执行SQL时都需要创建连接、销毁链接,这种频繁的重复创建销毁的过程是比较耗费计算机的性能。
2). 有数据库连接池的情况
数据库连接池是个容器,负责分配、管理数据库连接(Connection)
- 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象
允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
- 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)
释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
- 客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象
数据库连接池的好处:
-
资源重用
-
提升系统响应速度
-
避免数据库连接遗漏
3.1.4.2 产品
要怎么样实现数据库连接池呢?
-
官方(sun)提供了数据库连接池标准(javax.sql.DataSource接口)
-
功能:获取连接
public Connection getConnection() throws SQLException;
-
第三方组织必须按照DataSource接口实现
常见的数据库连接池:C3P0 、DBCP 、Druid 、Hikari (springboot默认)
现在使用更多的是:Hikari、Druid (性能更优越)
1). Hikari(追光者) [默认的连接池]
从控制台输出的日志,我们也可以看出,springboot底层默认使用的数据库连接池就是 Hikari。
2). Druid(德鲁伊)
-
Druid连接池是阿里巴巴开源的数据库连接池项目
-
功能强大,性能优秀,是Java语言最好的数据库连接池之一
如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要完成以下两步操作即可:
参考官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
①. 在pom.xml
文件中引入依赖
<dependency><!-- Druid连接池依赖 --><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.19</version>
</dependency>
②. 在application.properties
中引入数据库连接配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/web
spring.datasource.druid.username=root
spring.datasource.druid.password=1234
配置完毕之后,我们再次运行单元测试,大家会看到控制台输出的日志中,已经将连接池切换为了 Druid连接池。
3.1.5 增删改查操作
3.1.5.1 删除
-
需求:根据ID删除用户信息
-
SQL:delete from user where id = 5;
-
Mapper接口方法:
- 方式一:
/*** 根据id删除*/ @Delete("delete from user where id = 5") public void deleteById();
这种方式执行删除操作,调用deleteById方法只能删除id为5的用户信息,因为将id直接写死在代码中了,不可取。
在Mybatis中,我们可以通过参数占位符号 #{...}
来占位,在调用deleteById
方法时,传递的参数值,最终会替换占位符。
@Delete("delete from user where id = #{id}")
public void deleteById(Integer id);
-
编写单元测试方法进行测试
在单元测试类中,增加如下测试方法.
@Test public void testDeleteById(){userMapper.deleteById(36); }
运行单元测试,结果如下:
运行之后,我们发现,#{...}
占位符,其实最终被替换成了**?占位符** ,生成的是预编译的SQL语句。【推荐】
-
DML语句执行完毕,是有返回值的,我们可以为Mapper接口方法定义返回值来接收,如下:
/*** 根据id删除*/ @Delete("delete from user where id = #{id}") public Integer deleteById(Integer id);
Integer类型的返回值,表示DML语句执行完毕影响的记录数。
-
Mybatis的提供的符号,有两个,一个是
#{...}
,另一个是${...}
,区别如下:
符号 | 作用 | 用法说明 | 安全性 | 性能 |
---|---|---|---|---|
#{...} | 预编译参数占位符 | 将参数值通过 JDBC 的 PreparedStatement 设置到 SQL 中,使用预编译方式防止 SQL 注入。 | 高 | 高(因为使用了预编译) |
${...} | 字符串替换占位符 | 将参数值直接拼接到 SQL 字符串中,不进行预编译,适用于动态表名、列名等场景,但容易引发 SQL 注入问题。 | 低 | 低(不进行预编译,直接拼接) |
那在企业项目开发中,强烈建议使用 #{…} 。
预编译原理:MyBatis 使用 #{...}
进行预编译时,会将 SQL 语句和参数分开处理。这样,MyBatis 会把参数值绑定到 SQL 语句中的占位符,而不会直接插入到 SQL 语句的结构中(例如表名、列名、运算符等)。这意味着,#{...}
只能用于动态值(如字符串、数字等),而不能修改 SQL 的结构。
3.1.5.2 新增
-
需求:添加一个用户
-
SQL:insert into user(username,password,name,age) values(‘zhouyu’,‘123456’,‘周瑜’,20);
-
Mapper接口:
/*** 添加用户*/
@Insert("insert into user(username,password,name,age) values(#{username},#{password},#{name},#{age})")
public void insert(User user);
如果在SQL语句中,我们需要传递多个参数,我们可以把多个参数封装到一个对象中。然后在SQL语句中,我们可以通过#{对象属性名}
的方式,获取到对象中封装的属性值。注意不是字段名
- 单元测试:
在测试类中添加测试方法,代码如下:
@Test
public void testInsert(){User user = new User();user.setUsername("admin");user.setPassword("123456");user.setName("管理员");user.setAge(30);userMapper.insert(user);
}
运行结果如下:
3.1.5.3 修改
-
需求:根据ID更新用户信息
-
SQL:update user set username = ‘zhouyu’, password = ‘123456’, name = ‘周瑜’, age = 20 where id = 1;
-
Mapper接口方法:注意是属性名!!!跟字段名没关系
/*** 根据id更新用户信息*/
@Update("update user set username = #{username},password = #{password},name = #{name},age = #{age} where id = #{id}")
public void update(User user);
- 单元测试:
在测试类中添加测试方法,代码如下:
@Test // 全参构造也可以
public void testUpdate(){User user = new User();user.setId(6);user.setUsername("admin666");user.setPassword("123456");user.setName("管理员");user.setAge(30);userMapper.update(user);
}
运行结果如下:
3.1.5.4 查询(带条件)
-
需求:根据用户名和密码查询用户信息
-
SQL:select * from user where user name = ‘zhouyu’ and password = ‘123456’;
-
Mapper接口方法:
/*** 根据用户名和密码查询用户信息*/
@Select("select * from user where username = #{username} and password = #{password}") //注意这里是为形参起的名字而不是方法形参名称
public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
@param注解的作用是为接口的方法形参起名字的。(由于用户名唯一的,所以查询返回的结果最多只有一个,可以直接封装到一个对象中)
在默认的情况下,Java 编译器会去除方法参数的名称,只保留参数的类型。这意味着编译后的字节码中可能没有方法参数的原始名字(例如
username
和password
),因此 MyBatis 无法直接通过这些参数名称来引用它们。之前增删改只有一个形参,现在不止一个形参
- 单元测试:
在测试类中添加测试方法,代码如下:
@Test
public void testFindByUsernameAndPassword(){User user = userMapper.findByUsernameAndPassword("admin666", "123456");System.out.println(user);
}
运行结果如下:

说明:基于官方骨架创建的springboot项目中,接口编译时会保留方法形参名,@Param注解可以省略 (#{形参名})。
这里直接写形参名即可
如果用阿里框架还不给起名字,报错如下
字节码文件:形参名没有被保留,只保留类型

起了名字之后的字节码文件
注意报错不要从上往下读,直接找Caused by
3.1.6 XML映射配置
Mybatis的开发有两种方式:
-
注解: 如@Select @Update等等
-
XML
3.1.6.1 XML配置文件规范
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。
在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:
XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下**(同包同名)**
XML映射文件的namespace属性为Mapper接口全限定名一致
- XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致(注意是单条返回类型)。
<select>标签:就是用于编写select查询语句的。
- resultType属性,指的是查询返回的单条记录所封装的类型。
- id属性:这个sql语句的唯一标识,且与方法名命名一致
3.1.6.2 XML配置文件实现
第1步: 创建XML映射文件
这里不要用 .
第2步:编写XML映射文件
xml映射文件中的dtd约束,直接从mybatis官网复制即可; 或者直接AI生成。这个是固定的,不用记
官方:https://mybatis.net.cn/getting-started.html
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace=""></mapper>
第3步:配置
a. XML映射文件的namespace属性为Mapper接口全限定名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper"></mapper>
b. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper"><!--查询操作--><select id="findAll" resultType="com.itheima.pojo.User">select * from user</select></mapper>
resultType 属性的值,与查询返回的单条记录封装的类型一致。
运行测试类,执行结果:
匹配规则
- 一旦调用了UserMapper中的方法,就会去查询要执行的sql语句在哪个映射文件中定义,就回去查找哪份XML文件的namesapce属性是我Mapper接口的全类名,进行匹配
- 一份XML文件中可以有多条sql语句,那我们怎么知道调用那条呢?
根据方法名和id名匹配来调用,
注意:一个接口方法对应的SQL语句,要么使用注解配置,要么使用XML配置,切不可同时配置。
开发规范
3.1.6.3 MybatisX的使用
1. 指定配置XML映射文件位置
mybatis.mapper-locations=classpath:mapper/*.xml
java目录和resources目录在编译后都会放在class目录下,所以先指定一下最后位置
2.MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生。
MybatisX的安装:
可以通过MybatisX快速定位:
MybatisX的使用在后续学习中会继续分享。
- 学习了Mybatis中XML配置文件的开发方式了,大家可能会存在一个疑问:到底是使用注解方式开发还是使用XML方式开发?
官方说明:https://mybatis.net.cn/getting-started.html。下面是官方说明:
结论:使用Mybatis的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句。
4. SpringBoot配置文件
4.1 介绍
前面我们一直使用springboot项目创建完毕后自带的application.properties
进行属性的配置,而如果在项目中,我们需要配置大量的属性,采用properties配置文件这种 key=value
的配置形式,就会显得配置文件的层级结构不清晰,也比较臃肿。
那其实呢,在springboot项目当中是支持多种配置方式的,除了支持properties配置文件以外,还支持另外一种类型的配置文件,就是我们接下来要讲解的yml格式的配置文件。yml格式配置文件名字为:application.yaml
, application.yml
这两个配置文件的后缀名虽然不一样,但是里面配置的内容形式都是一模一样的。
我们可以来对比一下,采用 application.properties
和 application.yml
来配置同一段信息(数据库连接信息),两者之间的配置对比:yml格式冗余部分简洁化了
在项目开发中,我们推荐使用application.yml配置文件来配置信息,简洁、明了、以数据为中心。
4.2 语法
简单的了解过springboot所支持的配置文件,以及不同类型配置文件之间的优缺点之后,接下来我们就来了解下yml配置文件的基本语法:
- 大小写敏感
- key和value用冒号分开,但value前必须有空格,作为分隔符
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格,所以正常用)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
#
表示注释,从这个字符一直到行尾,都会被解析器忽略
了解完yml格式配置文件的基本语法之后,接下来我们再来看下yml文件中常见的数据格式。在这里我们主要介绍最为常见的两类:
-
定义对象或Map集合
-
定义数组、list或set集合
- 对象/Map集合
user:name: zhangsanage: 18password: 123456
- 数组/List/Set集合
-
后面加各个元素的值
hobby: - java #元素1- game #元素2- sport #元素3
在yml格式的配置文件中,如果配置项的值是以 0 开头的,值需要使用 ‘’ 引起来,因为以0开头在yml中表示8进制的数据。
4.3 案例
熟悉完了yml文件的基本语法后,我们修改下之前案例中使用的配置文件,变更为application.yml配置方式:
-
修改application.properties名字为:
_application.properties
(名字随便更换,只要加载不到即可) -
创建新的配置文件:
application.yml
- 原有的
application.properties
配置文件
- 新建的
application.
yml 配置文件
配置文件的内容如下:
#数据源配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/web01username: rootpassword: root@1234
#mybatis配置
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl