动态sql
动态 sql 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接。
我们可以先思考这样一个问题某系统要完成用户注册功能,会在数据库中要插入一个用户,用户注册时,用户名密码是必填项
其他字段(昵称、性别、生日、头像)是选填项,
比如用户完成注册功能式传递的参数是用户名和密码的必填项,我们这样设计sql:直接插入用户名和密码,
<insert id="insertUser" parameterType="User">
insert into user(username, password) values (#{username},#{password});
</insert>
这样设计存在一个缺陷,如果用户提交的不仅仅包括必填项,还包括选填项,显然这个sql已经不能满足我们的业务了,我们需要对这个sql进行改进,如果用户传递的参数包括选填项,我们就要使用用户传入的参数,如果用户只传入必填项,选填项为null,我们使用默认值就可以了, 这种sql就被称为动态sql
其实本质就是根据不同的的条件生成不同的sql语句,本质依然是sql语句,只是在sql层面,增加了一些逻辑语句
环境准备
创建一个数据库表blog
drop table if exists blog;
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 COMMENT '创建时间',
`views` int(30) COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
数据库实体类编写 使用lombok插件完成getset以及无参有参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
private String id;
private String title;
private String author;
private Date createTime; //属性名和字段名不一致
private int views;
}
springboot 配置文件中配置mybatis
spring.datasource.username=root
spring.datasource.password=tong0726
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#整合mybatis
mybatis.type-aliases-package=com.doer.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
编写mapper接口 以及mapper.xml配置文件
@Mapper
@Repository
public interface BlogMapper {
//插入博客
int insertBlog(Blog blog);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.doer.mapper.BlogMapper">
</mapper>
if 标签
编写BlogMapper接口
@Mapper
@Repository
public interface BlogMapper {
//插入博客
int insertBlog(Blog blog);
}
插入博客的字段创建时间如果是选填项,我们可以使用mybatis提供的if标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.doer.mapper.BlogMapper">
<insert id="insertBlog" parameterType="User">
insert into mybatis.blog(
id, title, author,
<if test="createTime != null">
#{createTime},
</if>
views)
values (
#{id},
#{title},
#{author},
<if test="createTime != null">
#{createTime},
</if>
#{views}
)
</insert>
</mapper>
接下进行测试,
选择创建时间的字段
@Autowired
BlogMapper blogMapper;
@Test
void insertUser() {
Blog blog = new Blog();
blog.setId("1");
blog.setAuthor("doer");
blog.setTitle("一月");
blog.setCreateTime(new Date());
blog.setViews(1);
blogMapper.insertBlog(blog);
}
不选则创建时间字段
@Test
void insertUser1(){
Blog blog = new Blog();
blog.setId("1");
blog.setAuthor("doer");
blog.setTitle("一月");
blog.setViews(1);
blogMapper.insertBlog(blog);
}
可以看到输出结果中完成了动态sql的插入
在select语句中使用if标签
接口
//查询博客,如果传入的是一个title就查询title对应的博客,如果是什么都不传入,将所有博客查询出来
List<Blog> queryBlogIf(Map map);
编写对应mapper,xml
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog where
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
测试
@Test
void queryBlogIf(){
Map map = new HashMap();
map.put("author","doer");
List<Blog> blogs = blogMapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
我们在测试中只传入了一个参数author,相当于if标签中的title标签并没有生效,此时如果我们执行会出现sql异常
发现我们写的sql中多了一个and,此时解决的办法就是引入where标签
where标签
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除
对于出现and错误的例子我们使用where标签就可以解决这个问题
<select id="queryBlogIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
进行测试
@Test
void queryBlogIf(){
Map map = new HashMap();
map.put("author","doer");
List<Blog> blogs = blogMapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
可以发现mybatis已经将我们sql语句中的and去除
set标签
where元素是将额外的and和or关键字删除
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
<update id="updateBlog" parameterType="Blog">
update blog
<set>
<if test="title!= null">
title=#{title},
</if>
<if test="author != null">
author= #{author}
</if>
where id=#{id};
</set>
</update>
测试
@Test
void updateBlog(){
Map map = new HashMap();
map.put("id",1);
map.put("title","二月");
blogMapper.updateBlog(map);
}
choose (when, otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
<select id="queryBlogChoose" resultType="Blog" parameterType="map">
select * from mybatis.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>
foreach
对集合进行遍历时可以使用该标签。标签有如下属性:
- collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
- item:遍历时的每一个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
接口
List<Blog> queryBlogForEach(List<Integer> list);
sql
select * from user where id in(1,2,3)
编写mapper.xml文件
<select id="queryBlogForEach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="list" item="id" separator="," open="id in (" close=")">
id = #{id}
</foreach>
</where>
</select>
测试
@Test
void queryBlogForEach(){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Blog> blogs = blogMapper.queryBlogForEach(list);
for (Blog blog : blogs) {
System.out.println(blog);
}
}