MyBatis_3

上一篇文章,我們學習了使用XML實現MyBatis進行增、刪、查、改等操作,本篇文章,我們將學習#{ }和${ }獲取方法參數的區別和使用MyBatisXML實現動態SQL語句。

#{ }和${ }的區別

在之前的文章中我們都是使用#{ }進行賦值,但實際上Mybatis也支持${ } 對參數進行賦值。

Integer類型的參數

我們先來看Integer類型的參數的賦值:

mapper:

  //Integer@Select("select * from userinfo where id = #{id} ")List<Userinfo> getById(Integer id);@Select("select * from userinfo where id = ${id} ")List<Userinfo> getById2(Integer id);

測試:

    @Testvoid getById() {System.out.println(mapper.getById(1));}@Testvoid getById2() {System.out.println(mapper.getById(1));}

#{ }:?

${ }:?

可以看到查詢到的結果是一樣的,都能夠正常查到。此時,我們再換一個String類型試試。

String類型的參數

mapper:

   //String@Select("select * from userinfo where username = #{username} ")List<Userinfo> getByName(String username);@Select("select * from userinfo where username = ${username} ")List<Userinfo> getByName2(String username);

測試:

   @Testvoid getByName() {System.out.println(mapper.getByName("xmy"));}@Testvoid getByName2() {System.out.println(mapper.getByName2("xmy"));}

#{ }:?

測試通過。?

?${ }:

可以看到報出BadSql異常。

此時觀察日志,我們傳過去的sql語句是這樣的:通過觀察,我們發現xmy處少了引號,正確的sql語句是‘xmy’。我們手動給sql語句加上引號,修改代碼如下:?

    @Select("select * from userinfo where username = '${username}' ")List<Userinfo> getByName2(String username);

測試通過。

通過對比#{ }和${ }打出來的日志我們發現#{ }是預編譯sql(通過?占位的方式,提前對sql進行編譯然后把參數填充到SQL語句中),#{ }會根據參數類型自動拼接引號;而${ }則是直接進行字符替換,一起對SQL進行編譯(即時sql)。如果參數為字符串,需要加上引號。?

在開發環境下,我們能使用#{ }就不要使用${ }。不僅僅因為#{ }的效率比${ }更高,而是因為${ }可能會產生sql注入的問題。

那么什么是sql注入呢?下面我們通過代碼來演示sql注入。

${ }引發sql注入問題

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

我們先嘗試在數據庫中使用下面的sql語句進行查詢:

SELECT * FROM `userinfo` where username = ' OR 1 = '1;

?可以看到此時我們的代碼是有問題的:

下面我們使用#{ }和${ }?分別進行查詢:

  //String@Select("select * from userinfo where username = #{username} ")List<Userinfo> getByName(String username);@Select("select * from userinfo where username = '${username}' ")List<Userinfo> getByName2(String username);

?測試:

    @Testvoid getByName() {System.out.println(mapper.getByName("' OR 1 = '1"));}@Testvoid getByName2() {System.out.println(mapper.getByName2("' OR 1 = '1"));}

#{ }:?

${ }:?可以看到,${ }依然正常查詢出來了,其中參數or被當作了SQL語句的一部分:

${ }的作用

從上面的例子中,我們可以知道:${}會有sql注入的風險,所以我們盡量使用#{}完成查詢。

既然如此,${ }是不是就沒有存在的必要了呢?

當然不是。接下來我們通過代碼來看看${ }的作用。

1、使用${}實現排序功能

mapper:?

 @Select("select * from userinfo order by  id ${order}")List<Userinfo> getByOrder(String order);

測試:?

    @Testvoid getByOrder() {System.out.println(mapper.getByOrder("desc"));}

可以看到,能夠根據id逆序輸出結果。此時我們將${ }改成#{ }?。

    @Select("select * from userinfo order by id #{order}")List<Userinfo> getByOrder(String order);

測試結果:?

可以發現,當使用#{sort}查詢時,desc前后加上了引號,導致sql錯誤。

2、使用${ }實現模糊查詢

?mapper:

    @Select("select * from userinfo where username like '%${username}%'")List<Userinfo> getByLike(String username);

測試:?

 @Testvoid getByLike() {System.out.println(mapper.getByLike("zhangsan"));}

此時我們將${ } 改為#{ }?:

 @Select("select * from userinfo where username like '%#{username}%'")List<Userinfo> getByLike(String username);

可以看到,依然是因為引號的關系,出現了異常。?

但是在模糊查詢中使用#{ }也有解決辦法 :我們可以使用concat()來拼接字符串:

  @Select("select * from userinfo where username like concat('%',#{username},'%')")List<Userinfo> getByLike1(String username);

測試:?

  @Testvoid getByLike1() {System.out.println(mapper.getByLike1("zhangsan"));}

總結: #{ }和${ }的區別

1、#{ }和${ }的區別就是預編譯sql(占位)和即時sql(直接拼接)的區別。

2、#{ }使用預編譯的形式所以性能會比${ }更高

絕大多數情況下,某一條sql語句可能會被反復調用執行,或者每次執行的時候只有個別的值不同(比如select的where子句值不同,insert的values值不同)。如果每次都需要上面語法解析,sql優化,sql編譯等過程,效率就明顯不行了。

預編譯sql,編譯一次之后會將編譯后的sql語句緩存起來,后面再次執行這條語句時,不會再次編譯(只是輸入的參數不同),省去了解析優化等過程,以此來提高效率。

3、#{ }更安全(防止sql注入)

在使用${ }的場景下一定一定要考慮到sql注入問題,并采取措施進行防止:例如:1、在接口層(Controller層)進行判定,如果輸入的結果不是我們想要的直接返回。2、直接給接口寫死,根據用戶輸入的內容來決定調用哪個接口,如果沒有接口符合,則返回。

4、在一些場景下仍然需要用到${ }

比如:排序,參數不需要引號……?

數據庫連接池

在MyBtis中,我們使用了數據庫連接池技術,避免頻繁地創建銷毀連接。

數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用一個現有地數據庫連接,而不是重新建立一個。?

沒有使用數據庫連接池的情況:每次執行sql語句,要先創建一個新的連接對象,然后執行sql語句,sql語句執行完,再關閉連接對象釋放資源。這種重復的創建連接,銷毀連接比較消耗資源。

使用數據庫連接池的情況:程序啟動時,會在數據庫連接池中創建一定數量的Connection對象,當客戶請求數據庫連接池,會從數據庫連接池中獲取Connection對象,然后執行sql,sql語句執行完,再把Connection歸還給連接池。

優點:

1、減少了網絡開銷

2、資源重用

3、提升了系統性能?

動態SQL

動態sql是mybaatis的強大特性之一,能夠完成不同條件下不同的sql拼接。

官方文檔:動態 SQL_MyBatis中文網

?<if>標簽

在注冊用戶的時候,可能會有這樣一個問題,如下圖所示:

注冊分為兩種字段:必填字段和非必填字段,那如果在添加用戶的時候有不確定的字段傳入,程序應該如何實現呢?

這時候就需要使用動態標簽進行判斷了,如添加的時候性別為非必填字段,具體實現如下(XML實現):?

Mapper:

  Integer insertBatch(Userinfo userinfo);

XML:

    <insert id="insertBatch">insert into userinfo (username,password,age<if test="gender!=null">,gender</if>)values (#{username},#{password},#{age}<if test="gender!=null">,#{gender}</if>)</insert>

測試(有性別):?

    @Testvoid insertBatch() {Userinfo userinfo = new Userinfo();userinfo.setUsername("lisi");userinfo.setAge(16);userinfo.setPassword("lisi666");userinfo.setGender(2);mapper.insertBatch(userinfo);}

測試(無性別):?

    @Testvoid insertBatch() {Userinfo userinfo = new Userinfo();userinfo.setUsername("lisi");userinfo.setAge(16);userinfo.setPassword("lisi666");mapper.insertBatch(userinfo);}
?if標簽詳解:

<trim>標簽

mapper:

Integer insertBatch2(Userinfo userinfo);

假如我們有許多的元素需要選填,那么我們此時的XML語句會變成這樣:

    <insert id="insertBatch2">insert into userinfo(<if test="username != null">username</if><if test="password != null">,password</if><if test="age != null">,age</if><if test="gender != null">,gender  </if>)values (<if test="username != null">#{username}</if><if test="password != null">,#{password}</if><if test="age != null">,#{age}</if><if test="gender != null">,#{gender}</if>)</insert>

我們測試的時候選填其中兩個參數(性別,年齡):?

 @Testvoid insertBatch2() {Userinfo userinfo = new Userinfo();userinfo.setGender(1);userinfo.setAge(16);mapper.insertBatch2(userinfo);}

?測試不通過,觀察報錯日志發現是因為<if>標簽的原因,導致我們sql語句多加了個逗號。

那么我們能不能把逗號加在后面呢?同樣的,如果將逗號加在后面,那么后面也會多一個逗號。

下面我們使用<trim>標簽來解決問題:

 <insert id="insertBatch2">insert into userinfo<trim prefix="(" suffix=")" prefixOverrides=","><if test="username != null">username</if><if test="password != null">,password</if><if test="age != null">,age</if><if test="gender != null">,gender</if></trim>values <trim prefix="(" suffix=")" prefixOverrides=","> <if test="username != null">#{username}</if><if test="password != null">,#{password}</if><if test="age != null">,#{age}</if><if test="gender != null">,#{gender}</if></trim></insert>

?測試通過:

那么<trim>標簽的作用是什么呢??

<trim>標簽詳解:

<where>標簽

我們在淘寶上逛東西時,通常會有一些按鈕能夠動態組裝我們的查詢條件。

這種功能如何實現呢??

1、通過上面的<trim>標簽和<if>標簽實現

mapper:

  List<Userinfo> queryByConditin(Userinfo userinfo);

XML:?

   <select id="queryByConditin" resultType="com.example.mybatis.model.Userinfo">select * from userinfo<trim prefix="where" prefixOverrides="and"><if test="username != null">username = #{username}</if><if test="age != null">and age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="password != null">and password = #{password}</if></trim></select>

測試(查詢姓名為:“lisi” 年齡為:16的用戶):?

  @Testvoid queryByConditin() {Userinfo userinfo = new Userinfo();userinfo.setUsername("lisi");userinfo.setAge(16);System.out.println(mapper.queryByConditin(userinfo));

?這種查詢方法固然能夠成功,但是并不專業而且如果我們不添加任何查詢條件時sql語句會多出來一個where導致Badsql異常,我們可以使用where標簽來代替<trim>標簽。

2、使用<where>標簽實現

XML代碼:?

    <select id="queryByConditin" resultType="com.example.mybatis.model.Userinfo">select * from userinfo<where><if test="username != null">username = #{username}</if><if test="age != null">and age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="password != null">and password = #{password}</if></where></select>

測試:?

測試不加任何條件:

    @Testvoid queryByConditin() {Userinfo userinfo = new Userinfo();System.out.println(mapper.queryByConditin(userinfo));}

可以發現我們傳入的sql語句并沒有where。?

<where>標簽詳解:

<set>標簽

與選擇查詢相同有時候我們也要更新一些用戶的選項值,而保證其他值不變,比如:用戶修改手機號、用戶修改密碼等。

同樣的,這一功能也能通過<trim>標簽和<if>標簽實現:

mapper:

 Integer updateByConditin(Userinfo userinfo);

XML:

    <update id="updateByConditin">update userinfo<trim prefix="set" suffixOverrides=","><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender},</if><if test="password != null">password = #{password},</if></trim>where id = #{id}</update>

測試(修改id為6的用戶名和密碼):?

    @Testvoid updateByConditin() {Userinfo userinfo = new Userinfo();userinfo.setPassword("123456");userinfo.setUsername("wangwu");userinfo.setId(6);mapper.updateByConditin(userinfo);}

同樣的,我們也可以使用<set>標簽來代替這里的<trim>標簽和<if>標簽:?

    <update id="updateByConditin">update userinfo<set><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender},</if><if test="password != null">password = #{password},</if></set>where id = #{id}</update>

?測試(修改id為7的用戶名和密碼):

 @Testvoid updateByConditin() {Userinfo userinfo = new Userinfo();userinfo.setPassword("666666");userinfo.setUsername("wuwuwu");userinfo.setId(7);mapper.updateByConditin(userinfo);}

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

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

相關文章

智能圖書館管理系統開發實戰系列(一):項目架構設計與技術選型

項目背景 智能圖書館管理系統&#xff08;ILMS&#xff09;是一個現代化的桌面應用程序&#xff0c;采用前后端分離架構&#xff0c;結合了Web技術的靈活性和桌面應用的用戶體驗。本項目從高保真原型設計開始&#xff0c;經過完整的軟件開發生命周期&#xff0c;最終實現為一個…

應急前端“黃金3分鐘”設計:極端場景下的操作界面極速搭建技術

摘要**地震突發&#xff0c;應急指揮系統的操作界面卻因加載緩慢無法及時調取數據&#xff1b;火災現場&#xff0c;消防員手持終端的操作步驟繁瑣&#xff0c;延誤救援時機。在分秒必爭的極端場景中&#xff0c;傳統前端操作界面為何頻頻 “掉鏈子”&#xff1f;怎樣才能在 “…

【Android】三種彈窗 Fragment彈窗管理

三三要成為安卓糕手 零&#xff1a;布局轉換 在很多工程當中用的都是LinearLayout和relativelayout&#xff0c;這兩者都可以轉化為Constrainlayout 注&#xff1a;這種用法并不能精確轉換&#xff0c;具體還是要根據自己的需求來做布局約束一&#xff1a;snackbar顯示彈窗 ((2…

【AI繪畫】Stable Diffusion webUI 與 ComfyUI 全解析:安裝、模型、插件及功能對比

一、Stable Diffusion 與 UI 工具概述 Stable Diffusion 是當前最主流的開源 AI 繪畫模型&#xff0c;通過文本描述生成高質量圖像。為降低使用門檻&#xff0c;開發者推出了多種圖形界面&#xff08;UI&#xff09;工具&#xff0c;其中AUTOMATIC1111 webUI&#xff08;簡稱 …

ABP VNext + GraphQL Federation:跨微服務聯合 Schema 分層

ABP VNext GraphQL Federation&#xff1a;跨微服務聯合 Schema 分層 &#x1f680; 在微服務架構下&#xff0c;服務之間往往需要相互通信&#xff0c;而 GraphQL Federation 提供了一個有效的解決方案&#xff0c;幫助我們將多個微服務的 GraphQL API 聚合成一個統一的入口…

小程序組件的生命周期,以及在小程序中進行接口請求的方法設置

微信小程序組件生命周期與接口請求方法詳解一、小程序組件生命周期微信小程序組件的生命周期指的是組件在不同階段自動觸發的函數&#xff0c;開發者可以利用這些鉤子函數在特定時機執行相應操作。小程序組件的生命周期主要分為兩類&#xff1a;組件自身生命周期和組件所在頁面…

在線游戲玩家與物品交互處理

玩家與物品接觸后的判定if (hit ! null && hit.CompareTag("Item")){Debug.Log("撿東西");var worldItem hit.gameObject.GetComponent<WorldItem>();if (worldItem ! null){var inventory GetComponent<PlayerInventory>();if (inv…

深入解析Java Stream 構建:AbstractPipeline

Java Stream 宏觀介紹見&#xff1a;深入解析 Java Stream 設計&#xff1a;從四幕劇看流水線設計與執行機制-CSDN博客 PipelineHelper PipelineHelper 是 Java Stream API 內部一個至關重要的輔助類。正如其名&#xff0c;它是一個“管道助手”。可以把它想象成一個執行上下文…

《林景媚與命運回響》

《林景媚與命運回響》——當數據庫開始回響命運&#xff0c;現實是否還能被信任&#xff1f;《林景媚數據庫宇宙》系列第九部第一章&#xff1a;命運的漣漪公元 2089 年&#xff0c;數據庫神諭的運行已趨于穩定&#xff0c;PostgreSQL Quantum Engine&#xff08;PQE&#xff0…

圖神經網絡入門:從GNN開始01圖卷積網絡GCN節點分類 02圖注意力網絡GAT 03圖自編碼器GAE 04 門控圖神經網絡GGNN

目錄 一.基礎1-[圖論、圖算法、CNN] 二.基礎2-[圖卷積神經網絡GCN] 三.torch-geometric.nn工具包安裝&#xff08;包含各種算法和數據集&#xff09; 四.GCN任務[節點分類-Cora 數據集] 五.圖注意力網絡&#xff08;GAT&#xff09; 六.圖自編碼器&#xff08;GAE&#x…

001 Configuration結構體構造

目錄DramSys 代碼分析1 Configuration結構體構造1.1 from_path 函數詳解1.2 構造過程總結這種設計的好處2 Simulator 例化過程2.1 instantiateInitiatorDramSys 代碼分析 1 Configuration結構體構造 好的&#xff0c;我們來詳細解釋一下 DRAMSysConfiguration.cpp 文件中 fro…

以太坊十年:智能合約與去中心化的崛起

以太坊10周年&#xff0c;敬開發者&#xff0c;敬構建者&#xff0c;敬還在鏈上的我們 以太坊即將迎來十周年紀念,作為一名在這個生態中深耕了8到9年的見證者&#xff0c;我親歷了它從一紙白皮書的構想到成長為全球領先去中心化平臺的全過程。這十年間&#xff0c;以太坊經歷了…

kafka 3.9.1版本: kraft + sasl+ standlone 模式完整可行安裝步驟

Kafka 3.9.1 Kraft 單機模式安裝 安裝 OpenJDK 11 CentOS/RHEL yum install -y java-11-openjdk-develUbuntu/Debian apt install -y openjdk-11-jdk下載安裝包 wget https://mirrors.aliyun.com/apache/kafka/3.9.1/kafka_2.12-3.9.1.tgz tar -zxvf kafka_2.12-3.9.1.tgz -C /…

Gitee DevOps平臺深度評測:本土化優勢與功能特性全面解析

Gitee DevOps平臺深度評測&#xff1a;本土化優勢與功能特性全面解析 在數字化轉型浪潮下&#xff0c;企業軟件開發流程的自動化與協作效率成為核心競爭力。作為國內領先的代碼托管與DevOps平臺&#xff0c;Gitee&#xff08;碼云&#xff09;憑借其本土化服務與全流程支持能力…

從零開始本地化部署Dify:開源大模型應用平臺搭建全指南

在AI應用開發的浪潮中&#xff0c;Dify作為一款開源的大語言模型(LLM)應用開發平臺&#xff0c;正逐漸成為開發者和企業的首選工具。它巧妙地融合了后端即服務&#xff08;BaaS&#xff09;和LLMOps的理念&#xff0c;讓開發者能夠快速搭建生產級的生成式AI應用。無論是構建智能…

Qt 多媒體開發:音頻與視頻處理

Qt 多媒體模塊提供了一套完整的 API&#xff0c;用于開發音頻和視頻處理應用。從簡單的媒體播放到復雜的音視頻編輯&#xff0c;Qt 都提供了相應的工具和組件。本文將從基礎到高級全面解析 Qt 多媒體開發。 一、Qt 多媒體模塊概述 1. 主要組件 Qt 多媒體模塊包含以下核心組件&a…

Mac 專業圖像處理 Pixelmator Pro

原文地址&#xff1a;Pixelmator Pro Mac 專業圖像處理 Pixelmator Pro&#xff0c;是一款非常強大、美觀且易于使用的圖像編輯器&#xff0c;專為 Mac 設計。 采用單窗口界面、基于機器學習的智能圖像編輯、自動水平檢測&#xff0c;智能快速選擇及更好的修復工具等功能優點…

iptables和IPVS比較

iptables 和 IPVS (IP Virtual Server) 都是 Linux 系統上用于處理網絡流量的強大工具&#xff0c;但它們的設計目標、工作原理和適用場景有顯著區別&#xff1a; 核心區別&#xff1a;主要目的&#xff1a; iptables&#xff1a; 核心是一個包過濾防火墻和網絡地址轉換工具。它…

語音識別指標計算 WER

目錄 CER&#xff08;Character Error Rate&#xff09; WER Word Error Rate&#xff08;詞錯誤率&#xff09; &#x1f9ee; WER 計算方式 &#x1f4cc; 示例 ? 理解要點 CER&#xff08;Character Error Rate&#xff09; 語音識別中的 CER&#xff08;Character …

【前端基礎篇】JavaScript之jQuery介紹

文章目錄前言JQuery基本介紹和使用方法引入依賴jQuery語法jQuery選擇器jQuery事件操作元素獲取/設置元素內容獲取/設置元素屬性獲取/返回css屬性添加元素刪除元素總結&#xff1a;常用的jQuery方法 - 詳細解釋與示例事件處理拓展 - 詳細解釋與示例其他拓展內容前言 在閱讀過程…