MyBatis 動態 SQL 最全教程,這樣寫 SQL 太優雅了!

来源:https://www.cnblogs.com/javastack/p/18159350
-Advertisement-
Play Games

一、MyBatis動態 sql 是什麼 動態 SQL 是 MyBatis 的強大特性之一。在 JDBC 或其它類似的框架中,開發人員通常需要手動拼接 SQL 語句。根據不同的條件拼接 SQL 語句是一件極其痛苦的工作。 例如,拼接時要確保添加了必要的空格,還要註意去掉列表最後一個列名的逗號。而動態 ...


一、MyBatis動態 sql 是什麼

動態 SQL 是 MyBatis 的強大特性之一。在 JDBC 或其它類似的框架中,開發人員通常需要手動拼接 SQL 語句。根據不同的條件拼接 SQL 語句是一件極其痛苦的工作。

例如,拼接時要確保添加了必要的空格,還要註意去掉列表最後一個列名的逗號。而動態 SQL 恰好解決了這一問題,可以根據場景動態的構建查詢。

動態SQL(code that is executed dynamically),它一般是根據用戶輸入或外部條件動態組合的SQL語句塊。 動態SQL能靈活的發揮SQL強大的功能、方便的解決一些其它方法難以解決的問題。 相信使用過動態SQL的人都能體會到它帶來的便利,然而動態SQL有時候在執行性能 (效率)上面不如靜態SQL,而且使用不恰當,往往會在安全方面存在隱患 (SQL 註入式攻擊)。

1.Mybatis 動態 sql 是做什麼的?

Mybatis 動態 sql 可以讓我們在 Xml 映射文件內,以標簽的形式編寫動態 sql,完成邏輯判斷和動態拼接 sql 的功能。

2.Mybatis 的 9 種 動 態 sql 標 簽有哪些?

3.動態 sql 的執行原理?

原理為:使用 OGNL 從 sql 參數對象中計算表達式的值,根據表達式的值動態拼接 sql,以此來完成動態 sql 的功能。

推薦一個開源免費的 Spring Boot 實戰項目:

https://github.com/javastacks/spring-boot-best-practice

二、MyBatis標簽

1.if標簽:條件判斷

MyBatis if 類似於 Java 中的 if 語句,是 MyBatis 中最常用的判斷語句。使用 if 標簽可以節省許多拼接 SQL 的工作,把精力集中在 XML 的維護上。

1)不使用動態sql

<select id="selectUserByUsernameAndSex"
        resultType="user" parameterType="com.ys.po.User">
    <!-- 這裡和普通的sql 查詢語句差不多,對於只有一個參數,後面的 #{id}表示占位符,裡面          不一定要寫id,
         寫啥都可以,但是不要空著,如果有多個參數則必須寫pojo類裡面的屬性 -->
    select * from user where username=#{username} and sex=#{sex}
</select>

if 語句使用方法簡單,常常與 test 屬性聯合使用。語法如下:

<if test="判斷條件">    SQL語句</if>

2)使用動態sql

上面的查詢語句,我們可以發現,如果 #{username} 為空,那麼查詢結果也是空,如何解決這個問題呢?使用 if 來判斷,可多個 if 語句同時使用。

以下語句表示為可以按照網站名稱(name)或者網址(url)進行模糊查詢。如果您不輸入名稱或網址,則返回所有的網站記錄。但是,如果你傳遞了任意一個參數,它就會返回與給定參數相匹配的記錄。

<select id="selectAllWebsite" resultMap="myResult">  
    select id,name,url from website 
    where 1=1    
   <if test="name != null">        
       AND name like #{name}   
   </if>    
   <if test="url!= null">        
       AND url like #{url}    
   </if>
</select>

2.where+if標簽

where、if同時使用可以進行查詢、模糊查詢

註意,<if>失敗後, <where> 關鍵字只會去掉庫表欄位賦值前面的and,不會去掉語句後面的and關鍵字,即註意,<where> 只會去掉<if> 語句中的最開始的and關鍵字。所以下麵的形式是不可取的

<select id="findQuery" resultType="Student">
    <include refid="selectvp"/>
    <where>
        <if test="sacc != null">
            sacc like concat('%' #{sacc} '%')
        </if>
        <if test="sname != null">
            AND sname like concat('%' #{sname} '%')
        </if>
        <if test="sex != null">
            AND sex=#{sex}
        </if>
        <if test="phone != null">
            AND phone=#{phone}
        </if>
    </where>
</select>

這個“where”標簽會知道如果它包含的標簽中有返回值的話,它就插入一個‘where’。此外,如果標簽返回的內容是以AND 或OR 開頭的,則它會剔除掉。

3.set標簽

set可以用來修改

<update id="upd">
    update student
    <set>
        <if test="sname != null">sname=#{sname},</if>
        <if test="spwd != null">spwd=#{spwd},</if>
        <if test="sex != null">sex=#{sex},</if>
        <if test="phone != null">phone=#{phone}</if>
    sid=#{sid}
    </set>
    where sid=#{sid}
</update>

4.choose(when,otherwise) 語句

有時候,我們不想用到所有的查詢條件,只想選擇其中的一個,查詢條件有一個滿足即可,使用 choose 標簽可以解決此類問題,類似於 Java 的 switch 語句

<select id="selectUserByChoose" resultType="com.ys.po.User" parameterType="com.ys.po.User">
      select * from user
      <where>
          <choose>
              <when test="id !='' and id != null">
                  id=#{id}
              </when>
              <when test="username !='' and username != null">
                  and username=#{username}
              </when>
              <otherwise>
                  and sex=#{sex}
              </otherwise>
          </choose>
      </where>
  </select>

也就是說,這裡我們有三個條件,id、username、sex,只能選擇一個作為查詢條件

  • 如果 id 不為空,那麼查詢語句為:select * from user where id=?
  • 如果 id 為空,那麼看username 是否為空,如果不為空,那麼語句為 select * from user where username=?;
  • 如果 username 為空,那麼查詢語句為 select * from user where sex=?

5.trim

trim標記是一個格式化的標記,可以完成set或者是where標記的功能

推薦一個開源免費的 Spring Boot 實戰項目:

https://github.com/javastacks/spring-boot-best-practice

①、用 trim 改寫上面第二點的 if+where 語句

<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User">
    select * from user
    <!-- <where>
        <if test="username != null">
           username=#{username}
        </if>
         
        <if test="username != null">
           and sex=#{sex}
        </if>
    </where>  -->
    <trim prefix="where" prefixOverrides="and | or">
        <if test="username != null">
           and username=#{username}
        </if>
        <if test="sex != null">
           and sex=#{sex}
        </if>
    </trim>
</select>
  • prefix:首碼
  • prefixoverride:去掉第一個and或者是or

②、用 trim 改寫上面第三點的 if+set 語句

<!-- 根據 id 更新 user 表的數據 -->
<update id="updateUserById" parameterType="com.ys.po.User">
    update user u
        <!-- <set>
            <if test="username != null and username != ''">
                u.username = #{username},
            </if>
            <if test="sex != null and sex != ''">
                u.sex = #{sex}
            </if>
        </set> -->
        <trim prefix="set" suffixOverrides=",">
            <if test="username != null and username != ''">
                u.username = #{username},
            </if>
            <if test="sex != null and sex != ''">
                u.sex = #{sex},
            </if>
        </trim>
     
     where id=#{id}
</update>
  • suffix:尾碼
  • suffixoverride:去掉最後一個逗號(也可以是其他的標記,就像是上面首碼中的and一樣)

③、trim+if同時使用可以添加

<insert id="add">
    insert  into student
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="sname != null">sname,</if>
        <if test="spwd != null">spwd,</if>
        <if test="sex != null">sex,</if>
        <if test="phone != null">phone,</if>
    </trim>

    <trim prefix="values (" suffix=")"  suffixOverrides=",">
        <if test="sname != null">#{sname},</if>
        <if test="spwd != null">#{spwd},</if>
        <if test="sex != null">#{sex},</if>
        <if test="phone != null">#{phone}</if>
    </trim>

</insert>

6.MyBatis foreach標簽

foreach是用來對集合的遍歷,這個和Java中的功能很類似。通常處理SQL中的in語句。

foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體內使用的集合項(item)和索引(index)變數。它也允許你指定開頭與結尾的字元串以及集合項迭代之間的分隔符。這個元素也不會錯誤地添加多餘的分隔符

你可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數組對象作為集合參數傳遞給 foreach。當使用可迭代對象或者數組時,index 是當前迭代的序號,item 的值是本次迭代獲取到的元素。當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。

//批量查詢
<select id="findAll" resultType="Student" parameterType="Integer">
    <include refid="selectvp"/> WHERE sid in
    <foreach item="ids" collection="array"  open="(" separator="," close=")">
        #{ids}
    </foreach>
</select>
//批量刪除
<delete id="del"  parameterType="Integer">
    delete  from  student  where  sid in
    <foreach item="ids" collection="array"  open="(" separator="," close=")">
        #{ids}
    </foreach>
</delete>
整合案例

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yzx.mapper.StuMapper">
    <sql id="selectvp">
        select  *  from  student
    </sql>
    
    <select id="find" resultType="Student">
        <include refid="selectvp"/>
    </select>

    <select id="findbyid"  resultType="student">
        <include refid="selectvp"/>
        WHERE 1=1
        <if test="sid != null">
            AND sid like #{sid}
        </if>
    </select>

        <select id="findQuery" resultType="Student">
            <include refid="selectvp"/>
            <where>
                <if test="sacc != null">
                    sacc like concat('%' #{sacc} '%')
                </if>
                <if test="sname != null">
                    AND sname like concat('%' #{sname} '%')
                </if>
                <if test="sex != null">
                    AND sex=#{sex}
                </if>
                <if test="phone != null">
                    AND phone=#{phone}
                </if>
            </where>
        </select>

    <update id="upd">
        update student
        <set>
            <if test="sname != null">sname=#{sname},</if>
            <if test="spwd != null">spwd=#{spwd},</if>
            <if test="sex != null">sex=#{sex},</if>
            <if test="phone != null">phone=#{phone}</if>
        sid=#{sid}
        </set>
        where sid=#{sid}
    </update>

    <insert id="add">
        insert  into student
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="sname != null">sname,</if>
            <if test="spwd != null">spwd,</if>
            <if test="sex != null">sex,</if>
            <if test="phone != null">phone,</if>
        </trim>

        <trim prefix="values (" suffix=")"  suffixOverrides=",">
            <if test="sname != null">#{sname},</if>
            <if test="spwd != null">#{spwd},</if>
            <if test="sex != null">#{sex},</if>
            <if test="phone != null">#{phone}</if>
        </trim>

    </insert>
    <select id="findAll" resultType="Student" parameterType="Integer">
        <include refid="selectvp"/> WHERE sid in
        <foreach item="ids" collection="array"  open="(" separator="," close=")">
            #{ids}
        </foreach>
    </select>

    <delete id="del"  parameterType="Integer">
        delete  from  student  where  sid in
        <foreach item="ids" collection="array"  open="(" separator="," close=")">
            #{ids}
        </foreach>
    </delete>



</mapper>

測試類:

package com.yzx.test;

import com.yzx.entity.Student;
import com.yzx.mapper.StuMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class StuTest {
    SqlSession sqlSession=null;
    InputStream is=null;

    @Before
    public   void  before() throws IOException {
        //1.讀取核心配置文件
        is= Resources.getResourceAsStream("sqlMapperConfig.xml");
        //2.拿到工廠構建類
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
        //3.拿到具體工廠
        SqlSessionFactory build=sqlSessionFactoryBuilder.build(is);
        //4.拿到session
        sqlSession = build.openSession();
    }

    @After
    public  void  after(){
        //7,提交事務
        sqlSession.commit();
        //8.關閉資源
        sqlSession.close();
        if(is!=null){
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        };
    }

    //查詢所有
    @Test
    public  void  find(){
        //5.獲取具體的mapper介面
        StuMapper mapper=sqlSession.getMapper(StuMapper.class);
        //6.調用執行
        List<Student> list=mapper.find();
        list.forEach(a-> System.out.println(a));
    }
    //查詢單個
    @Test
    public  void  findbyid(){

        StuMapper mapper=sqlSession.getMapper(StuMapper.class);
        List<Student> list=mapper.findbyid(2);
        list.forEach(a-> System.out.println(a));
    }
    //模糊查詢
    @Test
    public  void  findQuery(){

        StuMapper mapper=sqlSession.getMapper(StuMapper.class);

        Student  stu=new Student();
        stu.setSname("小");
        stu.setSex("男");
        List<Student> list=mapper.findQuery(stu);
        list.forEach(a-> System.out.println(a));
    }
    //修改
    @Test
    public  void  upd(){

        StuMapper mapper=sqlSession.getMapper(StuMapper.class);

        Student  stu=new Student();
        stu.setSid(3);
        stu.setSname("小若");
        stu.setSex("人妖");
        int i=mapper.upd(stu);
        System.out.println("修改了"+i+"條數據"+"  "+stu.toString());

    }
    //添加
    @Test
    public  void  add(){

        StuMapper mapper=sqlSession.getMapper(StuMapper.class);

        Student  stu=new Student();
        stu.setSname("小賀");
        stu.setSex("男");
        stu.setPhone("99999999");
        int i=mapper.add(stu);
        System.out.println("添加了"+i+"條數據"+"  "+stu.toString());

    }

    //批量操作
    @Test
    public  void  findAll(){

        StuMapper mapper=sqlSession.getMapper(StuMapper.class);
        Integer[] i={1,2,3,4};
        List<Student> list=mapper.findAll(i);
        list.forEach(a-> System.out.println(a));
    }
    //批量操作

    //批量刪除
    @Test
    public  void  del(){
        StuMapper mapper=sqlSession.getMapper(StuMapper.class);
        Integer[] i={1,2,3,4};
        int i1=mapper.del(i);
        System.out.println("刪除了"+i1+"條數據");
    }
}

7.sql

在實際開發中會遇到許多相同的SQL,比如根據某個條件篩選,這個篩選很多地方都能用到,我們可以將其抽取出來成為一個公用的部分,這樣修改也方便,一旦出現了錯誤,只需要改這一處便能處處生效了,此時就用到了<sql>這個標簽了。

當多種類型的查詢語句的查詢欄位或者查詢條件相同時,可以將其定義為常量,方便調用。為求<select>結構清晰也可將 sql 語句分解。

<sql id="selectvp">
    select  *  from  student
</sql>

8.include

這個標簽和<sql>是天仙配,是共生的,include用於引用sql標簽定義的常量。比如引用上面sql標簽定義的常量

refid這個屬性就是指定<sql>標簽中的id值(唯一標識)

<select id="findbyid"  resultType="student">
    <include refid="selectvp"/>
    WHERE 1=1
    <if test="sid != null">
        AND sid like #{sid}
    </if>
</select>

9.如何引用其他XML中的SQL片段

比如你在com.xxx.dao.xxMapper這個Mapper的XML中定義了一個SQL片段如下:

<sql id="Base_Column_List"> ID,MAJOR,BIRTHDAY,AGE,NAME,HOBBY</sql>

此時我在com.xxx.dao.PatinetMapper中的XML文件中需要引用,如下:

<include refid="com.xxx.dao.xxMapper.Base_Column_List"></include>

三、MyBatis關聯查詢

1.MyBatis一對多關聯查詢

<!--一對多-->
<resultMap id="myStudent1" type="student1">
    <id property="sid" column="sid"/>
    <result property="sname" column="sname"/>
    <result property="sex" column="sex"/>
    <result property="sage" column="sage"/>
    <collection property="list" ofType="teacher">
        <id property="tid" column="tid"/>
        <result property="tname" column="tname"/>
        <result property="tage" column="tage"/>
    </collection>
</resultMap>

<!--一對多-->
<select id="find1" resultMap="myStudent1">
    select  *  from  student1  s  left  join  teacher  t  on s.sid=t.sid
</select>

2.MyBatis多對一關聯查詢

<!--多對一-->
<resultMap id="myTeacher" type="teacher">
    <id property="tid" column="tid"/>
    <result property="tname" column="tname"/>
    <result property="tage" column="tage"/>
    <association property="student1" javaType="Student1">
        <id property="sid" column="sid"/>
        <result property="sname" column="sname"/>
        <result property="sex" column="sex"/>
        <result property="sage" column="sage"/>
    </association>
</resultMap>


<!--多對一-->
<select id="find2" resultMap="myTeacher">
select  *  from  teacher  t right join student1 s on  t.sid=s.sid
</select>

3.MyBatis多對多關聯查詢

<!--多對多 以誰為主表查詢的時候,主表約等於1的一方,另一方相當於多的一方-->
<select id="find3" resultMap="myStudent1">
    select  *  from  student1 s  left join relevance r on  s.sid=r.sid  left join teacher t on  r.tid=t.tid
</select>

版權聲明:本文為CSDN博主「變成禿頭怪」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/SGLP521/article/details/121509714

更多文章推薦:

1.Spring Boot 3.x 教程,太全了!

2.2,000+ 道 Java面試題及答案整理(2024最新版)

3.免費獲取 IDEA 激活碼的 7 種方式(2024最新版)

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C-07.InnoDB數據存儲結構 1.資料庫的存儲結構:頁 索引結構給我們提供了高效的索引方式,不過索引信息以及數據記錄都是保存在文件上的,確切說是存儲在頁結構中。另一方面,索引是在存儲引擎中實現的,MySQL伺服器上的存儲引擎負責對錶中數據的讀取和寫入工作。不同存儲引擎中存放的格式一般是不同的, ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 分享10款ER圖工具,詳細分析他們的功能特點、價格和適用場景,可以根據你的需求進行選擇。ER圖(Entity-Relationship Diagram)是資料庫設計中常用的一種模型,用於描述實體之間的關係。這種圖形化的表示方法旨在幫助人們理解和設計資料庫結構,它們在資料庫開發和設計中非常有用。 1 ...
  • 1. 索引 在資料庫中索引最核心的作用是:加速查找。 例如:在含有300w條數據的表中查詢,無索引需要700秒,而利用索引可能僅需1秒。 mysql> select * from big where password="81f98021-6927-433a-8f0d-0f5ac274f96e"; + ...
  • 1. 棧和局部變數操作 1.1 將常量壓入棧的指令 指令 功能描述 aconst_null 將null對象引用壓入棧 iconst_m1 將將int類型常量-1壓入棧 iconst_0 將int類型常量0壓入棧 iconst_1 將int類型常量1壓入棧 iconst_2 將int類型常量2壓入棧 ...
  • 當在 Spring Boot 應用程式中使用Spring Data JPA 進行資料庫操作時,配置Schema名稱是一種常見的做法。然而,在某些情況下,模式名稱需要是動態的,可能會在應用程式運行時發生變化。比如:需要做數據隔離的SaaS應用。 所以,這篇博文將幫助您解決了在 Spring Boot ...
  • 在keycloak中,我們在進行brower瀏覽器的表單認證時,一般在跳到本頁面時,URL上會有redirect_uri這種參數,用來告訴keycloak,在認證成功後的跳轉地址,你在表單認證控制器中,可以通過context.getHttpRequest().getUri().getQueryPar ...
  • 在Java開發中,我們經常需要獲取和處理時間,這需要使用到各種不同的方法。其中,使用SimpleDateFormat類來格式化時間是一種常見的方法。雖然這個類看上去功能比較簡單,但是如果使用不當,也可能會引發一些問題。 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...