MyBatis 探秘之#{} 與 ${} 參傳差異解碼(數(shù)據(jù)庫(kù)連接池筑牢數(shù)據(jù)交互根基)
???1. #{} 與 ${}的使用
我們?cè)谥暗膶W(xué)習(xí)中,了解到了“#{}”,但是這里的${}是什么呢?其實(shí)這里的${}也具有參數(shù)傳遞的功能但是我們之前為什么不使用$符號(hào)呢?且聽(tīng)下面的分析過(guò)程~~~
1.1integer類型數(shù)據(jù)
我們可以通過(guò)id類整型參數(shù)的傳遞進(jìn)行實(shí)驗(yàn),首先得先創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),如下所示:
接下來(lái)我們知己使用XML的方式進(jìn)行代碼的編寫(xiě):
在Mapper類中:
List<UserInfo> select2(Integer id);
這里先定義數(shù)據(jù)的返回類型,然后再在XML文件中實(shí)現(xiàn)查詢SQL的方法:
<select id="select2" resultType="com.example.mybatis.Model.UserInfo"> select * from user_info where id=#{id} </select>
然后再測(cè)試類進(jìn)行測(cè)試后們可以看到如下的打印的日志:
解釋:
可以發(fā)現(xiàn)在SQL查詢語(yǔ)句中,參數(shù)被“?”給替代了,然后下面的參數(shù)就是“2”,此時(shí)可以了解到我們輸?的參數(shù)并沒(méi)有在后?拼接,id的值是使? ? 進(jìn)?占位. 這種SQL 我們稱之為"預(yù)編譯SQL"
然后我們將這里的“#{ }” 替換成“${}”,具體的情況就是如下所示的:
解釋:
可以發(fā)現(xiàn)此時(shí)SQL語(yǔ)句,就沒(méi)有“?”,然后下面的paramters參數(shù)就為空了,然后我們就知道${} 會(huì)直接進(jìn)?字符替換, ?起對(duì)SQL進(jìn)?編譯,不會(huì)像#號(hào)一樣使用占位符;這種我們就稱之為即時(shí)SQL
1.2String類型數(shù)據(jù)
這里我們使用String類來(lái)進(jìn)行匹配查詢,具體的操作還是和上面差不多;
Mapper類的代碼如下:
List<UserInfo> selectAllByUsername(String username);
然后再XML寫(xiě)SQL語(yǔ)句的操作:
解釋:
此時(shí)可以看到,和上面的string類型的數(shù)據(jù)是一樣的,這里會(huì)標(biāo)明參數(shù)的類型為string類型,然后再進(jìn)行替換占位的時(shí)候會(huì)自動(dòng)添加引號(hào);
然后我們將這里的“#{ }” 替換成“${}”,具體的情況就是如下所示的:
<select id="selectAllByUsername" resultType="com.example.mybatis.Model.UserInfo"> select * from user_info where username=${username} </select>
這里就是通過(guò)輸入的名字進(jìn)行查詢,將這#號(hào)替換成了$符號(hào),然后再測(cè)試類進(jìn)行測(cè)試后打印的日志報(bào)錯(cuò)了,具體的打印日志如下所示:
解釋:
這里可以看到此時(shí)的$符號(hào)的操作,出現(xiàn)了報(bào)錯(cuò),原因就是BadSql,我們?cè)谏厦婵吹剑捎谥苯哟娴脑?,查詢條件中字符串沒(méi)有出現(xiàn)“ ' ' ”符號(hào),即引號(hào),然后就是SQL語(yǔ)句語(yǔ)法不正確導(dǎo)致錯(cuò)誤;
解決辦法:
在XML編寫(xiě)SQL語(yǔ)句的時(shí)候進(jìn)行手動(dòng)添加' ';
代碼如下:
<select id="selectAllByUsername" resultType="com.example.mybatis.Model.UserInfo"> select * from user_info where username='${username}' </select>
此時(shí)進(jìn)行測(cè)試打印,打印的日志就是如下所示的:
解釋:此時(shí)可以看到在字符串添加了“ ' ' ”雙引號(hào),然后參數(shù)仍然為空;
綜上所述:
#{} 使?的是預(yù)編譯SQL, 通過(guò) ? 占位的?式, 提前對(duì)SQL進(jìn)?編譯, 然后把參數(shù)填充到SQL語(yǔ)句中. #{} 會(huì)根據(jù)參數(shù)類型, ?動(dòng)拼接引號(hào) '' .
${} 使用的就是即時(shí)編譯SQL,會(huì)直接進(jìn)?字符替換, ?起對(duì)SQL進(jìn)?編譯. 如果參數(shù)為字符串, 需要加上引號(hào) ''
???2. #{} 與 ${}的區(qū)別
2.1性能
當(dāng)客?發(fā)送?條SQL語(yǔ)句給服務(wù)器后, ?致流程如下:
1. 解析語(yǔ)法和語(yǔ)義, 校驗(yàn)SQL語(yǔ)句是否正確
2. 優(yōu)化SQL語(yǔ)句, 制定執(zhí)?計(jì)劃
3. 執(zhí)?并返回結(jié)果
?條 SQL如果?上述流程處理, 我們稱之為 Immediate Statements(即時(shí) SQL)
但是絕?多數(shù)情況下, 某?條 SQL 語(yǔ)句可能會(huì)被反復(fù)調(diào)?執(zhí)?, 或者每次執(zhí)?的時(shí)候只有個(gè)別的值不同(?如 select 的 where ?句值不同, update 的 set ?句值不同, insert 的 values 值不同). 如果每次都需要經(jīng)過(guò)上?的語(yǔ)法解析, SQL優(yōu)化、SQL編譯等,則效率就明顯不?了
總結(jié):
所以預(yù)編譯SQL就在執(zhí)行上述的優(yōu)化操作后,遇到同樣的SQL語(yǔ)句,就不會(huì)對(duì)SQL進(jìn)行再次的優(yōu)化編譯了,就直接改變參數(shù),省去了解析優(yōu)化等過(guò)程, 以此來(lái)提?效率
預(yù)編譯SQL的性能比即時(shí)SQL的性能更高;
2.2安全性
這里出現(xiàn)的安全性就是(SQL注入)
SQL注?:是通過(guò)操作輸?的數(shù)據(jù)來(lái)修改事先定義好的SQL語(yǔ)句,以達(dá)到執(zhí)?代碼對(duì)服務(wù)器進(jìn)?攻擊的?法。由于沒(méi)有對(duì)??輸?進(jìn)?充分檢查,?SQL?是拼接?成,在??輸?參數(shù)時(shí),在參數(shù)中添加?些SQL關(guān)鍵字,達(dá)到改變SQL運(yùn)?結(jié)果的?的,也可以完成惡意攻擊
注意:這里只針對(duì)的就是${ }符號(hào);
假如我們?cè)诖朔?hào)中添加 ' or 1='1
然后在#{ }運(yùn)行中,可以發(fā)現(xiàn)此時(shí)的此時(shí)的打印日志告訴我們,這里的字符串在數(shù)據(jù)庫(kù)中沒(méi)有找到
如下所示:
這里的結(jié)果確實(shí)如我們所料,但是在${ }符號(hào)中,如下所示:
解釋:
哎奇怪,怎么會(huì)出現(xiàn)這種情況,這個(gè)字符串在我們的數(shù)據(jù)庫(kù)列中就沒(méi)有這個(gè)字段;但是為啥就全部搜索出來(lái)了呢??
因?yàn)槠唇拥膯?wèn)題和原因,所以這里出現(xiàn)了誤判,' {' or 1='1} ' 拼接后成了以下SQL語(yǔ)句
' ' or 1='1'
解釋:
具體的意思就是為空或者為true,這就是為啥全部搜索出來(lái)了,這里存在SQL關(guān)鍵字or;
所以SQL注?是?種?常常?的數(shù)據(jù)庫(kù)攻擊?段, SQL注?漏洞也是?絡(luò)世界中最普遍的漏洞之?.
如果發(fā)?在??登錄的場(chǎng)景中, 密碼輸?為 ' or 1='1 , 就可能完成登錄;
總之在后面的學(xué)習(xí)中能用#{} 那么就使用#{ },${ }非特殊情況盡量不要使用
???3.${ }使用場(chǎng)景
3.1排序
在實(shí)現(xiàn)排序功能的時(shí)候,由于“desc”不需要使用引號(hào),所以這里我們就可以使用${}符號(hào)
具體的代碼如下所示:
<select id="selectAllById" resultType="com.example.mybatis.Model.UserInfo"> select * from user_info order by id ${sort} </select>
這里即時(shí)XML的寫(xiě)法,可以看到此時(shí)order by id然后排序關(guān)鍵詞,就不需要引號(hào),那么此時(shí)我們就可以使用$符號(hào);
在測(cè)試類中的實(shí)現(xiàn)代碼:
@Test void selectAllById() { userInfoXMLMapper.selectAllById("desc"); }
我們這里就是按照降序排序進(jìn)行查詢結(jié)果的排序的,最后打印的日志如下所示:
解釋:
上面的兩行就是SQL語(yǔ)句和參數(shù),參數(shù)為空,然后即時(shí)SQL進(jìn)行拼接,SQL語(yǔ)句就成為了一個(gè)查詢語(yǔ)句按照降排序的方式進(jìn)行查詢結(jié)果的展示;
3.2模糊查詢
代碼如下所示:
<select id="selectByUsername" resultType="com.example.mybatis.Model.UserInfo"> select * from user_info where username like '%${username}%' </select>
這里也是不需要引號(hào),然后也可以使用$符號(hào),在測(cè)試類中代碼:
@Test void selectByUsername() { userInfoXMLMapper.selectByUsername("o"); }
最后打印的日志如下所示:
解釋:
這里的模糊查詢,中間的參數(shù)是不需要自動(dòng)添加引號(hào)的,并且這里的模糊查詢的條件就是查找名字里包含“o”的那一段數(shù)據(jù);
但是這里由于注入等安全性,這里我們可以使用#進(jìn)行另一種寫(xiě)法,具體的代碼如下所示:
<select id="selectByUsername2" resultType="com.example.mybatis.Model.UserInfo"> select * from user_info where username like concat('%',#{username},'%') </select>
解釋:
這里使用concat關(guān)鍵字,實(shí)現(xiàn)需要引號(hào)的拼接操作,這樣就可以使用#{}來(lái)進(jìn)行參數(shù)的傳遞,幾避免了SQL注入的安全問(wèn)題,還可能提高了執(zhí)行的效率;
???4.數(shù)據(jù)庫(kù)連接池
4.1介紹
數(shù)據(jù)庫(kù)連接池負(fù)責(zé)分配、管理和釋放數(shù)據(jù)庫(kù)連接,它允許應(yīng)?程序重復(fù)使??個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)連接,?不是再重新建??個(gè)
沒(méi)有使?數(shù)據(jù)庫(kù)連接池的情況: 每次執(zhí)?SQL語(yǔ)句, 要先創(chuàng)建?個(gè)新的連接對(duì)象, 然后執(zhí)?SQL語(yǔ)句, SQL語(yǔ)句執(zhí)?完, 再關(guān)閉連接對(duì)象釋放資源. 這種重復(fù)的創(chuàng)建連接, 銷毀連接?較消耗資源
使?數(shù)據(jù)庫(kù)連接池的情況: 程序啟動(dòng)時(shí), 會(huì)在數(shù)據(jù)庫(kù)連接池中創(chuàng)建?定數(shù)量的Connection對(duì)象, 當(dāng)客?請(qǐng)求數(shù)據(jù)庫(kù)連接池, 會(huì)從數(shù)據(jù)庫(kù)連接池中獲取Connection對(duì)象, 然后執(zhí)?SQL, SQL語(yǔ)句執(zhí)?完, 再把Connection歸還給連接池
優(yōu)點(diǎn):
1. 減少了?絡(luò)開(kāi)銷
2. 資源重?
3. 提升了系統(tǒng)的性能
4.2使用
常?的數(shù)據(jù)庫(kù)連接池:
• C3P0
• DBCP
• Druid
• Hikari
主流就是Hikari,Druid;
我們的SpringBoot默認(rèn)使用的就是Hikari;日志如下所示:
可以看到這就是springboot默認(rèn)使用的就是Hikari;
???5.總結(jié)
本期小編主要講解了關(guān)于#{ },${ },的區(qū)別與如何進(jìn)行使用,講解了兩者的性能比較,比較重要的SQL注入的問(wèn)題,以及簡(jiǎn)單的闡述了數(shù)據(jù)庫(kù)連接池的介紹~~~
到此這篇關(guān)于MyBatis 探秘:#{} 與 ${} 參傳差異解碼,數(shù)據(jù)庫(kù)連接池筑牢數(shù)據(jù)交互根基的文章就介紹到這了,更多相關(guān)MyBatis #{} 與 ${} 參傳差異解碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Java?NIO編寫(xiě)一個(gè)簡(jiǎn)單版Netty服務(wù)端
基于?NIO?實(shí)現(xiàn)的網(wǎng)絡(luò)框架,可以用少量的線程,處理大量的連接,更適用于高并發(fā)場(chǎng)景,所以被就將利用NIO編寫(xiě)一個(gè)簡(jiǎn)單版Netty服務(wù)端,需要的可以參考下2024-04-04Java爬蟲(chóng) 信息抓取的實(shí)現(xiàn)
本文主要介紹 Java爬蟲(chóng) 信息抓取的實(shí)現(xiàn),這里詳細(xì)介紹了如何實(shí)現(xiàn)該方法,并附示例代碼供大家學(xué)習(xí)參考,有興趣的小伙伴可以參考下2016-09-09MyBatis-Plus集成Druid環(huán)境搭建的詳細(xì)教程
這篇文章主要介紹了MyBatis-Plus集成Druid環(huán)境搭建的詳細(xì)教程,需要的朋友可以參考下2020-08-08Java類中this關(guān)鍵字與static關(guān)鍵字的用法解析
這篇文章主要介紹了Java類中this關(guān)鍵字與static關(guān)鍵字的用法解析,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09MybatisPlus查詢數(shù)據(jù)日期格式化問(wèn)題解決方法
MyBatisPlus是MyBatis的增強(qiáng)工具,支持常規(guī)的CRUD操作以及復(fù)雜的聯(lián)表查詢等功能,這篇文章主要給大家介紹了關(guān)于MybatisPlus查詢數(shù)據(jù)日期格式化問(wèn)題的解決方法,需要的朋友可以參考下2023-10-10Java如何比較兩個(gè)對(duì)象并獲取不相等的字段詳解
這篇文章主要給大家介紹了關(guān)于Java如何比較兩個(gè)對(duì)象并獲取不相等的字段以及JAVA判斷(獲?。﹥蓚€(gè)相同對(duì)象不同的數(shù)據(jù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-11-11