動態 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 標籤裏,可能會出問題。