文章目錄
- 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