Java EE 進階:MyBatis

MyBatis是一個優秀的持久化框架,用于簡化JDBC的開發。

持久層就是持久化訪問的層,就是數據訪問層(Dao),用于訪問數據庫的。

MyBatis使用的準備工作

創建項目,導入mybatis的啟動依賴,mysql的驅動包

在pom文件中生成依賴

        <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.4</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency>

首先我們準備一下數據庫的數據

?

?創建user_info表

DROP DATABASE IF EXISTS mybatis_test;CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
USE mybatis_test;DROP TABLE IF EXISTS user_info;CREATE TABLE `user_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1
',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )VALUES ( 'admin', 'admin', 18, 1, '18612340001' );INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

配置數據庫的連接(.yml文件中的配置)

?

創建數據庫表中對應的實體類

@Data
public class UserInfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;
}

?注:數據庫中字段全部都使用小寫,單詞之間用_來連接,Java中的屬性使用小駝峰的方式

?注:由于引入了lombok依賴,所以直接使用@Date注解,不需要再去寫Getter和Setter方法

?

創建在持久層的接口,在MyBatis中持久層中的接口一般使用 xxxMapper 的命名方法

我們創建了UserInfoMapper接口

注:@Mapper注解就是表明該接口是MyBatis中的Mapper接口

單元測試

在創建出來的SpringBoot?程中,在src下的test?錄下,已經?動幫我們創建好了測試類,我們可以 直接使?這個測試類來進?測試

單元測試是開發人員進行的測試,與測試人員無關。

?

進行測試的方法

生成測試類的方法

?找到你需要生成測試類的Mapper接口的方法上,右鍵generate,點擊Test

在你需要測試的方法上打 √ ,然后點ok就會自動生成了

MyBatis中的基礎操作(注解)

打印日志

只需要在配置文件中配置即可

就可以直接看到sql的執行過程,參數傳遞,執行結果。

參數的傳遞

查詢對象的方式(select)

id為固定值的時候
@Select("select * from user_info where id=3")List<UserInfo> selectAllById();
@Testvoid selectAllId() {System.out.println(userInfoMapper.selectAllById());}

?

id為動態的數值

使用#{}的方式獲取方法中的參數

    @Select("select * from user_info where id=#{id}")UserInfo selectAllById3(Integer id);
@Testvoid selectAllById3() {System.out.println(userInfoMapper.selectAllById3(1));}

?

?

傳遞多個參數?
 @Select("select * from user_info where username=#{username} and password=#{password}")UserInfo selectAllByUserNameAndPassWord(String username,String password);
 @Testvoid selectAllByUserNameAndPassWord() {System.out.println(userInfoMapper.selectAllByUserNameAndPassWord("zhangsan","zhangsan"));}

?可以使用@Param注解進行參數的綁定(重命名)
@Select("select * from user_info where username=#{aa} and password=#{bb}")UserInfo selectAllByUserNameAndPassWord2(@Param("aa") String username,@Param("bb") String password);
@Testvoid selectAllByUserNameAndPassWord2() {System.out.println(userInfoMapper.selectAllByUserNameAndPassWord2("zhangsan","zhangsan"));}

?

增 (Insert)

@Insert("insert into user_info (username,password,age) values (#{username},#{password},#{age})")Integer insert(UserInfo userInfo);
@Testvoid insert() {UserInfo userInfo=new UserInfo();userInfo.setUsername("lisi");userInfo.setPassword("lisi");userInfo.setAge(1);Integer result=userInfoMapper.insert(userInfo);System.out.println("新增行數"+result);}

?

返回主鍵

獲得自增Id

//獲取自增Id@Options(useGeneratedKeys = true,keyProperty = "id")@Insert("insert into user_info (username,password,age) values (#{username},#{password},#{age})")Integer insert1(UserInfo userInfo);
@Testvoid insert1() {UserInfo userInfo=new UserInfo();userInfo.setUsername("lisi");userInfo.setPassword("lisi");userInfo.setAge(1);Integer result=userInfoMapper.insert1(userInfo);System.out.println("新增行數"+result+"Id"+userInfo.getId());}

?

刪 (delete)

@Delete("delete from user_info where id =#{id}")Integer deleteUser(Integer id);
@Testvoid deleteUser() {userInfoMapper.deleteUser(13);}

?

?

改(update)

@Update("update user_info set delete_flag= #{deleteFlag},phone= #{phone} where id= #{id}")Integer updateUser(UserInfo userInfo);
@Testvoid updateUser() {UserInfo userInfo=new UserInfo();userInfo.setDeleteFlag(1);userInfo.setPhone("123");userInfo.setId(11);Integer result=userInfoMapper.updateUser(userInfo);System.out.println(result);}

?

查詢的需要注意的問題

當我們進行查詢的時候,我們會發現某些字段是沒有進行賦值的,只有Java對象屬性和數據庫字段?模?樣時,才會進?賦值

原因:MyBatis 在執行查詢后,會將數據庫中的字段值映射(映射 = 賦值)到 Java 實體類對象的屬性中。但是,如果 數據庫字段名和 Java 對象屬性名不一致,MyBatis 默認不會自動進行映射賦值

解決辦法:

1.起別名

@Select("select id, username,`password`, age, gender,  phone, " + 
"delete_flag as deleteFlag, create_time as createTime, update_time as updateTime" + 
" from user_info")

?2.結果映射

@Results(id = "BaseMap", value = {@Result(column = "delete_flag", property = "deleteFlag"),@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")})
@Select("select * from user_info")

3.添加駝峰命名的配置文件

設置為true

MyBatis XML配置文件?

MyBatis的開發方式一般有兩種,注解 & XML

現在我們來學習XML的開發方式

配置mybatis

Spring Boot 集成 MyBatis 時指定 Mapper XML 文件的位置,告訴 MyBatis 去哪里加載 SQL 映射文件?

添加Mapper接口

添加 .xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.blame.springmybatis.mapper.UserInfoMapperXML"><select id="selectUserAll" resultType="com.blame.springmybatis.model.UserInfo">select * from user_info</select></mapper>

?

單元測試

@SpringBootTest
class UserInfoMapperXMLTest {@Autowiredprivate UserInfoMapperXML userInfoMapperXML;@Testvoid selectUserAll() {userInfoMapperXML.selectUserAll().stream().forEach(x-> System.out.println(x));}
}

?

XML中的增刪改查?

增(insert)

 Integer insertUser(UserInfo userInfo);
 <insert id="insertUser">insert into user_info (username, `password`, age) VALUES (#{username}, #{password}, #{age})</insert>
@Testvoid insertUser() {UserInfo userInfo=new UserInfo();userInfo.setUsername("xiaohei");userInfo.setPassword("123");userInfo.setAge(11);Integer result=userInfoMapperXML.insertUser(userInfo);System.out.println("增加行數"+result+"Id"+userInfo.getId());}

?

使用@Param注解進行重命名

Integer insertUser2(@Param("UserInfo") UserInfo userInfo);
<insert id="insertUser2">insert into user_Info (username, `password`, age) VALUES (#{username}, #{password}, #{age})</insert>
@Testvoid insertUser2() {UserInfo userInfo=new UserInfo();userInfo.setUsername("hei");userInfo.setPassword("15");userInfo.setAge(13);Integer result=userInfoMapperXML.insertUser(userInfo);System.out.println("增加行數"+result+"Id"+userInfo.getId());}

自增Id

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">insert into user_info (username, `password`, age, gender, phone) values(#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})</insert>

?刪(delete)

Integer deleteUser(Integer id);
<delete id="deleteUser">delete from user_info where id =#{id}</delete>
   @Testvoid deleteUser() {userInfoMapperXML.deleteUser(16);}

?

?

改(update)

<update id="updateUser">update user_info set  password= #{password} , age=#{age} where id= #{id}</update>
Integer updateUser(String password,Integer age,Integer id);
@Testvoid updateUser() {UserInfo userInfo=new UserInfo();Integer result=userInfoMapperXML.updateUser("123",88,9);}

查(select)?

List<UserInfo> selectUserAll();
<select id="selectUserAll" resultType="com.blame.springmybatis.model.UserInfo">select * from user_info</select>

其他查詢

聯合查詢(多表查詢)

在數據庫中導入表

DROP TABLE IF EXISTS articleinfo;
CREATE TABLE articleinfo (id INT PRIMARY KEY auto_increment,title VARCHAR ( 100 ) NOT NULL,content TEXT NOT NULL,uid INT NOT NULL,delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT,create_time DATETIME DEFAULT now(),update_time DATETIME DEFAULT now() 
) DEFAULT charset 'utf8mb4';INSERT INTO articleinfo ( title, content, uid ) VALUES ( 'Java', 'Java正?', 1 );

配置對應的實體類

@Data
public class ArticleInfo {private Integer id;private String title;private String content;private Integer uid;private Integer deleteFlag;private Date createTime;private Date updateTime;private String username;private Integer age;
}

?定義接口

@Mapper
public interface ArticleInfoMapper {ArticleInfo selectArticleById(Integer id);
}

配置 .xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.blame.springmybatis.mapper.ArticleInfoMapper"><select id="selectArticleById" resultType="com.blame.springmybatis.model.ArticleInfo">select ta.*,tb.username,tb.age from article_info ta left 
join user_info tb on ta.uid = tb.id where ta.id=#{id}</select>
</mapper>

測試類

@SpringBootTest
class ArticleInfoMapperTest {@Autowiredprivate ArticleInfoMapper articleInfoMapper;@Testvoid selectArticleById() {articleInfoMapper.selectArticleById(1);}
}

?

#{ }和${ }

這是mybatis中非常重要的兩個占位符

首先我們看一下#{}

@Select("select username,`password`,age,gender,phone from user_info where id=#{id}")UserInfo queryById(Integer id);
@Testvoid queryById() {System.out.println(userInfoMapper.queryById(2));}

通過觀察我們的sql語句,我們發現我們輸入的參數 2 ,并沒有在語句后面顯示,而是用 ? ,我們把這種叫做預編譯SQL

我們再觀察一下${}

    @Select("select username,`password`,age,gender,phone from user_info where id=${id}")UserInfo queryById2(Integer id);
@Testvoid queryById2() {System.out.println(userInfoMapper.queryById2(2));}

?我們可以發現參數直接是拼在SQL語句中了

我們再把參數換成String類型

@Select("select username,`password`,age,gender,phone from user_info where username=#{username}")UserInfo queryById3(String username);@Select("select username,`password`,age,gender,phone from user_info where username=${username}")UserInfo queryById4(String username);
@Testvoid queryById3() {System.out.println(userInfoMapper.queryById3("zhangsan"));}@Testvoid queryById4() {System.out.println(userInfoMapper.queryById4("zhangsan"));}

我們先來看一下#{}的測試結果

?再看一下${}的測試結果

我們發現報錯了,說SQL語法異常

這次的參數是直接拼在SQL語句的后面,沒有自動給username添加? ' '

修改后,測試成功

 @Select("select username,`password`,age,gender,phone from user_info where username='${username}'")UserInfo queryById4(String username);

?

通過上面這個例子,我們可以發現#{}使用的是預編譯SQL,通過?進行占位,提前對SQL進行編譯,將參數填充到SQL語句中,#{}會根據參數類型,自動拼接引號 ' '

${}會直接進行字符的替換,一起對SQL進行編譯,如果是字符串,得加上引號' '

SQL語句的執行流程

1.語法解析????????2.SQL優化????????3.SQL編譯????????4.SQL執行?

?#{} 和${}區別

#{}的性能更高

${}會出現SQL注入的風險

SQL注入:是通過操作輸?的數據來修改事先定義好的SQL語句,以達到執?代碼對服務器進?攻擊的 ?法

SQL注入的代碼演示

@Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")List<UserInfo> queryByName(String name);
@Testvoid queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("admin");System.out.println(userInfos);}

?測試成功

但是,如果我們參數傳為以下的情況,我們發現不僅沒有報錯,而且得到了我們表中的全部信息,這就是SQL注入

@Testvoid queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");System.out.println(userInfos);}

?

排序功能?

當我們表中數據按Id逆序進行排序,發現${}排序成功

 @Select("select id, username, age, gender, phone, delete_flag, create_time,update_time " +"from user_info order by id ${sort} ")List<UserInfo> queryAllUserBySort(String sort);
@Testvoid queryAllUserBySort() {System.out.println(userInfoMapper.queryAllUserBySort("desc"));}

但我們發現#{}?沒有成功,進行了報錯。

@Select("select id, username, age, gender, phone, delete_flag, create_time,update_time " +"from user_info order by id #{sort} ")List<UserInfo> queryAllUserBySort2(String sort);

SQL語法錯誤異常?

?

可以發現,當使? #{sort} 查詢時,asc前后?動給加了引號,導致sql錯誤

like的使用

 @Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from user_info where username like '%#{key}%' ")List<UserInfo> queryAllUserByLike(String key);

這里 '%#{key}%' 是錯誤寫法,因為 #{} 在 MyBatis 中是 參數占位符,它不會在 SQL 字符串內部被當作字符串拼接,而是被替換成 ??

我們可以使用從concat()

@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from user_info where username like concat('%',#{key},'%')")List<UserInfo> queryAllUserByLike(String key);

?

數據庫連接池

預先創建好的一批數據庫連接,當應用程序要訪問數據庫時,從池中獲取連接,操作完成后再歸還,而不是每次都重新建立和關閉連接

常用的的數據連接池

HikariCP(Spring Boot默認):性能最快、輕量

Druid(阿里巴巴):功能豐富、監控強大

C3P0 :比較落后

DBCP :使用少

切換數據庫連接池?

如果我們想把默認的數據庫連接池切換為Druid數據庫連接池,只需要引?相關依賴即可?

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version></dependency>

希望能對大家有所幫助!!!!!?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72928.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72928.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72928.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Go語言的基礎類型

一基礎數據類型 一、布爾型&#xff08;Bool&#xff09; 定義&#xff1a;表示邏輯真 / 假&#xff0c;僅有兩個值&#xff1a;true 和 false內存占用&#xff1a;1 字節使用場景&#xff1a;條件判斷、邏輯運算 二、數值型&#xff08;Numeric&#xff09; 1. 整數類型&…

【愚公系列】《高效使用DeepSeek》019-外語學習

??【技術大咖愚公搬代碼:全棧專家的成長之路,你關注的寶藏博主在這里!】?? ??開發者圈持續輸出高質量干貨的"愚公精神"踐行者——全網百萬開發者都在追更的頂級技術博主! ?? 江湖人稱"愚公搬代碼",用七年如一日的精神深耕技術領域,以"…

發布第四代液晶電視,TCL引領全新美學境界

在不斷革新的消費電子領域中&#xff0c;電視行業在視覺體驗上正面臨重要的美學挑戰。如何打破全面屏時代的物理束縛&#xff0c;將家居空間提升到“視覺無界”的層次&#xff0c;以及如何讓尖端技術更好地服務于影像沉浸感&#xff0c;成為行業關注的焦點。 3月10日&#xff…

劍指 Offer II 113. 課程順序

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20113.%20%E8%AF%BE%E7%A8%8B%E9%A1%BA%E5%BA%8F/README.md 劍指 Offer II 113. 課程順序 題目描述 現在總共有 numCourses 門課需要選&#xff0c;記為 0 到 n…

【C++】STL庫面試常問點

STL庫 什么是STL庫 C標準模板庫&#xff08;Standard Template Libiary&#xff09;基于泛型編程&#xff08;模板&#xff09;&#xff0c;實現常見的數據結構和算法&#xff0c;提升代碼的復用性和效率。 STL庫有哪些組件 STL庫由以下組件構成&#xff1a; ● 容器&#xf…

【問題解決】Postman 測試報錯 406

現象 Tomcat 日志 org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException Resolved org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation HTTP狀態 406 - 不可接收 的報錯&#xff0c;核心原因 客…

第3節:AWK的特點和優勢

1 第3節&#xff1a;AWK的特點和優勢 AWK是一種功能強大的文本處理工具&#xff0c;具有以下特點和優勢&#xff1a; 1.1.1 簡潔性 AWK的語法簡潔明了&#xff0c;對于簡單的數據處理任務&#xff0c;通常只需編寫簡短的命令即可完成。例如&#xff0c;要從一個文本文件中提…

Flutter 打包 ipa出現錯誤問題 exportArchive

一、錯誤信息: Encountered error while creating the IPA: error: exportArchive: "Runner.app" requires a provisioning profile with the Push Notifications feature. Try distributing the app in Xcode: open /project/your_app/build/ios/archive/Runner.…

STC89C52單片機學習——第28節: [12-2] AT24C02數據存儲秒表(定時器掃描按鍵數碼管)

寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難&#xff0c;但我還是想去做&#xff01; 本文寫于&#xff1a;2025.03.20 51單片機學習——第28節: [12-2] AT24C02數據存儲&秒表&#xff08;定時器掃…

Verilog-HDL/SystemVerilog/Bluespec SystemVerilog vscode 配置

下載 verible https://github.com/chipsalliance/verible的二進制包 然后配置 vscode

STM32使用HAL庫,模擬UART輸出字符串

測試芯片是STM32F103C8T6&#xff0c;直接封裝好了&#xff0c;波特率是 9600 MyDbg.h #ifndef __MYDBG_H #define __MYDBG_H #include "stm32f1xx_hal.h" #include <stdio.h> #include <stdarg.h>/*使用GPIO口 模擬 UART 輸出字符串 */ //初始化調試…

[工控機安全] 使用DriverView快速排查不可信第三方驅動(附詳細圖文教程)

導語&#xff1a; 在工業控制領域&#xff0c;設備驅動程序的安全性至關重要。第三方驅動可能存在兼容性問題、安全漏洞甚至惡意代碼&#xff0c;威脅設備穩定運行。本文將手把手教你使用 DriverView工具&#xff0c;高效完成工控機驅動安全檢查&#xff0c;精準識別可疑驅動&a…

HTML5響應式使用css媒體查詢

HTML 負責搭建頁面結構&#xff0c;CSS 負責樣式設計&#xff0c;并且通過媒體查詢實現了較好的響應式效果&#xff0c;能夠適應不同屏幕尺寸下面就是寫了一個詳細的實例。 CSS 部分 * {margin: 0;padding: 0;box-sizing: border-box; } * 是通配選擇器&#xff0c;會選中頁面…

洛谷P1434 [SHOI2002] 滑雪

P1434 [SHOI2002] 滑雪 - 洛谷 代碼區&#xff1a; #include<algorithm> #include<iostream> #include<cstring> using namespace std;const int MAX 105; int r, c; int arr[MAX][MAX], dp[MAX][MAX]; int xindex[4] {-1,1,0,0};//上下左右 int yindex[…

【操作系統】進程間通信方式

進程間通信方式 前言 / 概述一、管道管道命名管道 二、消息隊列三、共享內存四、信號量信號量概述互斥訪問條件同步信號 五、socket總結 前言 / 概述 每個進程的用戶地址空間都是獨立的&#xff0c;?般而言是不能互相訪問的&#xff0c;但內核空間是每個進程都共享的&#xff…

WPF 布局中的共性尺寸組(Shared Size Group)

1. 什么是共性尺寸組&#xff1f; 在 WPF 的 Grid 布局中&#xff0c;SharedSizeGroup 允許多個 Grid 共享同一列或行的尺寸&#xff0c;即使它們屬于不同的 Grid 也能保持大小一致。這樣可以保證界面元素的對齊性&#xff0c;提高布局的一致性。 SharedSizeGroup 主要用于需…

Netty源碼—2.Reactor線程模型二

大綱 1.關于NioEventLoop的問題整理 2.理解Reactor線程模型主要分三部分 3.NioEventLoop的創建 4.NioEventLoop的啟動 4.NioEventLoop的啟動 (1)啟動NioEventLoop的兩大入口 (2)判斷當前線程是否是NioEventLoop線程 (3)創建一個線程并啟動 (4)NioEventLoop的啟動總結 (…

前端項目中應該如何選擇正確的圖片格式

在前端項目中選擇正確的圖片格式是優化頁面性能、提升用戶體驗的關鍵步驟之一。以下是常見圖片格式的特點、適用場景及選擇建議&#xff0c;幫助你在不同場景下做出最優決策&#xff1a; 一、常見圖片格式對比 格式特點適用場景不適用場景JPEG- 有損壓縮&#xff0c;文件小- 不…

保姆級 STM32 HAL 庫外部中斷教學

1. 外部中斷概述 為什么用外部中斷&#xff1f; 當按鍵按下時&#xff0c;CPU 無需輪詢檢測引腳狀態&#xff0c;而是通過中斷機制立即響應&#xff0c;提高效率&#xff0c;適用于實時性要求高的場景。 關鍵概念 EXTI (External Interrupt/Event Controller)&#xff1a;ST…

Postman高級功能深度解析:Mock Server與自動化監控——構建高效API測試與監控體系

引言&#xff1a;Postman在API開發中的核心價值 在數字化時代&#xff0c;API&#xff08;應用程序編程接口&#xff09;已成為系統間交互的“神經網絡”&#xff0c;其質量直接影響用戶體驗與業務連續性。然而&#xff0c;傳統API測試面臨兩大挑戰&#xff1a; 開發階段依賴…