1. 程式人生 > 資料庫 >MyBatis-06-動態SQL

MyBatis-06-動態SQL

十、動態SQL

  • 什麼是動態SQL?
    • 根據給出引數的不同,通過拼接等操作,生成不同的SQL語句

10.1 構造實驗環境

  • 資料庫設計

    CREATE TABLE `blog` (
      `id` varchar(50) NOT NULL COMMENT '部落格id',
      `title` varchar(100) NOT NULL COMMENT '部落格標題',
      `author` varchar(30) NOT NULL COMMENT '部落格作者',
      `create_time` datetime NOT NULL COMMENT '建立時間',
      `views` int NOT NULL COMMENT '瀏覽量'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
  • mybatis-config.xml

    <settings>
        <!--開啟日誌以及自動轉換駝峰命名-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
  • 實體類

    package com.pbx.pojo;
    
    import java.util.Date;
    
    /**
     * @author BruceXu
     * @date 2020/11/8
     */
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;
        private int views;
    }
    
  • id工具類

    package com.pbx.utils;
    
    import java.util.UUID;
    
    /**
     * @author BruceXu
     * @date 2020/11/8
     */
    public class IDUtils {
        public static String getId() {
            return UUID.randomUUID().toString().replaceAll("-","");
        }
    }
    
  • BlogMapper介面

    package com.pbx.mapper;
    
    import com.pbx.pojo.Blog;
    import org.apache.ibatis.annotations.Insert;
    
    /**
     * @author BruceXu
     * @date 2020/11/8
     */
    public interface BlogMapper {
        @Insert("insert into blog (id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views});")
        int addBlog(Blog blog);
    }
    
    
  • 插入資料

    @Test
    public void addBlog() {
        SqlSession session = MyBatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(IDUtils.getId());
        blog.setTitle("Mybatis如此簡單");
        blog.setAuthor("BruceXu");
        blog.setCreateTime(new Date());
        blog.setViews(9999);
    
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("Java如此簡單");
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("Spring如此簡單");
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("微服務如此簡單");
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("狂神說Java真的好");
        mapper.addBlog(blog);
        session.commit();
        session.close();
    }
    

10.2 if

  • 這個if和高階語言裡面的判斷語句if是一樣的功能

  • 需求

    • 根據author和title來查詢部落格,如果一個為空,則就按照另外一個進行查詢
  • BlogMapper介面

    List<Blog> getBlog(Map map);
    
  • BlogMapper.xml

    <mapper namespace="com.pbx.mapper.BlogMapper">
        <select id="getBlog" parameterType="map" resultType="Blog">
            select * from blog where 1=1
            <if test="title != null">
                and title = #{title}
            </if>
            <if test="author != null">
                and author = #{author}
            </if>
        </select>
    </mapper>
    
  • 測試

    @Test
    public void getBlog() {
        SqlSession session = MyBatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, String> map = new HashMap<>();
    //        map.put("title", "Mybatis如此簡單");
    //        map.put("author", "BruceXu");
    
        List<Blog> blogList = mapper.getBlog(map);
        for (Blog blog : blogList) {
            System.out.println(blog);
        }
        session.close();
    }
    

    image-20201108230751709

    image-20201108231000629

    image-20201108231053256

  • 如果說if中的條件有滿足的話,則拼接上SQL語句如果沒有滿足的話,則不進行拼接

10.3 choose (when, otherwise)

  • choose的作用類似於高階語言中的switch~case語句塊

  • 需求:根據author或title來查詢部落格,有誰查誰,如果都沒有就按照views查詢

  • BlogMapper介面

    List<Blog> getBlog2(Map map);
    
  • BlogMapper.xml

    <mapper>
        <select id="getBlog2" parameterType="map" resultType="Blog">
            select  * from blog where 1=1
            <choose>
                <when test="title != null">
                    and title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = 9999
                </otherwise>
            </choose>
        </select>
    </mapper>
    
  • 測試

    @Test
    public void getBlog2() {
        SqlSession session = MyBatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, String> map = new HashMap<>();
    
    //        map.put("title", "Java如此簡單");
    //        map.put("author", "BruceXu");
        List<Blog> blogList = mapper.getBlog2(map);
        for (Blog blog : blogList) {
            System.out.println(blog);
        }
        session.close();
    }
    

    image-20201108234816404

    image-20201108235014385

    image-20201108235101881

  • 可以看到,如果when中的條件沒有一個匹配上,那麼拼接上otherwise中的內容。如果when中有多個匹配,那麼則只拼接第一個匹配的

10.4 trim (where, set)

  • 如果仔細看上面兩個測試環境中使用的SQL語句,都會發現存在這麼一點 where 1=1,這個是為了保證在進行拼接的時候不出現這樣的錯誤:select * from blog where and author = ...

  • 那麼有沒有這樣的一種方法,能夠自動判斷是不是要加上and或者空格等符號以保證SQL語句的正確性?

  • trim標籤,當然,MyBatis也為我們提前封裝好了where和set標籤

    <trim prefix="字首匹配" prefixOverrides="字首替換" suffix="字尾匹配" suffixOverrides="字尾替換">...</trim>
    
  • where和set示例

    • BlogMapper介面
    List<Blog> getBlog3(Map map);
    int updateBlog(Map map);
    
    • BlohMapper.xml
    <mapper>
        <select id="getBlog3" parameterType="map" resultType="Blog">
            select * from blog
            <where>
                <if test="title != null">
                    and title = #{title}
                </if>
                <if test="author != null">
                    and author = #{author}
                </if>
            </where>
        </select>
        
        <update id="updateBlog" parameterType="map">
            update blog
            <set>
                <if test="title != null">
                    title = #{title},
                </if>
                <if test="author != null">
                    author = #{author},
                </if>
                <if test="view != null">
                    views = #{view},
                </if>
            </set>
        </update>
    </mapper>
    
    • 測試
    @Test
    public void getBlog3() {
        SqlSession session = MyBatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, String> map = new HashMap<>();
    //        map.put("title", "Mybatis如此簡單");
        map.put("author", "BruceXu");
        List<Blog> blog3 = mapper.getBlog3(map);
        for (Blog blog : blog3) {
            System.out.println(blog);
        }
        session.close();
    }
    
    @Test
    public void updateBlog() {
        SqlSession session = MyBatisUtils.getSqlSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, String> map = new HashMap<>();
        map.put("view", "6666");
        map.put("id", "ccfb770fc1b64dbf8fd7379a564dcd9f");
        map.put("title", "狂神Java說的是真的好");
        mapper.updateBlog(map);
    
        List<Blog> blog3 = mapper.getBlog3(map);
        for (Blog blog : blog3) {
            System.out.println(blog);
        }
        session.commit();
        session.close();
    }
    

    image-20201109005119316

    • 可以看到,MyBatis都幫助我們成功的完成了SQL拼接,並沒有出現任何錯誤

10.5 SQL片段

  • 有些時候,在業務的處理過程中,可能某一部分SQL語句使用比較頻繁,所以我們可以將其抽取出來,單獨成為一個片段。然後在需要的使用通過 <include>標籤匯入即可

  • 提取SQL片段

    <sql id="if-title-author">
    	<if test="title != null">
    		title = #{title}
    	</if>
    	<if test="author != null">
    		and author = #{author}
    	</if>
    </sql>
    
  • 引用SQL片段

    <select id="queryBlogIf" parameterType="map" resultType="blog">
    	select * from blog
    	<where>
    		<!-- 引用 sql 片段,如果refid 指定的不在本檔案中,那麼需要在前面加上 namespace-->
    		<include refid="if-title-author" />
    		<!-- 在這裡還可以引用其他的 sql 片段 -->
    	</where>
    </select>
    

10.6 foreach

  • 動態 SQL 的另一個常見使用場景是對集合進行遍歷(尤其是在構建 IN 條件語句的時候)。比如:
<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
  • foreach 元素的功能非常強大,

    • 它允許你指定一個集合,宣告可以在元素體內使用的集合項(item)和索引(index)變數。
    • 它也允許你指定開頭與結尾的字串以及集合項迭代之間的分隔符。這個元素也不會錯誤地新增多餘的分隔符,
  • 提示

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