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