1. 程式人生 > 實用技巧 >mybatis問題合集:#{}與${}區別、動態sql語句、快取機制

mybatis問題合集:#{}與${}區別、動態sql語句、快取機制

一、MyBatis 中#{}和${}區別

  #{} 是預編譯處理,像傳進來的資料會加個" "(#將傳入的資料都當成一個字串,會對自動傳入的資料加一個雙引號)

  ${}就是字串替換。直接替換掉佔位符。$方式一般用於傳入資料庫物件,例如傳入表名.

  使用 ${} 的話會導致 sql 注入。什麼是 SQL 注入呢?比如 select * from user where id = ${value}。value 應該是一個數值吧。然後如果對方傳過來的是 001 and name = tom。這樣不就相當於多加了一個條件嘛?把SQL語句直接寫進來了。如果是攻擊性的語句呢?001;drop table user,直接把表給刪了。

  所以為了防止 SQL 注入,能用 #{} 的不要去用 ${}

  如果非要用 ${} 的話,那要注意防止 SQL 注入問題,可以手動判定傳入的變數,進行過濾,一般 SQL 注入會輸入很長的一條 SQL 語句

二、動態sql語句

1、if

<select id="select" resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
 <if
test="name!= null"> AND name like #{title} </if> </select>

2、where:像上面的那種情況,如果where後面沒有條件,然後需要直接寫if判斷(開頭如果是 and / or 的話,會去除掉)

<select id="select" resultType="Blog">
  SELECT * FROM BLOG
  <where>
      <if test="title != null">
        AND title like #{title}
      
</if> <if test="name!= null"> AND name like #{title} </if> <where> </select>

3、choose(when、otherwise):choose 相當於 java 裡面的 switch 語句。otherwise(其他情況)

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

4、set:set 元素主要是用在更新操作的時候,如果包含的語句是以逗號結束的話將會把該逗號忽略,如果set包含的內容為空的話則會出錯。

<update id="dynamicSetTest" parameterType="Blog">  
    update t_blog  
    <set>  
        <if test="title != null">  
            title = #{title},  
        </if>  
        <if test="content != null">  
            content = #{content},  
        </if>  
        <if test="owner != null">  
            owner = #{owner}  
        </if>  
    </set>  
    where id = #{id}  
</update> 

5、foreach:主要用在構建in條件中

<select id="dynamicForeachTest" resultType="Blog">  
  select * from t_blog where id in  
  <foreach collection="list" index="index" item="item" open="(" separator="," close=")">  
    #{item}  
  </foreach>
</select>  

  open separator close 相當於是 in (?,?,?)

  如果是個map怎麼辦

<select id="dynamicForeach3Test" resultType="Blog">  
  select * from t_blog where title like "%"#{title}"%" and id in  
  <foreach collection="ids" index="index" item="item" open="(" separator="," close=")">  
    #{item}  
  </foreach>  
</select>  
// collection對應map的鍵,像這樣
List<Integer> ids = new ArrayList<Integer>();  
ids.add(1);
ids.add(2);
ids.add(3);
Map<String, Object> params = new HashMap<String, Object>();  
params.put("ids", ids);  

三、快取機制

  快取機制減輕資料庫壓力,提高資料庫效能。mybatis的快取分為兩級:一級快取、二級快取

1、一級快取:

  一級快取為 sqlsesson 快取,快取的資料只在 SqlSession 內有效。在操作資料庫的時候需要先建立 SqlSession 會話物件,在物件中有一個 HashMap 用於儲存快取資料,此 HashMap 是當前會話物件私有的,別的 SqlSession 會話物件無法訪問。

具體流程:

第一次執行 select 完畢會將查到的資料寫入 SqlSession 內的 HashMap 中快取起來

第二次執行 select 會從快取中查資料,如果 select 相同切傳引數一樣,那麼就能從快取中返回資料,不用去資料庫了,從而提高了效率

注意:

1、如果 SqlSession 執行了 DML 操作(insert、update、delete),並 commit 了,那麼 mybatis 就會清空當前 SqlSession 快取中的所有快取資料,這樣可以保證快取中的存的資料永遠和資料庫中一致,避免出現髒讀

2、當一個 SqlSession 結束後那麼他裡面的一級快取也就不存在了, mybatis 預設是開啟一級快取,不需要配置

3、 mybatis 的快取是基於 [namespace:sql語句:引數] 來進行快取的,意思就是, SqlSession 的 HashMap 儲存快取資料時,是使用 [namespace:sql:引數] 作為 key ,查詢返回的語句作為 value 儲存的

2、二級快取:

  二級快取是 mapper 級別的快取,也就是同一個 namespace 的 mappe.xml ,當多個 SqlSession 使用同一個 Mapper 操作資料庫的時候,得到的資料會快取在同一個二級快取區域

  二級快取預設是沒有開啟的。需要在 setting 全域性引數中配置開啟二級快取

  開啟二級快取步驟:

  1、conf.xml 配置全域性變數開啟二級快取

<settings>
    <setting name="cacheEnabled" value="true"/> // 預設是false:關閉二級快取
<settings>

  2、在userMapper.xml中配置

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
// 當前mapper下所有語句開啟二級快取

// 這裡配置了一個LRU快取,並每隔60秒重新整理,最大儲存512個物件,而且返回的物件是隻讀的

// 若想禁用當前select語句的二級快取,新增useCache="false"修改如下:
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">

具體流程:

  1.當一個 sqlseesion 執行了一次 select 後,在關閉此 session 的時候,會將查詢結果快取到二級快取

  2.當另一個 sqlsession 執行 select 時,首先會在他自己的一級快取中找,如果沒找到,就回去二級快取中找,找到了就返回,就不用去資料庫了,從而減少了資料庫壓力提高了效能。