MyBatis學習筆記11、動態 SQL

動態 SQL 就是根據不同的條件生成不同的 SQL 語句。

和 JSTL 差不多,也是用各種標籤

11.1、準備工作

建數據表

create table `blog` (
    `id` int(11) auto_increment comment '博客id',
    `title` varchar(100) not null comment '博客標題',
    `author` varchar(30) not null comment '博客作者',
    `create_time` datetime not null comment '創建時間',
    `views` int(11) not null comment '瀏覽次數',
    primary key (`id`)
) engine=InnoDB default charset=utf8

再學一點:

UUID 是通用唯一識別碼(Universally Unique Identifier)。

其目的,是讓分佈式系統中的所有元素,都能有唯一的辨識信息,而不需要通過中央控制端來做辨識信息的指定。

UUID是指在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。通常平臺會提供生成的API。

UUID是有128個比特,發生碰撞的概率比一個人每年都被隕石擊中的機率還低。

格式:00000000-0000-0000-0000-000000000000

java.util.UUID 類可以生成UUID

比如我們創建一個生成 ID 的類 IDUtils

public class IDUtils {
    public static String getID() {
        return UUID.randomUUID().toString().replace("-","");
    }

    @Test
    public void testGetID(){
        System.out.println(IDUtils.getID());
    }
}

運行測試部分,可以得到如下id

74fa7ee0c0a9413191d331e810e85f3d

新建 pojo 類 Blog

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

可以看出其中的 createTime 和數據表中的列名不一致,數據庫中是下劃線命名,而java是駝峯

mybatis有一個自動轉換的功能:mapUnderscoreToCamelCase

在 mybatis-comfig.xml 中設置爲 true 後下劃線和駝峯就能自動映射

<settings>
	<setting name="logImpl" value="STDOUT_LOGGING"/>
	<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

BlogMapper 接口:

public interface BlogMapper {
    int addBlog(Blog blog);
}

BlogMapper.xml :

<mapper namespace="com.dzy.dao.BlogMapper">
    <insert id="addBlog" parameterType="Blog">
        insert into blog (id, title, author, create_time, views)
        values (#{id}, #{title}, #{author}, #{createTime}, #{views})
    </insert>
</mapper>

測試:

public class TestBolgMapper {
    @Test
    public void TestAddBlog() {
        try(SqlSession session = MybatisUtils.getSqlSession()){
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            Blog blog = new Blog(IDUtils.getID(), "《mybatis》", "dzy", new Date(), 9999);
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);

            blog.setTitle("《Spring》");
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);

            blog.setTitle("《SpringMVC》");
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);

            blog.setTitle("《SpringBoot》");
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);
            // 因爲創建sqlSession時寫了參數true
            // 是自動提交,所以沒有session.commit()
        }
    }
}

插入成功,現在表中有四條記錄。

11.2、if where標籤

如果有對應的篩選,就篩選,沒有就不做。

接口:

List<Blog> queryBlogIF(Map<String, Object> map);

mapper.xml

<select id="queryBlogIF" 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>

測試1:map中什麼都沒有

@Test
public void testQueryBlogIF() {
	try (SqlSession session = MybatisUtils.getSqlSession()) {
		BlogMapper mapper = session.getMapper(BlogMapper.class);
		Map<String, Object> map = new HashMap<>();
		List<Blog> blogList = mapper.queryBlogIF(map);
		for (Blog b : blogList) {
			System.out.println(b);
		}
	}
}

輸出所有行

測試2:在map中加入查詢範圍

map.put("title", "《mybatis》");
map.put("author", "dzy");

只查出標題是《mybatis》,作者是dzy的一條記錄。

但是在 mapper.xml 中,有一句 1=1。這個很不好看,但是如果沒有而且第一個 if 不成立,會出現 when and 的現象,sql 就不對了,而when 1=1 and xxx是對的。

where 標籤解決了這個問題,where 會忽略掉 when 後面,if 標籤開頭的 and 或 or。我們就可以寫出正常的 sql

<select id="queryBlogIF" resultType="Blog">
    select * from blog
    <where>
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
    </where>
</select>

這樣,這個 sql 看起來就正常多了

11.3、choose when otherwise

向 switch-case-default 一樣,多箇中選一個執行

先寫接口:

List<Blog> queryBlogChooes(Map<String, Object> map);

再寫mapper:

<select id="queryBlogChooes" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and views > #{views}
            </otherwise>
        </choose>
    </where>
</select>

再寫測試:

public void TestQueryBlogChoose() {
	try(SqlSession session = MybatisUtils.getSqlSession()) {
    	BlogMapper mapper = session.getMapper(BlogMapper.class);
    	Map<String, Object> map = new HashMap<>();
    	// map.put("title", "《mybatis》");
		// map.put("author", "dzy");
  	  	// map.put("views", 1000);
        List<Blog> blogList = mapper.queryBlogChooes(map);
    	for (Blog b : blogList) {
    		System.out.println(b);
    	}
    }
}

和 if 不同,這種方式執行且只執行一種情況,如果都不符合,執行 otherwise,如果map裏什麼都沒有:

Opening JDBC Connection
Created connection 855700733.
==>  Preparing: select * from blog WHERE views > ? 
==> Parameters: null
<==      Total: 0
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3300f4fd]
Returned connection 855700733 to pool.

11.4、trim(where set)

where 和 set 是 trim 類型的標籤,where 解決的是 when 後面有沒有 and 的問題, set 解決的是 set 後面有沒有逗號的問題。

比如寫個update

int updateBlog(Map<String,Object> map);
<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>
@Test
public void TestUpdateBlog() {
    try (SqlSession session = MybatisUtils.getSqlSession()) {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("title", "《MyBatis》");
        map.put("author", "dzydzy7");
        map.put("id", "183e05095a9643bf916bfa5e9a773664");
        mapper.updateBlog(map);
        // 自動提交
    }
}

11.5 foreach

用於對集合進行遍歷,通常用在 in 後面

接口:

List<Blog> queryBloyForeach(Map<String, Object> map);

blogMapper.xml,有很多細節,很重要:

<select id="queryBloyForeach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <if test="titleList != null">
        and title in
        <foreach collection="titleList" item="atitle" open="(" close=")" separator=",">
            #{atitle}
        </foreach>
        </if>
    </where>
</select>

foreach標籤裏的屬性依次是,map中集合的key,每一項的迭代名(#{}中的值),開頭,結尾,分隔符。

這個sql其實是:

select * from blog WHERE title in ( ? , ? , ? ) 

測試:

@Test
public void testSelectForeach() {
    try (SqlSession session = MybatisUtils.getSqlSession()) {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<>();
        List<String> titleList = new ArrayList<>();
        titleList.add("《mybatis》");
        titleList.add("《Spring》");
        titleList.add("《SpringMVC》");
        map.put("titleList", titleList);
        List<Blog> blogList = mapper.queryBloyForeach(map);
        for (Blog b : blogList) {
            System.out.println(b);
        }
    }
}

11.6、sql 片段

sql 片段就是抽取出 sql 語句中相同的部分,以便複用

比如 if 的例子:

<select id="queryBlogIF" 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>

可以改成:

<sql id="if-title-author">
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</sql>

<select id="queryBlogIF" resultType="Blog">
    select * from blog
    <include refid="if-title-author"/>
</select>

儘量不用把 where 或 set 放到 sql 標籤裏,可能會出問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章