MyBatis --- 動態SQL、緩存機制

来源:http://www.cnblogs.com/lwj-0923/archive/2017/09/07/7488091.html
-Advertisement-
Play Games

有的時候需要根據要查詢的參數動態的拼接SQL語句 常用標簽: - if:字元判斷 - choose【when...otherwise】:分支選擇 - trim【where,set】:字元串截取,其中where標簽封裝查詢條件,set標簽封裝修改條件 - foreach: if案例 1)在Employ ...


有的時候需要根據要查詢的參數動態的拼接SQL語句

常用標簽:

- if:字元判斷

- choose【when...otherwise】:分支選擇

- trim【where,set】:字元串截取,其中where標簽封裝查詢條件,set標簽封裝修改條件

- foreach:

 

if案例

1)在EmployeeMapper介面文件添加一個方法

public Student getStudent(Student student);

 2)如果要寫下列的SQL語句,只要是不為空,就作為查詢條件,如下所示,這樣寫實際上是有問題的,所以我們要寫成動態SQL語句:

<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee">
       select *from tbl_employee where id = #{id} and user_name = #{userName} and email = #{email} and gender = #{gender}
</select>

 3)用if標簽改寫為動態SQL,如下所示:

 test:判斷表達式(OGNL):OGNL參照PPT或者官方文檔。  test從參數中取值進行判斷  遇見特殊符號,應該去寫轉義字元:如<>分別為&lt,&gt
<select id="getStudent" resultType="com.neuedu.mybatis.entity.Student">
      SELECT *
      FROM student
       where
            <if test="id != null">
                  id=#{id}
            </if>
            <if test="name !=null and name!=''">
                  and name=#{name}
            </if>
            <if test="password !=null and password !=''">
                  and password=#{password}
            </if>
            <if test="email !=null and email !=''">
                  and email=#{email}
            </if>
</select>

4)測試代碼

@Test
public void TestgetStudent(){
      StudentMapper bean = ioc.getBean(StudentMapper.class);
      Student student = new Student(4,"jack", "111", "[email protected]");
      System.out.println(student);
      Student student2 = bean.getStudent(student);
      System.out.println(student2);
}

 #測試結果沒問題,

但是仔細來說,上面的sql語句是有問題的,當我們不給動態sql語句傳遞id值的時候,sql語句的拼裝就會有問題!【name前有一個and】

 

- where 標簽

解決辦法 1.給where後面加上1=1,以後的條件都可以使用and xxx了 2.可以使用 where 標簽來將所有的查詢條件包括在內    mybatis就會將where標簽中拼裝的sql,多出來的and或者or去掉!
<select id="getStudent" resultType="com.neuedu.mybatis.entity.Student">
      SELECT *
      FROM student
      <where>
            <if test="id != null">
                  id=#{id}
            </if>
            <if test="name !=null and name!=''">
                  and name=#{name}
            </if>
            <if test="password !=null and password !=''">
                  and password=#{password}
            </if>
            <if test="email !=null and email !=''">
                  and email=#{email}
            </if>
      </where>
</select>

3.需要註意:where標簽只會去掉第一個多出來的and或者or

   也就是說使用where標簽有時候還是不能解決問題的,那怎麼辦呢?我們這裡可以使用trim標簽

 

- trim標簽:可以自定義字元串的截取規則 

    後面多出的and或者or where標簽不能夠解決     prefix="":首碼:trim標簽體是整個字元串拼串後的結果。     prefix給拼串後的整個字元串加一個首碼     prefixOverrides="":首碼覆蓋:去掉整個字元串前面多餘的字元     suffix="":尾碼     suffix給拼串後的整個字元串加一個尾碼     suffixOverrides="":尾碼覆蓋:去掉整個字元串後面多餘的字元
<select id="getStudent" resultType="com.neuedu.mybatis.entity.Student">
      SELECT *
      FROM student
      <trim prefix="where" prefixOverrides="and">
            <if test="id != null">
                  id=#{id}
            </if>
            <if test="name !=null and name!=''">
                  and name=#{name}
            </if>
            <if test="password !=null and password !=''">
                  and password=#{password}
            </if>
            <if test="email !=null and email !=''">
                  and email=#{email}
            </if>
      </trim>
</select>

 

- choose標簽:分支選擇,類似於Java中的帶了break的switch...case

相當於確保了第一個case 符合之後,就跳出

案例演示:

1.在EmployeeMapper介面中添加一個方法

public List<Student> getStus(Student student);

 2.sql映射文件

<select id="getStus" resultType="com.neuedu.mybatis.entity.Student">
      select * from student
      <where>
            <choose>
                  <when test="id !=null">
                        id = #{id}
                  </when>
                  <when test="name !=null and name!=''">
                        name = #{name}
                  </when>
                  <when test="password !=null and password!=''">
                        password = #{password}
                  </when>
                  <when test="email !=null and email!=''">
                        email = #{email}
                  </when>
                  <otherwise>
                        1 = 1
                  </otherwise>
            </choose>
      </where>
</select>

 

- set標簽:字元串截取,可以寫在trim裡面

set元素會動態前置set關鍵字,同時也會消除無關的逗號

1)在EmployeeMapper中添加一個更新的方法

public void updateStu(Student student);

 2)在sql映射文件中,填寫相應的sql語句,如下所示【set標簽可以將欄位後面的逗號去掉】

<update id="updateStu">
      update student
      <set>
            <if test="name !=null and name!=''">
                  name=#{name},
            </if>
            <if test="password !=null and password !=''">
                  password=#{password},
            </if>
            <if test="email !=null and email !=''">
                  email=#{email}
            </if>
      </set>
            where id = #{id}
</update>

 3)測試類代碼為

@Test
public void TestUpdateStu(){
      StudentMapper bean = ioc.getBean(StudentMapper.class);
      bean.updateStu(new Student(4, "jackk", null, null));
}

 將set標簽用trim標簽代替

<update id="updateStu">
      update student
      <trim prefix="set" suffixOverrides=",">
            <if test="name !=null and name!=''">
                  name=#{name},
            </if>
            <if test="password !=null and password !=''">
                  password=#{password},
            </if>
            <if test="email !=null and email !=''">
                  email=#{email}
            </if>
      </trim>
            where id = #{id}
</update>

 

- foreach:遍歷元素

動態SQL的另一個常用的操作是需要對一個集合進行遍歷,通常在構建in條件語句的時候! foreach元素允許指定一個集合,聲明集合項和索引變數,並可以指定開閉匹配的字元串以及在迭代之間放置分隔符。   案例演示: 1.在EmployeeMapper介面中加入一個方法
public List<Student> getStuByIdForEach(@Param("ids")List<Integer> ids);

2.在MyBatis的sql映射文件中寫相應的代碼

<select id="getStuByIdForEach" resultType="com.neuedu.mybatis.entity.Student">
      select * from student
      where id
      in
      <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
      </foreach>
</select>

 3.測試類代碼

@Test
public void getStuByIdForEach(){
      StudentMapper bean = ioc.getBean(StudentMapper.class);
      List<Integer> list = Arrays.asList(16,17,18,19);
      List<Student> stuByIdForEachlist = bean.getStuByIdForEach(list);
      for (Student student : stuByIdForEachlist) {
            System.out.println(student);
      }
}

 foreach標簽還可以用於批量保存數據

1.在EmployeeMapper介面類中添加批量插入的方法

public void insertStus(@Param("stus")List<Student> student);

 2.在EmployeeMapper.xml的sql映射文件中添加響應的語句

foreach 中用 collection,collection中是從Mapper介面傳來的參數,separator是去掉中間符號

<insert id="insertStus">
      insert into student (name,password,email) values
      <foreach collection="stus" item="stu" separator=",">
            (#{stu.name},#{stu.password},#{stu.email})
      </foreach>
</insert>

 3.測試代碼

@Test
public void TestInsertStus(){
      StudentMapper bean = ioc.getBean(StudentMapper.class);
      List<Student> list = new ArrayList<Student>();
      list.add(new Student("123","123", "123"));
      list.add(new Student("123","123", "123"));
      list.add(new Student("123","123", "123"));
      bean.insertStus(list);
}

 

MyBatis-緩存機制   

MyBatis 包含一個非常強大的查詢緩存特性,它可以非常方便地配置和定製。緩存可以極大的提升查詢效率。 只在MyBatis中,在SSM整合文件中沒用,因為SqlSession 定義在 bean.xml中,無法重新定義SqlSession   MyBatis系統中預設定義了兩級緩存。 一級緩存和二級緩存      一級緩存:(本地緩存):SqlSession級別的緩存,一級緩存是一直開啟的,沒法關閉。方法之間不共用!             與資料庫同一次會話期間查詢到的數據放在本地緩存中。             以後如果需要獲取相同的數據,直接從緩存中拿,沒必要再去查詢資料庫;      二級緩存(全局緩存):           –1、預設情況下,只有一級緩存(SqlSession級別的緩存,也稱為本地緩存)開啟。         –2、二級緩存需要手動開啟和配置,他是基於namespace級別的緩存。         –3、為了提高擴展性。MyBatis定義了緩存介面Cache。我們可以通過實現Cache介面來自定義二級緩存。  

一級緩存:

案例:測試一級緩存【預設是開啟的】

將返回一條select查詢語句, 將返回true,說明emp與emp2是緩存,而不是重新查找
@Test
public void TestFirstCache(){
      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
      session = sqlSessionFactory.openSession();
      mapper = session.getMapper(EmployeeMapper.class);

      Employee emp = mapper.getEmpInfoById(4);
      System.out.println(emp);

      Employee emp2 = mapper.getEmpInfoById(4);
      System.out.println(emp2);

      System.out.println(emp == emp2);

      session.commit();
      session.close();
}

 

 一級緩存失效的情況【4種】(沒有使用到當前一級緩存的情況,效果就是,還需要再向資料庫發出查詢)

1.sqlSession不同,重新定義SqlSession

將返回兩條select語句

將返回false,說明emp2不是emp的緩存

@Test
public void TestFirstCache(){
      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
      session = sqlSessionFactory.openSession();
      mapper = session.getMapper(EmployeeMapper.class);
      Employee emp = mapper.getEmpInfoById(4);
      System.out.println(emp);

      SqlSession session2 = sqlSessionFactory.openSession();
      EmployeeMapper mapper2 = session2.getMapper(EmployeeMapper.class);
      Employee emp2 = mapper2.getEmpInfoById(4);
      System.out.println(emp2);

      System.out.println(emp == emp2);

      session.commit();
      session.close();
}

 

2.SqlSession相同,但是查詢條件不一樣[當前緩存中還沒有這個數據]

就是相當於根據不同條件再次查找

@Test
public void TestFirstCache(){
      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
      session = sqlSessionFactory.openSession();
      mapper = session.getMapper(EmployeeMapper.class);

      Employee emp = mapper.getEmpInfoById(4);
      System.out.println(emp);

      Employee emp2 = mapper.getEmpInfoById(16);
      System.out.println(emp2);

      System.out.println(emp == emp2);

      session.commit();
      session.close();
}

 

3.SqlSession相同,但是兩次查詢之間執行了增刪改操作【這次增刪改可能對當前數據有影響】

因為預設自動刷新了緩存

@Test
public void TestFirstCache(){
      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
      session = sqlSessionFactory.openSession();
      mapper = session.getMapper(EmployeeMapper.class);

      Employee emp = mapper.getEmpInfoById(4);
      System.out.println(emp);

      mapper.deleteEmp(16);

      Employee emp2 = mapper.getEmpInfoById(4);
      System.out.println(emp2);

      System.out.println(emp == emp2);

      session.commit();
      session.close();
}

 

4.SqlSession相同,手動清除了一級緩存[緩存清空]

手動清除了緩存,所以得重新查找

@Test
public void TestFirstCache(){
      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
      session = sqlSessionFactory.openSession();
      mapper = session.getMapper(EmployeeMapper.class);

      Employee emp = mapper.getEmpInfoById(4);
      System.out.println(emp);

      session.clearCache();

      Employee emp2 = mapper.getEmpInfoById(4);
      System.out.println(emp2);

      System.out.println(emp == emp2);

      session.commit();
      session.close();
}

 

二級緩存:

【全局緩存】:基於namespace級別的緩存:一個namespace對應一個二級緩存。 【一級緩存的範圍還是太小了,每次SqlSession一關閉,一級緩存中的數據就消失】 所以從這個角度講:能跨sqlSession的緩存為二級緩存!   工作機制: 1.一個會話,查詢一條數據,這個數據就會被放在當前會話的一級緩存中。 2.如果會話關閉,一級緩存中的數據會被保存到二級緩存中;新的會話查詢信息,就可以參照二級緩存中。 不同namespace查出的數據會放在自己對應的緩存中(map)    效果:數據會從二級緩存中獲取           查出的數據都會被預設先放在一級緩存中。           只有會話提交或者關閉之後,一級緩存中的數據才會轉移到二級緩存中。           需要註意的是:在哪個Mapper.xml文件中開啟了<cache>緩存標簽,哪個Mapper中就開啟了二級緩存。

 

案例:

1)開啟全局二級緩存配置:

<setting name="cacheEnabled" value="true"/>

 2)去mapper.xml中配置使用二級緩存

<cache eviction="FIFO" size="100" readOnly="false"/>

 

其中屬性: eviction=“FIFO”:緩存回收策略: LRU –最近最少使用的:移除最長時間不被使用的對象。 FIFO –先進先出:按對象進入緩存的順序來移除它們。 SOFT –軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。 WEAK –弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。 預設的是LRU。 flushInterval:緩存刷新間隔 ?緩存多長時間清空一次,預設不清空,設置一個毫秒值。 size:引用數目,正整數,預設1024       代表緩存最多可以存儲多少個對象,太大容易導致記憶體溢出 readOnly:是否只讀,true/false       true:只讀緩存;mybatis認為所有從緩存中獲取數據的操作都是只讀操作,不會修改數據。               mybatis為了加快獲取速度,直接就會將數據在緩存中的引用交給用戶。不安全,速度快。       false:非只讀:mybatis覺得獲取的數據可能會被修改。               mybatis會利用序列化&反序列化的技術克隆一份。安全,速度慢。 type:指定自定義緩存的全類名 實現cache介面即可!

 

3)我們的POJO需要實現序列化介面[implements Serializable]

4)必須先關閉之前的sqlsession對象

測試:

可以看到只發送了一次SQL語句,第二次查詢時從二級緩存中拿到的數據,並沒有發送新的sql語句。

@Test
public void TestFirstCache(){
      SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
      session = sqlSessionFactory.openSession();
      mapper = session.getMapper(EmployeeMapper.class);
      Employee emp = mapper.getEmpInfoById(4);
      System.out.println(emp);
      session.close();

      SqlSession session2 = sqlSessionFactory.openSession();
      EmployeeMapper mapper2 = session2.getMapper(EmployeeMapper.class);
      Employee emp2 = mapper2.getEmpInfoById(4);
      System.out.println(emp2);
      session2.close();
}

需要註意的是:只有一級緩存中關閉的情況下,二級緩存才會被使用。

需要註意的是:在哪個Mapper.xml文件中開啟了<cache>緩存標簽,哪個Mapper中就開啟了二級緩存。

和緩存有關的設置/屬性: 1)cacheEnabled="true": false:關閉緩存(二級緩存關閉)【一級緩存一直可用】 2)每個select標簽都有useCache="true";                   false:不使用緩存(一級緩存依然使用,二級緩存不使用) 3)每個增刪改標簽都有一個flushCache="true":增刪改執行完成後就會清楚緩存【一級二級緩存都會被清空】            查詢標簽:flushCache = "false"            如果flushCache = true;每次查詢之前都會清空緩存,緩存是沒有被使用!    
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Gordon L. Hempton是西雅圖的一位黑客和設計師,他花費了幾個月的時間研究和比較了12種流行的JavaScript MVC框架,併在博客中總結了每種框架的優缺點,最終的結果是,Ember.js勝出。 此次比較針對的特性標準有四種,分別是: UI綁定(UI Bindings) 複合視圖(C ...
  • 哎呀,看了那麼多博客,搬運了那麼多代碼,第一次自己寫博客,我發現即使會使用某技術了,但是要表達、轉述出來還是不簡單的。希望我能堅持下去哈。 為什麼使用Zxing?Google 名氣大啊,其他我也不瞭解啊。 上手還是很容易的,引入jar 包後十幾代碼的事。 1、在pom.xml 引入依賴 2、後臺co ...
  • 概述 Java集合框架由Java類庫的一系列介面、抽象類以及具體實現類組成。我們這裡所說的集合就是把一組對象組織到一起,然後再根據不同的需求操縱這些數據。集合類型就是容納這些對象的一個容器。也就是說,最基本的集合特性就是把一組對象放一起集中管理。根據集合中是否允許有重覆的對象、對象組織在一起是否按某 ...
  • 1 // MARK: 1.斷言 assert,參數如果為ture則繼續,否則拋出異常 2 let number = 3 3 4 //第一個參數為判斷條件,第二各參數為條件不滿足時的列印信息 5 assert(number >= 3,"number 不大於 3") 6 7 //如果斷言被處罰(numb... ...
  • 團隊更換新框架。新的業務全部使用新的框架,甚至是新的資料庫 Mysql。 這邊之前一直是使用oracle,各種訂單號、流水號、批次號啥的,都是直接使用oracle的sequence提供的數字序列號。現在資料庫更換成Mysql了,顯然以前的老方法不能適用了。 需要新寫一個: 分散式場景使用 滿足一定的 ...
  • 例1:y'=ry(1-y/K) y(0)=2 對應的R代碼為: library(deSolve)#parameters and initial valuesr<-1K<-10yini<-2#the function derivs<-function(t,y,parms){ return(list(r ...
  • package test; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Timer;... ...
  • 怎樣學習才能從一名Java初級程式員成長為一名合格的架構師,或者說一名合格的架構師應該有怎樣的技術知識體系,這是不僅一個剛剛踏入職場的初級程式員也是工作三五年之後開始迷茫的老程式員經常會問到的問題。希望這篇文章會是你看到過的最全面最權威的回答。 一: 編程基礎 不管是C還是C++,不管是Java還是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...