深入解析MyBatis中#{}和${}的區別與應用場景

在MyBatis框架的使用過程中,SQL映射文件的編寫是核心工作之一。而#{}和${}這兩種參數占位符語法,雖然看起來相似,卻有著本質的區別。正確理解和使用它們,不僅關系到應用程序的安全性,還會影響系統性能。本文將全面剖析這兩種語法的區別、實現原理、使用場景以及最佳實踐。

一、基本概念與語法

1.1 #{}語法

#{}是MyBatis中的預編譯占位符,也稱為參數標記。它的基本形式如下:

<select id="findUserById" resultType="User">SELECT * FROM users WHERE id = #{userId}
</select>

1.2 ${}語法

${}是MyBatis中的字符串替換占位符,也稱為非轉義字符串替換。它的基本形式如下:

<select id="findUsersByTable" resultType="User">SELECT * FROM ${tableName} WHERE status = 1
</select>

二、底層實現原理

2.1 #{}的工作原理

當MyBatis遇到#{}時,會進行以下處理:

  1. 解析階段:MyBatis解析SQL映射文件時,識別出#{}標記

  2. 參數處理:運行時將參數值通過PreparedStatement的set方法設置

  3. SQL生成:最終生成帶有"?"的預編譯SQL語句

例如上面的例子會生成:

SELECT * FROM users WHERE id = ?

然后通過PreparedStatement的setInt/setString等方法設置參數值。

2.2 ${}的工作原理

對于${}的處理則完全不同:

  1. 直接替換:MyBatis在SQL解析階段就直接將${}替換為實際的參數值

  2. 字符串拼接:替換后的SQL語句是通過字符串拼接而成的

  3. 直接執行:最終生成的是完整的SQL語句,而非預編譯語句

例如,如果tableName="users",生成的SQL就是:

SELECT * FROM users WHERE status = 1

三、核心區別對比

3.1 安全性對比

特性#{}${}
SQL注入防護安全,能防止SQL注入不安全,存在SQL注入風險
實現方式參數化查詢字符串拼接

#{}示例:

String sql = "SELECT * FROM users WHERE name = #{name}";
// 即使用戶輸入 name = "' OR '1'='1"
// 實際執行:SELECT * FROM users WHERE name = ? 
// 參數值會被正確處理,不會導致注入

${}示例:

String sql = "SELECT * FROM users WHERE name = '${name}'";
// 如果用戶輸入 name = "' OR '1'='1"
// 實際執行:SELECT * FROM users WHERE name = '' OR '1'='1'
// 這將返回所有用戶數據,造成SQL注入

3.2 性能對比

特性#{}${}
數據庫優化支持預編譯,可緩存執行計劃每次都是新SQL,無法緩存
網絡傳輸只需傳輸參數需要傳輸完整SQL
編譯次數一次編譯多次執行每次都需要重新編譯

3.3 使用場景對比

場景#{}${}說明
普通參數值??推薦使用#{}
表名??動態表名必須使用${}
列名??動態列名必須使用${}
ORDER BY子句??動態排序需謹慎使用
GROUP BY子句??動態分組需謹慎使用
LIKE模糊查詢??需特殊處理通配符

四、深入應用場景

4.1 必須使用#{}的場景

  1. 所有用戶輸入的參數值

    WHERE username = #{username} AND password = #{password}
  2. 數值型參數

    WHERE age > #{minAge} AND age < #{maxAge}
  3. 日期型參數

    WHERE create_time > #{startDate}

4.2 可能需要使用${}的場景

  1. 動態表名?

    SELECT * FROM ${tableName}

    適用于分表場景,如表名按年份分表:user_2022, user_2023等

  2. 動態列名?

    SELECT ${columns} FROM users

    適用于動態選擇返回字段的場景

  3. ORDER BY排序?

    ORDER BY ${sortColumn} ${sortOrder}

    但更安全的做法是:

    <choose><when test="sortColumn == 'name'">ORDER BY name</when><when test="sortColumn == 'age'">ORDER BY age</when><otherwise>ORDER BY id</otherwise>
    </choose>

4.3 特殊場景處理

LIKE模糊查詢的正確寫法:

錯誤方式:

WHERE name LIKE '%${name}%'

正確方式:

// Java代碼中處理參數
String nameParam = "%" + name + "%";
WHERE name LIKE #{nameParam}

或使用SQL函數:

WHERE name LIKE CONCAT('%', #{name}, '%')

五、最佳實踐建議

5.1 安全性實踐

  1. 默認使用#{}:除非確有必要,否則總是使用#{}

  2. 嚴格過濾${}參數:使用${}時,必須對參數值進行白名單驗證

    // 驗證表名是否合法
    if (!isValidTableName(tableName)) {throw new IllegalArgumentException("Invalid table name");
    }
  3. 避免用戶輸入直接用于${}:特別是排序、分組等場景

5.2 性能優化實踐

  1. 優先使用#{}:利用預編譯語句的緩存優勢

  2. 減少${}使用頻率:對于頻繁調用的SQL,避免使用${}導致無法緩存執行計劃

  3. 批量處理動態SQL:對于必須使用${}的場景,考慮批量處理減少SQL解析次數

5.3 代碼可維護性實踐

  1. 明確注釋:在使用${}的地方添加注釋說明原因

    <!-- 必須使用${}因為表名是動態的 -->
    SELECT * FROM ${tableName}
  2. 集中管理:將動態部分集中管理,便于維護和安全檢查

  3. 單元測試:為使用${}的SQL編寫額外的安全測試用例

六、常見問題解答

Q1:為什么ORDER BY不能使用#{}?

A:因為#{}會給參數值添加引號,例如:

ORDER BY 'name' 'DESC'  -- 錯誤的SQL語法

而正確的應該是:

ORDER BY name DESC

Q2:什么情況下必須使用${}?

A:當SQL語句的非參數部分需要動態變化時,如:

  • 動態表名

  • 動態列名

  • SQL關鍵字(如ASC/DESC)

Q3:如何安全地使用${}?

A:可以采取以下措施:

  1. 使用白名單驗證參數值

  2. 避免直接使用用戶輸入

  3. 對參數值進行轉義處理

  4. 最小化使用范圍

總結

#{}和${}在MyBatis中扮演著不同的角色:

  • #{}?是安全的參數占位符,適用于幾乎所有參數值的場景,能防止SQL注入,性能更好。

  • ${}?是字符串替換,適用于SQL語句本身需要動態變化的場景,但存在安全風險,應當謹慎使用。

在實際開發中,我們應該遵循以下原則:

  1. 默認使用#{}

  2. 謹慎評估${}的使用必要性

  3. 對必須使用${}的場景實施嚴格的安全控制

  4. 編寫清晰的文檔和注釋說明使用原因

正確理解和使用這兩種占位符,將使你的MyBatis應用更加安全、高效和可維護。

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

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

相關文章

ELKB日志分析平臺 部署

ElasticSearch ELKB 日志分析 介紹 docker-compose一鍵部署ELK(elasticsearchlogstashkibana) 以下是使用 Docker Compose 部署 Elasticsearch、Logstash、Kibana 和 Beats&#xff08;以 Filebeat 為例&#xff09; 的完整方案&#xff0c;涵蓋配置文件、關鍵參數說明及部署步…

File IO 字節流 | Java 學習日志 | 第 12 天

File 1.概述 File表示路徑&#xff0c;可以表示文件和文件夾&#xff0c;可以存在也可以不存在 相對路徑&#xff08;相對當前項目&#xff09;&#xff0c;絕對路徑。 構造方法File(file/string)&#xff0c;File(file/string,string)。 public static void main(String[] ar…

基于SpringBoot的服裝公司進銷存管理系統設計與開發(代碼+數據庫+LW)

摘要 隨著服裝行業競爭的加劇&#xff0c;傳統手工或簡單電子表格管理進銷存的方式已難以滿足現代企業的需求&#xff0c;效率低下且易出錯。基于SpringBoot框架的服裝公司進銷存管理系統應運而生&#xff0c;旨在通過信息化手段提升運營效率和服務質量。系統特別設計了銷售員…

openFeign用的什么協議,dubbo用的什么協議

簡單直接的答案是&#xff1a;?OpenFeign?&#xff1a;默認使用 ?HTTP? 協議&#xff08;通常是 HTTP/1.1&#xff0c;也支持 HTTP/2&#xff09;&#xff0c;通信格式為 ?RESTful JSON。?Dubbo?&#xff1a;默認使用 ?Dubbo 協議?&#xff08;一種自定義的、基于 TCP…

Android SystemServer 系列專題【篇四:SystemServerInitThreadPool線程池管理】

本篇重點介紹一下SystemServerInitThreadPool&#xff0c;顧名思義此類針對SystemServer進程的提供了一套ThreadPool線程池的統一標準方案&#xff0c;下面從源碼和日志的角度來剖析一個這個類。1、SystemServerInitThreadPool單例設計SystemServerInitThreadPool的源碼路徑在f…

2014-2024高教社杯全國大學生數學建模競賽賽題匯總預覽分析

一、分析賽題核心意義收集近 11 年的賽題并非簡單的 “題目存檔”&#xff0c;而是為了從歷史規律、能力匹配、實戰準備三個維度為參賽者或研究者提供價值。1.1把握競賽命題趨勢&#xff0c;降低選題盲目性賽題命題往往緊扣當年社會熱點、科技前沿與行業痛點&#xff08;如 202…

一個頭像圖片滾動輪播組件(React實現)

遇到一個效果&#xff0c;組件庫里沒有現成能用的組件&#xff0c;于是手搓了一個&#xff0c;傳入圖片url列表&#xff0c;和其他配置項即可直接使用。 輪播效果實現思路 假設共有10張圖片輪流滾動&#xff0c;輪播圖展示3張圖片。給正在輪播的圖片綁定visible類&#xff0c;輪…

從入門到理解:支持向量機的核心原理與實戰思路

一、SVM 的核心目標&#xff1a;找 “最好” 的超平面。1.1 什么是 “超平面”&#xff1f;超平面是一個幾何概念&#xff0c;簡單來說&#xff1a;在 2 維空間&#xff08;平面&#xff09;中&#xff0c;超平面是一條直線&#xff08;1 維&#xff09;&#xff1b;在 3 維空間…

Python 辦公自動化實戰:Excel 批量處理 + 自動發郵件

Python 辦公自動化實戰&#xff1a;Excel 批量處理 自動發郵件關鍵詞&#xff1a; Python辦公自動化 ? Pandas ? OpenPyXL ? Email ? 定時任務 摘要&#xff1a; 每月底還在手動處理幾十份Excel報表并郵件發送&#xff1f;快來學習如何用Python全自動完成&#xff01;本文…

高教杯數學建模2021-C 生產企業原材料的訂購與運輸

某建筑和裝飾板材的生產企業所用原材料主要是木質纖維和其他植物素纖維材料, 總體可分為 A&#xff0c;B&#xff0c;C 三種類型。該企業每年按 48 周安排生產&#xff0c;需要提前制定 24 周的原 材料訂購和轉運計劃&#xff0c;即根據產能要求確定需要訂購的原材料供應商&…

【Python系列】Flask 和 FastAPI對比

博客目錄1. 類型和設計目標2. 性能3. 異步支持4. 數據驗證和文檔5. 學習曲線和生態6. 使用場景示例對比Flask&#xff08;同步&#xff09;FastAPI&#xff08;異步&#xff09;總結Flask 和 FastAPI 都是 Python 中流行的 Web 框架&#xff0c;但它們的設計目標、特性和適用場…

把 AI 塞進「空調遙控器」——基于 MEMS 溫濕陣列的 1 分鐘極速房間熱場掃描

標簽&#xff1a;MEMS 陣列、熱場掃描、極速空調、TinyML、RISC-V、零樣本、離線推理、節能 ---- 背景&#xff1a;為什么空調遙控器要「畫圖」&#xff1f; 傳統空調只有一個溫濕度探頭&#xff0c;經常“東邊冷、西邊熱”。 ? 大客廳 30 ㎡&#xff0c;溫差 5 ℃&#xff1…

【機器學習】8 Logistic regression

本章目錄 8 Logistic regression 245 8.1 Introduction 245 8.2 Model specification 245 8.3 Model fitting 245 8.3.1 MLE 246 8.3.2 Steepest descent 247 8.3.3 Newton’s method 249 8.3.4 Iteratively reweighted least squares (IRLS) 250 8.3.5 Quasi-Newton (variabl…

C++中如何使用互斥(1)------std::lock_guard

操作系統&#xff1a;ubuntu22.04 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 std::lock_guard 是 C11 引入的一個 RAII&#xff08;Resource Acquisition Is Initialization&#xff09;風格的鎖管理類&#xff0c;用于自動管理互斥鎖&#xff08;mutex&#x…

AI算力提升7.5倍!英偉達發布新一代機器人超級計算機Jetson Thor,驅動物理AI革命

今天&#xff0c;NVIDIA 宣布其專為物理 AI 和機器人打造的新一代機器人計算機 Jetson Thor 正式發售。其中&#xff0c;Jetson AGX Thor 開發者套件售價為 3499 美元&#xff08;約合人民幣 2.5 萬元&#xff09;。NVIDIA 創始人兼首席執行官黃仁勛表示&#xff1a;“Jetson T…

【數學建模】如何總結數學建模中的層次分析法最好

模型簡介模型名稱&#xff1a;層次分析法核心問題類型&#xff1a;評價類核心思想和適用場景 核心思想&#xff1a;將大決策問題拆解成小比較問題&#xff0c;通過數學計算綜合最終結論&#xff1a;本質是人的主觀判斷轉換為客觀數據的工具[[適用場景]] 個人決策企業 / 項目決策…

`mmap` 系統調用詳解

mmap 是 Unix/Linux 系統中一個強大且多用途的系統調用&#xff0c;用于將文件或設備映射到進程的地址空間&#xff0c;實現內存映射I/O。 1. 函數的概念與用途 mmap&#xff08;內存映射&#xff09;函數允許程序將文件或其他對象直接映射到其地址空間&#xff0c;這樣文件內容…

深度剖析Spring AI源碼(二):Model抽象層 - “馴服”天下AI的“緊箍咒”

深度剖析Spring AI源碼&#xff08;二&#xff09;&#xff1a;Model抽象層 - “馴服”天下AI的“緊箍咒”上一章我們鳥瞰了Spring AI的宏偉藍圖&#xff0c;今天&#xff0c;我們要深入這座大廈的基石——Model抽象層。如果說Spring AI是連接Java與AI世界的橋梁&#xff0c;那…

永磁同步電機無速度算法--高頻脈振正弦波注入到兩相靜止坐標系

一、原理介紹采用一種改進的永磁同步電機低速無位置傳感器控制策略。與傳統的旋轉高頻信號注入法和脈振高頻信號注入法不同&#xff0c;該策略選擇向靜止坐標軸系注入高頻脈振信號&#xff0c;轉子位置估計信息可以通過載波電流響應提取。并使用一種類似于簡化型擴展卡爾曼濾波…

嵌入式學習日志————ADC模數轉換器之實驗

1.配置ADC的步驟①開啟RCC時鐘&#xff0c;包括ADC和GPIO的時鐘②配置GPIO&#xff0c;把相應端口配置成模擬輸入模式③配置多路開關&#xff0c;把左邊的通道接入右邊的規則組列表里④配置ADC轉換器⑤調用ADC_Cmd函數&#xff0c;開啟ADC2.庫函數配置ADCCLK分頻器void RCC_ADC…