动态 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 标签里,可能会出问题。