欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MyBatis #{}和${} |與數(shù)據(jù)庫連接池使用詳解

 更新時(shí)間:2024年01月11日 10:43:50   作者:不能再留遺憾了  
本文將為大家說說關(guān)于 #{} 和 ${},這個(gè)是 MyBatis 在面試中最常問的面試題,以及數(shù)據(jù)庫連接池相關(guān)的知識(shí),感興趣的朋友跟隨小編一起看看吧

前言

前面我們學(xué)習(xí)了 MyBatis 的基礎(chǔ)操作,其中參數(shù)的傳遞使用到了#{},而參數(shù)的傳遞不僅僅只有這一個(gè)可以使用,還有 ${} 可以使用,那么今天這篇文章我將為大家說說關(guān)于 #{} 和 ${},這個(gè)是 MyBatis 在面試中最常問的面試題,以及數(shù)據(jù)庫連接池相關(guān)的知識(shí)。

#{}和${}的使用

首先我們還是需要在 YAML 配置文件中配置數(shù)據(jù)庫的相關(guān)配置。

# 配置數(shù)據(jù)庫相關(guān)信息
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: lmh041105666
# 打印mybatis日志
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

UserInfoMapper文件中的代碼

@Mapper
public interface UserInfoMapper {
	//這里是使用#{}作為參數(shù)的傳遞
    @Select("select * from userinfo where id=#{id}")
    public UserInfo selectById(Integer id);
}

單元測試中代碼

@Slf4j
@SpringBootTest
class UserInfoMapperTest {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Test
    void selectById() {
        log.info(userInfoMapper.selectById(3).toString());
    }
}

然后我們講這里的 #{} 改為 ${} 看看有什么效果。

@Select("select * from userinfo where id=${id}")
public UserInfo selectById(Integer id);


這里可以看到 #{}${} 的一個(gè)區(qū)別:使用 #{} 進(jìn)行參數(shù)的傳遞的時(shí)候,不是直接將輸入的參數(shù)拼接到后面,而是先使用 ? 進(jìn)行占位,這種 SQL 我們稱之為“預(yù)編譯SQL”。而使用 ${} 進(jìn)行參數(shù)傳遞的時(shí)候,會(huì)將輸入的參數(shù)直接拼接到 SQL 的后面,這種 SQL 我們稱之為“即時(shí)SQL”。這兩個(gè) SQL 的區(qū)別我們后面再說。

上面我們傳入的參數(shù)都是 Integer 類型的參數(shù),那么如果我們傳入的參數(shù)類型是 String 的話,會(huì)發(fā)生什么呢?

@Select("select * from userinfo where username=#{username}")
public UserInfo selectByName(String username);
@Test
void selectByName() {
    log.info(userInfoMapper.selectByName("小美").toString());
}

可以看到,使用 #{} 進(jìn)行參數(shù)的傳遞的時(shí)候,會(huì)對(duì)傳遞的參數(shù)的類型進(jìn)行識(shí)別,我們傳入的類型是 String 類型的話,就會(huì)識(shí)別為 String 類型,我們?cè)賮砜纯词褂?${} 傳遞參數(shù),參數(shù)的類型為 String 類型的情況。

@Select("select * from userinfo where username=${username}")
public UserInfo selectByName(String username);

通過觀察 MyBatis 打印出來的日志可以發(fā)現(xiàn),傳遞參數(shù)的時(shí)候,直接將我們傳遞的參數(shù)拼接到了 SQL 的末尾,而且沒有加上引號(hào),那么在 SQL 中沒有加引號(hào)的字符串會(huì)被認(rèn)為是表中的列,而我們當(dāng)前表中沒有 小美 這個(gè)列,所以就會(huì)報(bào)錯(cuò),那么我們?nèi)绾谓鉀Q這個(gè)問題呢?

因?yàn)槭褂?${} 進(jìn)行參數(shù)的傳遞的時(shí)候是直接將傳遞的參數(shù)拼接到 SQL 的對(duì)應(yīng)位置,所以我們可以手動(dòng)在 SQL 中加入引號(hào)。

@Select("select * from userinfo where username='${username}'")
public UserInfo selectByName(String username);

通過上面的兩個(gè)例子,我們可以發(fā)現(xiàn):#{} 使用的是預(yù)編譯 SQL,通過 ? 占位的方式,提前對(duì) SQL 進(jìn)行編譯,然后把參數(shù)填充到 SQL 語句中,并且 #{} 會(huì)根據(jù)參數(shù)類型,自動(dòng)拼接 ‘’。而 ${} 則會(huì)直接進(jìn)行字符的替換,一起對(duì) SQL 進(jìn)行編譯,如果參數(shù)為字符串類型,需要我們手動(dòng)在 SQL 中加入引號(hào)。

#{}和${}的區(qū)別

當(dāng)別人問我們 #{}${} 的區(qū)別的時(shí)候,實(shí)際上是在問預(yù)編譯 SQL 和即時(shí) SQL 的區(qū)別。

當(dāng)一個(gè)客戶發(fā)送一條 SQL 給服務(wù)器之后,大致流程是這樣的:

  • 解析語法和語義,校驗(yàn) SQL 語句是否正確
  • 優(yōu)化 SQL 語句,制定執(zhí)行計(jì)劃
  • 執(zhí)行并返回結(jié)果

一個(gè) SQL 如果執(zhí)行流程是這樣的話,那么這個(gè) SQL 就被成為 即時(shí)SQL。

但是呢,絕大多數(shù)情況下,某一條 SQL 語句可能會(huì)被反復(fù)調(diào)用執(zhí)行,或者每次執(zhí)行的時(shí)候只有個(gè)別的值不同(比如 selelct 的 where 子句值不同,update 的 set 子句不同,insert 的 values 值不同)。如果我們每次調(diào)用這種 SQL 都按照上面的語法解析、SQL 優(yōu)化、SQL 編譯等,那么效率就會(huì)降低了。

類似這種 SQL,我們就可以使用預(yù)編譯 SQL 的方法來提升效率。

預(yù)編譯SQL是一種常見的數(shù)據(jù)庫優(yōu)化技術(shù),其主要目的是提高數(shù)據(jù)庫查詢的效率和安全性。在預(yù)編譯SQL中,SQL語句和參數(shù)被分開處理。首先,SQL語句被編譯成一個(gè)中間形式,這個(gè)中間形式通常是與數(shù)據(jù)庫管理系統(tǒng)(DBMS)的內(nèi)部表示相對(duì)應(yīng)的。然后,參數(shù)值與編譯后的SQL語句進(jìn)行動(dòng)態(tài)綁定,最終生成可以直接執(zhí)行的SQL語句。后面再傳入?yún)?shù)執(zhí)行這條語句的時(shí)候(只是輸入的參數(shù)不同),省去了解析優(yōu)化的過程,一次來提高效率。

預(yù)編譯 SQL 的優(yōu)點(diǎn)不止是可以提高效率,還可以防止 SQL 注入。

什么是 SQL 注入?

SQL注入是一種常見的網(wǎng)絡(luò)攻擊方式,發(fā)生在Web應(yīng)用程序中。它利用了Web應(yīng)用程序?qū)τ脩糨斎霐?shù)據(jù)合法性沒有進(jìn)行充分驗(yàn)證或過濾不嚴(yán)的漏洞。攻擊者可以在Web應(yīng)用程序中事先定義好的查詢語句的結(jié)尾上添加額外的SQL語句,從而實(shí)現(xiàn)在管理員不知情的情況下執(zhí)行非法操作。

當(dāng)用戶在Web頁面上輸入數(shù)據(jù)時(shí),如果這些數(shù)據(jù)未經(jīng)過適當(dāng)?shù)尿?yàn)證和處理,攻擊者可以在其中插入或“注入”惡意的SQL代碼。當(dāng)這些數(shù)據(jù)被用來構(gòu)建SQL查詢時(shí),這些惡意的SQL代碼就會(huì)被執(zhí)行。攻擊者可以執(zhí)行非授權(quán)的任意查詢,從而進(jìn)一步得到相應(yīng)的數(shù)據(jù)信息。

例如,假設(shè)一個(gè)Web應(yīng)用程序的登錄頁面允許用戶輸入用戶名和密碼。如果應(yīng)用程序沒有對(duì)用戶輸入進(jìn)行適當(dāng)?shù)尿?yàn)證,攻擊者可以在用戶名或密碼字段中輸入如’ OR ‘1’='1之類的SQL代碼。當(dāng)應(yīng)用程序?qū)⑦@些數(shù)據(jù)用于構(gòu)建SQL查詢時(shí),這些代碼會(huì)使得查詢變成
SELECT * FROM users WHERE username='' OR '1'='1' AND password='password',從而繞過正常的驗(yàn)證,獲得所有用戶的訪問權(quán)限。

因?yàn)?${} 傳遞參數(shù)的方式也就是即時(shí) SQL 不會(huì)對(duì)用戶的輸入進(jìn)行充分檢查,而 SQL 又是直接拼接而成的,在用戶輸入?yún)?shù)的時(shí)候,在參數(shù)中添加一些 SQL 關(guān)鍵字,達(dá)到改變 SQL 運(yùn)行結(jié)果的目的,從而實(shí)現(xiàn)惡意攻擊。

給大家舉個(gè)例子:

@Select("select * from userinfo where username='${username}'")
public List<UserInfo> selectByName(String username);
@Test
void selectByName() {
    log.info(userInfoMapper.selectByName("'or 1='1").toString());
}

通過這樣傳遞參數(shù)的話,那么將我們傳遞的參數(shù)直接拼接在 SQL 中,就會(huì)出現(xiàn)這樣的 SQL:select * from userinfo where username='' or 1='1',在這里 1 會(huì)被解釋為字符串 ‘1’,那么這時(shí) ‘1’='1’為真,那么這時(shí)就會(huì)將數(shù)據(jù)庫中該表中的所有信息都給顯示出來,這樣就會(huì)導(dǎo)致數(shù)據(jù)的泄露。

通過觀察代碼的執(zhí)行結(jié)果,我們也可以發(fā)現(xiàn),表中的所有信息都被顯示出來了。

而如果我們使用 #{} 來實(shí)現(xiàn) SQL 的注入,看看可以達(dá)到效果嗎?

@Select("select * from userinfo where username=#{username}")
public List<UserInfo> selectByName(String username);
@Test
void selectByName() {
    log.info(userInfoMapper.selectByName("'or 1='1").toString());
}

通過預(yù)編譯 SQL 占位的方式,我們輸入的參數(shù)會(huì)被認(rèn)為是表中的 username 列中的一個(gè)名稱,而不會(huì)被解析成為上面的 SQL,表中沒有一個(gè)叫“or 1='1"的username,所以查詢結(jié)果為0,也就是說,通過預(yù)編譯 SQL 的方式可以有效的解決 SQL 注入的情況。

所以為了防止 SQL 注入,我們應(yīng)盡量選擇 #{} 來使用,那么是否就代表著 ${} 就不被使用了了呢?不是這樣的,既然出現(xiàn)了,那么它就有存在的意義。

排序功能

有一些情況下,只能使用 ${},而不能使用 #{}。其中很典型的情況就是涉及到排序的時(shí)候,根據(jù)我們輸入的參數(shù)決定是升序排列還是降序排列。

@Select("select * from userinfo order by id #{sort}")
public List<UserInfo> selectBySort(String sort);
@Test
void selectBySort() {
    log.info(userInfoMapper.selectBySort("desc").toString());
}

可以看到,本來我們進(jìn)行排序查詢的話,排序規(guī)則是不需要加上引號(hào)的,而在預(yù)編譯占位的情況下,因?yàn)閭鬟f來的參數(shù)是字符串類型,所以在拼接 SQL 的時(shí)候,該參數(shù)就被加上了引號(hào),所以就導(dǎo)致出現(xiàn)錯(cuò)誤,在這種情況下我們就得使用 ${} 來進(jìn)行 SQL 的直接拼接。

@Select("select * from userinfo order by id ${sort}")
public List<UserInfo> selectBySort(String sort);

所以當(dāng)我們進(jìn)行傳遞排序規(guī)則的時(shí)候,需要使用到 ${}

模糊查詢

MySQL 中的模糊查詢使用 like 關(guān)鍵字,% 用來匹配任意任意數(shù)量的字符(包含0個(gè)),_ 用來匹配單個(gè)字符,那么如果我們想用參數(shù)傳遞的方式來指定模糊查詢的規(guī)則的話,就不能使用 #{} 的。

@Select("select * from userinfo where username like '%#{like}%'")
public List<UserInfo> selectByLike(String like);
@Test
void selectByLike() {
    log.info(userInfoMapper.selectByLike("小").toString());
}

同樣也是,使用 #{} 進(jìn)行參數(shù)的傳遞的話,因?yàn)閭鬟f過來的參數(shù)的類型是字符串類型,所以在進(jìn)行 SQL 拼接的時(shí)候就會(huì)自動(dòng)加上引號(hào)也就是 ‘%‘小’%’,這樣的形成的 SQL,就是有問題的 SQL。

我們使用 ${} 的話就不會(huì)出現(xiàn)這種問題:

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

但是在這種情況下還是會(huì)發(fā)生 SQL 注入的情況,所以我們不推薦使用 ${},而是使用 SQL 內(nèi)置的函數(shù) concat()。

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

總結(jié) #{} 和 ${} 的區(qū)別

  • 在使用 #{} 進(jìn)行參數(shù)的傳遞的時(shí)候,會(huì)根據(jù)參數(shù)的類型,在拼接 SQL 的時(shí)候做出調(diào)整,而 ${} 則是直接進(jìn)行 SQL 的拼接。
  • 在排序,傳遞排序規(guī)則的時(shí)候不能使用 #{},因?yàn)?SQL 編譯的時(shí)候會(huì)根據(jù)傳遞的字符串類型,在 SQL 中加入引號(hào),所以這種情況下只能使用 ${}。
  • 進(jìn)行模糊查詢的時(shí)候,雖然直接使用 #{} 會(huì)出現(xiàn)問題,使用 ${} 不會(huì)出現(xiàn)問題,但是我們不使用 ${},因?yàn)闀?huì)發(fā)生 SQL 注入,而是使用 SQL 內(nèi)置函數(shù) concat()
  • 最重要的就是預(yù)編譯 SQL 和即時(shí) SQL 的區(qū)別:
    • 預(yù)編譯 SQL 性能更高
    • 預(yù)編譯 SQL 不存在 SQL 注入的問題

所以在實(shí)際開發(fā)的過程中,能使用 #{} 的情況就盡量使用 #{},如果要使用 ${},就一定要考慮到 SQL 注入的問題。

數(shù)據(jù)庫連接池

數(shù)據(jù)庫連接池是程序在啟動(dòng)時(shí)創(chuàng)建足夠多的數(shù)據(jù)庫連接,并將這些連接放入一個(gè)池子中。這個(gè)池子中的連接可以被程序動(dòng)態(tài)地申請(qǐng)、使用和釋放。其作用是分配、管理和釋放數(shù)據(jù)庫連接,使得應(yīng)用程序可以重復(fù)使用現(xiàn)有的同一個(gè)數(shù)據(jù)庫連接,而不是每次都重新建立連接,從而避免了資源的消耗,提高了程序執(zhí)行的效率。

當(dāng)沒有數(shù)據(jù)庫連接池的話,當(dāng)客戶端向服務(wù)器發(fā)送 SQL 請(qǐng)求的話,會(huì)先創(chuàng)建一個(gè) Connection 連接,然后通過這個(gè)連接訪問到服務(wù)器,當(dāng)請(qǐng)求完成之后,這個(gè)連接又會(huì)被銷毀,下次再請(qǐng)求的時(shí)候,還是需要先創(chuàng)立連接,然后再釋放連接,這樣是比較消耗資源的。

當(dāng)使用數(shù)據(jù)庫連接池的情況,當(dāng)程序啟動(dòng)的時(shí)候,就會(huì)在數(shù)據(jù)庫連接池中創(chuàng)建一定數(shù)量的 Connection 對(duì)象,當(dāng)客戶端需要訪問服務(wù)器的時(shí)候,可以直接使用數(shù)據(jù)庫連接池中的 Connection 對(duì)象,并且當(dāng)使用完成之后,會(huì)將 Connection 對(duì)象歸還給數(shù)據(jù)庫連接池,這樣就極大的節(jié)省了 CConnection 對(duì)象的創(chuàng)建和銷毀所需要的時(shí)間。

總的來說,使用數(shù)據(jù)庫連接池具有以下優(yōu)點(diǎn):

  • 減少網(wǎng)絡(luò)開銷
  • 資源重用
  • 提升系統(tǒng)的性能

常見的數(shù)據(jù)庫連接池有:C3P0、DBCP、Druid、Hikari,當(dāng)前比較流行的是 Hikari和Druid。

而在我們的 SpringBoot 中,默認(rèn)使用的數(shù)據(jù)庫連接池是 Hikari。

當(dāng)我們啟動(dòng) MyBatis 的 SpringBoot 項(xiàng)目的時(shí)候,就會(huì)啟動(dòng) Hikari 數(shù)據(jù)庫連接池,建立 Connection 對(duì)象。

如果我們想要更換 SpringBoot 使用的數(shù)據(jù)庫連接池的話,只需要將要更換到的數(shù)據(jù)庫連接池的依賴添加到 pom.xml 文件即可。

這里假設(shè)我們更換 SpringBoot 的數(shù)據(jù)庫連接池為 Druid:

將下面這個(gè)坐標(biāo)添加到 pom.xml 文件中。并且刷新 pom.xml 文件,將新添加的依賴下載下來。

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

到此這篇關(guān)于MyBatis #{}和${} | 數(shù)據(jù)庫連接池的文章就介紹到這了,更多相關(guān)MyBatis #{}和${}內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring的xml文件打開沒有namespace等操作選項(xiàng)的解決方案

    spring的xml文件打開沒有namespace等操作選項(xiàng)的解決方案

    這篇文章主要介紹了spring的xml文件打開沒有namespace等操作選項(xiàng)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java?Selenium實(shí)現(xiàn)修改打開頁面窗口大小

    Java?Selenium實(shí)現(xiàn)修改打開頁面窗口大小

    Selenium是一個(gè)強(qiáng)大的自動(dòng)化測試工具,支持多種編程語言和瀏覽器,本文將詳細(xì)介紹如何使用Java?Selenium來修改打開頁面窗口的大小,需要的可以參考下
    2025-01-01
  • Java自旋鎖及自旋的好處詳解

    Java自旋鎖及自旋的好處詳解

    這篇文章主要介紹了Java自旋鎖及自旋的好處詳解,自旋就是自己在這里不停地循環(huán),直到目標(biāo)達(dá)成,而不像普通的鎖那樣,如果獲取不到鎖就進(jìn)入阻塞,需要的朋友可以參考下
    2023-10-10
  • Java基于Base64實(shí)現(xiàn)編碼解碼圖片文件

    Java基于Base64實(shí)現(xiàn)編碼解碼圖片文件

    這篇文章主要介紹了Java基于Base64實(shí)現(xiàn)編碼解碼圖片文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Spring IOC原理詳解

    Spring IOC原理詳解

    這篇文章主要介紹了Spring IOC原理詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • java 中InputStream,String,File之間的相互轉(zhuǎn)化對(duì)比

    java 中InputStream,String,File之間的相互轉(zhuǎn)化對(duì)比

    這篇文章主要介紹了java 中InputStream,String,File之間的相互轉(zhuǎn)化對(duì)比的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 詳解Mybatis的分頁插件

    詳解Mybatis的分頁插件

    這篇文章主要介紹了詳解Mybatis的分頁插件,在 Mybatis中,如何對(duì)數(shù)據(jù)進(jìn)行分頁是一個(gè)非常常見的問題,現(xiàn)在,我們可以通過使用 Mybatis 的分頁插件來實(shí)現(xiàn)對(duì)數(shù)據(jù)的分頁,需要的朋友可以參考下
    2023-05-05
  • RabbitMQ簡單隊(duì)列實(shí)例及原理解析

    RabbitMQ簡單隊(duì)列實(shí)例及原理解析

    這篇文章主要介紹了RabbitMQ簡單隊(duì)列實(shí)例及原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 基于java file 文件操作operate file of java的應(yīng)用

    基于java file 文件操作operate file of java的應(yīng)用

    本篇文章介紹了,基于java file 文件操作operate file of java的應(yīng)用。需要的朋友參考下
    2013-05-05
  • 利用java和sqlserver建立簡易圖書管理系統(tǒng)的完整步驟

    利用java和sqlserver建立簡易圖書管理系統(tǒng)的完整步驟

    圖書館管理系統(tǒng)是圖書館管理工作中不可缺少的部分,它對(duì)于圖書館的管理者和使用者都非常重要,下面這篇文章主要給大家介紹了關(guān)于利用java和sqlserver建立簡易圖書管理系統(tǒng)的完整步驟,需要的朋友可以參考下
    2022-06-06

最新評(píng)論