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

MyBatis中#{}?和?${}?的區(qū)別和動(dòng)態(tài)?SQL詳解

 更新時(shí)間:2024年11月23日 10:10:03   作者:2的n次方_  
這篇文章主要介紹了MyBatis中#{}和${}的區(qū)別,包括參數(shù)傳遞、安全性、性能等方面,然后詳細(xì)介紹了如何使用#{}和${}進(jìn)行排序、模糊查詢、動(dòng)態(tài)SQL、數(shù)據(jù)庫連接池等操作,最后,總結(jié)了注解方式的動(dòng)態(tài)SQL,感興趣的朋友跟隨小編一起看看吧

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

為了方便,接下來使用注解方式來演示:

#{} 的 SQL 語句中的參數(shù)是用過 ? 來起到類似于占位符的作用,而 ${} 是直接進(jìn)行參數(shù)替換,這種直接替換的即時(shí) SQL 就可能會(huì)出現(xiàn)一個(gè)問題

當(dāng)傳入一個(gè)字符串時(shí),就會(huì)發(fā)現(xiàn) SQL 語句出錯(cuò)了:

這里的 zhangsan并不是作為一個(gè)字符串使用的,應(yīng)該是加上引號(hào)的

加上之后就可以正常查詢了

這就可能會(huì)出現(xiàn) SQL 注入的問題

來看一下 SQL 注入的例子,假如傳入的參數(shù)是' or 1='1

@Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")
List<UserInfo> queryByName(String name);

按道理說是沒有這個(gè)用戶的,但是卻把所有用戶的信息都查出來了

如果在某些登錄的界面輸入 SQL 注入代碼' or 1='1就可能登錄成功

使用 #{} 就沒有這個(gè)問題

除了以上的區(qū)別外,二者還有性能方面的區(qū)別

在上面提到過,#{} 是預(yù)編譯 SQL,${} 是即時(shí) SQL ,預(yù)編譯SQL編譯一次之后會(huì)將編譯后的 SQL 語句緩存起來,后面再執(zhí)行這條語句時(shí),不會(huì)再次編譯,省去了解析優(yōu)化等過程,以此來提高效率,所以當(dāng)需要頻繁地使用 SQL 語句時(shí),預(yù)編譯的性能優(yōu)化就體現(xiàn)出來了,而對(duì)于即時(shí) SQL ,如果只是在啟動(dòng)時(shí)或者很少變化的場(chǎng)景下使用${}來配置一些數(shù)據(jù)庫對(duì)象名稱等,它可以避免預(yù)編譯的過程,執(zhí)行起來相對(duì)直接

2. 排序

在上面看來,#{} 無論是在安全性還是效率上,都占據(jù)了優(yōu)勢(shì),那么都是用 #{}可以嗎?

來看使用 #{}來實(shí)現(xiàn)排序功能:

@Select("select * from user_info order by id #{order}")
List<UserInfo> selectUserByOrder(String order);

這里把排序的方式作為參數(shù),給用戶選擇是升序還是降序排序,測(cè)試方法中傳入一個(gè)字符串表示降序

@Test
void selectUserByOrder() {
    userInfoMapper.selectUserByOrder("desc");
}

然后就會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了,可以看到 "desc" 確實(shí)是當(dāng)做字符串傳進(jìn)去了,#{} 的方式會(huì)把字符串類型加上單引號(hào),然后 SQL 語句就會(huì)變成這樣:

select * from user_info order by id 'order'

這樣肯定是不對(duì)的,那么這個(gè)時(shí)候就需要用到 ${} 了,直接進(jìn)行參數(shù)替換,但是使用 ${} 肯定就需要考慮 SQL 注入的問題,由于排序方式只有 asc 和 desc 兩種方式,可以采用枚舉類來進(jìn)行校驗(yàn),也可以通過判斷條件來實(shí)現(xiàn)校驗(yàn)

3. 模糊查詢

通過模糊查詢來查找名字中含有“zhang”的信息

@Select("select * from user_info where username like '%#{name}%'")
List<UserInfo> selectUserByLike(String name);
@Test
void selectUserByLike() {
    System.out.println(userInfoMapper.selectUserByLike("zhang"));
}

然后發(fā)現(xiàn)又報(bào)錯(cuò)了,因?yàn)槭褂玫氖?#{} ,所以就會(huì)替換為 '%'zhang'%',這樣是肯定不能運(yùn)行的,所以還是需要使用 ${} 進(jìn)行直接替換,但是這時(shí)怎么去解決 SQL 注入的問題呢,這樣就不能簡單的通過枚舉或者判斷來約束傳入的參數(shù)了,這時(shí)就可以通過使用拼接的方式

通過 CONCAT 函數(shù)來對(duì) SQL 語句進(jìn)行拼接,這樣就可以使用 #{},

@Select("select * from user_info where username like CONCAT('%',#{name},'%')")
List<UserInfo> selectUserByLike(String name);

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

在傳統(tǒng)的數(shù)據(jù)庫訪問模式中,每當(dāng)應(yīng)用程序需要與數(shù)據(jù)庫進(jìn)行交互時(shí),它會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù)庫連接,使用完畢后關(guān)閉連接,這樣頻繁地創(chuàng)建和銷毀數(shù)據(jù)庫連接會(huì)消耗大量的系統(tǒng)資源

數(shù)據(jù)庫連接池的出現(xiàn)就是為了解決這些問題。它在應(yīng)用程序啟動(dòng)時(shí)預(yù)先創(chuàng)建一定數(shù)量的數(shù)據(jù)庫連接,將這些連接存儲(chǔ)在一個(gè) “池” 中。當(dāng)應(yīng)用程序需要訪問數(shù)據(jù)庫時(shí),從池中獲取一個(gè)可用的連接,使用完畢后將連接歸還給池,而不是直接關(guān)閉連接,從而避免了頻繁創(chuàng)建和銷毀連接所帶來的性能開銷,這一點(diǎn)和線程池是類似的

常見的數(shù)據(jù)庫連接池有:C3P0 , DBCP , Druid , Hikari

Spring Boot 默認(rèn)使用的是 Hikari

如果想更換為 Druid 的話,導(dǎo)入相關(guān)的依賴即可

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

然后再啟動(dòng)程序之后就更換為了 Druid

也可以去 官方文檔 進(jìn)行查看

5. 動(dòng)態(tài) SQL

我們?cè)谔钜恍┍韱蔚臅r(shí)候應(yīng)該會(huì)見到下面這種,有的是必填項(xiàng),有的是選填項(xiàng),對(duì)于選填項(xiàng)來說,如果沒有填,肯定是需要賦一個(gè)默認(rèn)值的,比如 null,那么就需要?jiǎng)討B(tài) SQL 來實(shí)現(xiàn)這樣的功能

5.1. <if>

可以通過 if 標(biāo)簽來實(shí)現(xiàn)一下:

@Mapper
public interface UserInfoXmlMapper {
    Integer insertUserByCondition(UserInfo userInfo);
}

再來看 XML 中的 SQL 語句

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

if 標(biāo)簽中的參數(shù)和 java 對(duì)象中的屬性參數(shù)是對(duì)應(yīng)的

@Test
void insertUserByCondition() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("java");
    userInfo.setPassword("java");
    userInfo.setAge(19);
    //userInfo.setGender(1);
    Integer integer = userInfoXmlMapper.insertUserByCondition(userInfo);
}

如果不傳入性別的話來看一下結(jié)果:

由于性別沒有傳入,所以說 SQL 語句中是只有前三個(gè)參數(shù)的,所以第三個(gè)參數(shù)那里就多了一個(gè)逗號(hào),導(dǎo)致最終的 SQL 的語法錯(cuò)誤

那么就可以想一個(gè)辦法,如果把逗號(hào)直接加前面,是不是就可以解決了

這樣看似是可以解決的,但是如果說 username, age 都設(shè)為了非必填的,例如 username 沒有傳入?yún)?shù),但是 age 傳入了參數(shù),這樣前面就多了一個(gè)逗號(hào),這時(shí) SQL 語句就又會(huì)出錯(cuò)了,把逗號(hào)都加到右邊,也是會(huì)出現(xiàn)問題的

這時(shí)就需要用到下面的標(biāo)簽了

5.2. <trim>

主要用于去除 SQL 語句中多余的關(guān)鍵字或者字符,同時(shí)也可以添加自定義的前綴和后綴

?prefix:用于為包含在trim標(biāo)簽內(nèi)部的 SQL 語句塊添加一個(gè)前綴
?suffix:表示整個(gè)語句塊,以 suffix 的值作為后綴.
?prefixOverrides:為trim標(biāo)簽內(nèi)的 SQL 語句塊添加一個(gè)后綴.
?suffixOverrides:表示整個(gè)語句塊要去除掉的后綴.

<insert id="insertUserByCondition">
  insert into user_info
  <trim prefix="(" suffix=")" suffixOverrides=",">
    <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=")" suffixOverrides=",">
    <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>

這就表示在 SQL 語句前面加上一個(gè) '(' ,后面加上 ')' ,如果最后是以逗號(hào)結(jié)尾的就把逗號(hào)刪了,以此來實(shí)現(xiàn) SQL 語句拼接的效果

5.3. <where>

來看一下條件查詢

這里的 and 和上面的逗號(hào)是一樣的性質(zhì),放在右邊或者左邊都不合適,還是可以使用 trim 標(biāo)簽來解決

但是這時(shí)其實(shí)還有一個(gè)問題,如果說 age 和 deleteFlag 都沒有傳入的話,最后的 SQL 語句 where 后面就沒有了,這時(shí)又會(huì)報(bào)錯(cuò)了

這種情況 trim 就解決不了了,其中一種解決方式是在 where 后面加上 1=1,那么 and 就需要加在前面了:

比較推薦的寫法就是使用 <where> 標(biāo)簽

<select id="selectUserByCondition" resultType="com.example.mybatisdemo.model.UserInfo">
  select * from user_info
  <where>
    <if test="age!=null">
      and age=#{age}
    </if>
    <if test="deleteFlag!=null">
      and delete_flag = #{deleteFlag}
    </if>
  </where>
</select>

<where> 標(biāo)簽如果后面都沒有值的話,SQL 語句中的 where 也不會(huì)添加,并且如果只有一個(gè)值的話,前面的 and 也會(huì)被去掉,也不用 trim 標(biāo)簽了,不過去掉的是前面的 and,寫后面是不會(huì)去掉的

5.4. <set>

動(dòng)態(tài)更新操作也是,當(dāng)后面有值的時(shí)候就更新,沒有值的時(shí)候就不更新,<set> 標(biāo)簽的作用和 where 類似,也是后面有值的話就生成 set 關(guān)鍵字并且去除右邊的逗號(hào),但是后面設(shè)置的內(nèi)容也不能全部是空,此時(shí)就算沒有生成 set 標(biāo)簽,但是前面還有一個(gè) update 關(guān)鍵字,最后的 SQL 語句還是有問題

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

5.5. <foreach>

foreach 用于在 SQL 語句中遍歷集合,動(dòng)態(tài)地構(gòu)建包含多個(gè)參數(shù)的 SQL 語句,比如IN子句、批量插入語句等

  • collection:綁定方法參數(shù)中的集合,如 List,Set,Map 或數(shù)組對(duì)象。
  • item:遍歷時(shí)的每一個(gè)對(duì)象。
  • open:語句塊開頭的字符串。
  • close:語句塊結(jié)束的字符串。
  • separator:每次遍歷之間間隔的字符串。
<delete id="batchDelete">
  delete from user_info where id in
  <foreach collection="ids" separator="," item="id" open="(" close=")">
    #{id}
  </foreach>
</delete>

5.6. <include>

<include>標(biāo)簽主要用于代碼復(fù)用。它可以將一個(gè) SQL 片段(通常是在<sql>標(biāo)簽中定義的)包含到另一個(gè) SQL 語句中,使得 SQL 語句的編寫更加模塊化,減少重復(fù)代碼

例如上面的重復(fù)語句就可以提取出來

<sql id="insertCol">
  insert into user_info(username, password, age, gender)
</sql>

然后就可以通過 include 標(biāo)簽來引用了

6. 注解方式的動(dòng)態(tài) SQL

注解方式就是把原來 XML 中的 SQL 語句部分寫到注解的 <script> 標(biāo)簽下,可以看出,由于注解中是字符串拼接的方式,這種方法是非常容易出錯(cuò)的,而且排查錯(cuò)誤也是有些困難的

主頁 

到此這篇關(guān)于MyBatis中#{} 和 ${} 的區(qū)別和動(dòng)態(tài) SQL的文章就介紹到這了,更多相關(guān)MyBatis #{} 和 ${} 區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Security使用Lambda DSL配置流程詳解

    Spring Security使用Lambda DSL配置流程詳解

    Spring Security 5.2 對(duì) Lambda DSL 語法的增強(qiáng),允許使用lambda配置HttpSecurity、ServerHttpSecurity,重要提醒,之前的配置方法仍然有效。lambda的添加旨在提供更大的靈活性,但是用法是可選的。讓我們看一下HttpSecurity的lambda配置與以前的配置樣式相比
    2023-02-02
  • Spring 中使用反射創(chuàng)建 Bean 實(shí)例的幾種方式

    Spring 中使用反射創(chuàng)建 Bean 實(shí)例的幾種方式

    文章介紹了在Spring框架中如何使用反射來創(chuàng)建Bean實(shí)例,包括使用Class.newInstance()、Constructor.newInstance()、工廠方法以及Spring的BeanUtils工具類,文章還強(qiáng)調(diào)了反射操作的注意事項(xiàng),如異常處理、性能、安全性以及類型安全,感興趣的朋友一起看看吧
    2025-03-03
  • Spring Boot如何使用AOP實(shí)例解析

    Spring Boot如何使用AOP實(shí)例解析

    這篇文章主要介紹了Spring Boot如何使用AOP實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • SpringMvc向request域中設(shè)置數(shù)據(jù)的方法

    SpringMvc向request域中設(shè)置數(shù)據(jù)的方法

    這篇文章主要介紹了SpringMvc向request域中設(shè)置數(shù)據(jù)的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Spring Boot application.yml配置文件示例詳解

    Spring Boot application.yml配置文件示例詳解

    本文詳細(xì)介紹了SpringBootapplication.yml配置文件的使用和配置項(xiàng),通過學(xué)習(xí)本文,您應(yīng)該已經(jīng)掌握了如何使用application.yml文件來配置SpringBoot應(yīng)用程序的不同組件,如數(shù)據(jù)源、數(shù)據(jù)庫、緩存、郵件服務(wù)等,感興趣的朋友一起看看吧
    2025-02-02
  • Java實(shí)現(xiàn)簡單聊天機(jī)器人

    Java實(shí)現(xiàn)簡單聊天機(jī)器人

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡單聊天機(jī)器人,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 關(guān)于Java中常見的負(fù)載均衡算法

    關(guān)于Java中常見的負(fù)載均衡算法

    這篇文章主要介紹了關(guān)于Java中常見的負(fù)載均衡算法,負(fù)載平衡是一種電子計(jì)算機(jī)技術(shù),用來在多個(gè)計(jì)算機(jī)、網(wǎng)絡(luò)連接、CPU、磁盤驅(qū)動(dòng)器或其他資源中分配負(fù)載,以達(dá)到優(yōu)化資源使用、最大化吞吐率、最小化響應(yīng)時(shí)間、同時(shí)避免過載的目的,需要的朋友可以參考下
    2023-08-08
  • Java中漢字轉(zhuǎn)拼音pinyin4j用法實(shí)例分析

    Java中漢字轉(zhuǎn)拼音pinyin4j用法實(shí)例分析

    這篇文章主要介紹了Java中漢字轉(zhuǎn)拼音pinyin4j用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了pinyin4j庫的具體使用技巧,需要的朋友可以參考下
    2015-12-12
  • SpringBoot實(shí)現(xiàn)抽獎(jiǎng)算法的示例代碼

    SpringBoot實(shí)現(xiàn)抽獎(jiǎng)算法的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何通過SpringBoot實(shí)現(xiàn)抽獎(jiǎng)算法,文中的示例代碼簡潔易懂,具有一定的參考價(jià)值,感興趣的小伙伴可以了解一下
    2023-06-06
  • 解決引用slf4j中Logger.info只打印出文字沒有數(shù)據(jù)的問題

    解決引用slf4j中Logger.info只打印出文字沒有數(shù)據(jù)的問題

    這篇文章主要介紹了解決引用slf4j中Logger.info只打印出文字沒有數(shù)據(jù)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論