1. 程式人生 > >五、動態SQL

五、動態SQL

html 拼接 後綴 col include 對象 pack erb 比較

前面幾篇博客我們通過實例講解了用mybatis對一張表進行的CRUD操作,但是我們發現寫的 SQL 語句都比較簡單,如果有比較復雜的業務,我們需要寫復雜的 SQL 語句,往往需要拼接,而拼接 SQL ,稍微不註意,由於引號,空格等缺失可能都會導致錯誤。

  那麽怎麽去解決這個問題呢?這就是本篇所講的使用 mybatis 動態SQL,通過 if, choose, when, otherwise, trim, where, set, foreach等標簽,可組合成非常靈活的SQL語句,從而在提高 SQL 語句的準確性的同時,也大大提高了開發人員的效率。

  我們以 User 表為例來說明:

  技術分享圖片

回到頂部

1、動態SQL:if 語句

  根據 username 和 sex 來查詢數據。如果username為空,那麽將只根據sex來查詢;反之只根據username來查詢

  首先不使用 動態SQL 來書寫

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

  

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

1 2 3 4 5 6 7 8 9 10 <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> </select>

  這樣寫我們可以看到,如果 sex 等於 null,那麽查詢語句為 select * from user where username=#{username},但是如果usename 為空呢?那麽查詢語句為 select * from user where and sex=#{sex},這是錯誤的 SQL 語句,如何解決呢?請看下面的 where 語句

回到頂部

2、動態SQL:if+where 語句

1 2 3 4 5 6 7 8 9 10 11 12 <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> </select>

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

  

回到頂部

3、動態SQL:if+set 語句

  同理,上面的對於查詢 SQL 語句包含 where 關鍵字,如果在進行更新操作的時候,含有 set 關鍵詞,我們怎麽處理呢?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!-- 根據 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> where id=#{id} </update>

  這樣寫,如果第一個條件 username 為空,那麽 sql 語句為:update user u set u.sex=? where id=?

      如果第一個條件不為空,那麽 sql 語句為:update user u set u.username = ? ,u.sex = ? where id=?

回到頂部

4、動態SQL:choose(when,otherwise) 語句

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <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、動態SQL:trim 語句

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <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 語句

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <!-- 根據 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一樣)

回到頂部

6、動態SQL: SQL 片段

  有時候可能某個 sql 語句我們用的特別多,為了增加代碼的重用性,簡化代碼,我們需要將這些代碼抽取出來,然後使用時直接調用。

  比如:假如我們需要經常根據用戶名和性別來進行聯合查詢,那麽我們就把這個代碼抽取出來,如下:

1 2 3 4 5 6 7 8 9 <!-- 定義 sql 片段 --> <sql id="selectUserByUserNameAndSexSQL"> <if test="username != null and username != ‘‘"> AND username = #{username} </if> <if test="sex != null and sex != ‘‘"> AND sex = #{sex} </if> </sql>

  引用 sql 片段

1 2 3 4 5 6 7 8 <select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User"> select * from user <trim prefix="where" prefixOverrides="and | or"> <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那麽需要在前面加上 namespace --> <include refid="selectUserByUserNameAndSexSQL"></include> <!-- 在這裏還可以引用其他的 sql 片段 --> </trim> </select>

  註意:①、最好基於 單表來定義 sql 片段,提高片段的可重用性

     ②、在 sql 片段中不要包括 where

    

回到頂部

7、動態SQL: foreach 語句

  需求:我們需要查詢 user 表中 id 分別為1,2,3的用戶

  sql語句:select * from user where id=1 or id=2 or id=3

       select * from user where id in (1,2,3)

①、建立一個 UserVo 類,裏面封裝一個 List<Integer> ids 的屬性

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.ys.vo; import java.util.List; public class UserVo { //封裝多個用戶的id private List<Integer> ids; public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } }  

②、我們用 foreach 來改寫 select * from user where id=1 or id=2 or id=3

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User"> select * from user <where> <!-- collection:指定輸入對象中的集合屬性 item:每次遍歷生成的對象 open:開始遍歷時的拼接字符串 close:結束時拼接的字符串 separator:遍歷對象之間需要拼接的字符串 select * from user where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>

  測試:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //根據id集合查詢user表數據 @Test public void testSelectUserByListId(){ String statement = "com.ys.po.userMapper.selectUserByListId"; UserVo uv = new UserVo(); List<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(2); ids.add(3); uv.setIds(ids); List<User> listUser = session.selectList(statement, uv); for(User u : listUser){ System.out.println(u); } session.close(); }

  

③、我們用 foreach 來改寫 select * from user where id in (1,2,3)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User"> select * from user <where> <!-- collection:指定輸入對象中的集合屬性 item:每次遍歷生成的對象 open:開始遍歷時的拼接字符串 close:結束時拼接的字符串 separator:遍歷對象之間需要拼接的字符串 select * from user where 1=1 and id in (1,2,3) --> <foreach collection="ids" item="id" open="and id in (" close=") " separator=","> #{id} </foreach> </where> </select>

  

回到頂部

8、總結

  其實動態 sql 語句的編寫往往就是一個拼接的問題,為了保證拼接準確,我們最好首先要寫原生的 sql 語句出來,然後在通過 mybatis 動態sql 對照著改,防止出錯。

五、動態SQL