MyBatis 详解(九)

发布于 2021-07-28  215 次阅读


动态 SQL 语句

MyBatis 的动态 SQL 元素与 JSTL 或 XML 文本处理器相似,常用 if、choose、when、otherwise、trim、where、set、foreach 和 bind 等元素。

环境搭建

在 mybatis 数据库下创建名为 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 NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8

编写 pojo 层实体类

package ml.guest997.pojo;

import lombok.Data;
import java.util.Date;

@Data
public class Blog {
    private String id;      //id 是用 UUID 处理后生成的
    private String title;
    private String author;
    private Date createTime;    //属性名与字段名不一致,需要在核心配置文件中开启驼峰命名自动映射
    private int views;
}

编写工具类

package ml.guest997.util;

import java.util.UUID;

public class IDUtil {
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }
}

编写 Mapper 层接口和实现类

package ml.guest997.mapper;

import ml.guest997.pojo.Blog;

public interface BlogMapper {
    int addBlog(Blog blog);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!--BlogMapper.xml-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

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

注册配置文件

<settings>
    <!--开启驼峰命名自动映射-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
    <!--包扫描使用别名-->
    <package name="ml.guest997.pojo"/>
</typeAliases>
<mapper class="ml.guest997.mapper.BlogMapper"/>

插入数据

package ml.guest997.mapper;

import ml.guest997.pojo.Blog;
import ml.guest997.util.IDUtil;
import ml.guest997.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;

public class BlogMapperTest {
    @Test
    public void addBlog(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtil.getId());
        blog.setTitle("Mybatis");
        blog.setAuthor("Guest997");
        blog.setCreateTime(new Date());
        blog.setViews(9999);
        blogMapper.addBlog(blog);

        blog.setId(IDUtil.getId());
        blog.setTitle("Java");
        blogMapper.addBlog(blog);

        blog.setId(IDUtil.getId());
        blog.setTitle("MySQL");
        blogMapper.addBlog(blog);

        blog.setId(IDUtil.getId());
        blog.setTitle("JavaWeb");
        blogMapper.addBlog(blog);

        sqlSession.close();
    }
}

if 标签

在 MyBatis 中 if 元素是最常用的元素,它类似于 Java 中的 if 语句。

List<Blog> getBlogByIf(Map map);
<!--where 1=1 是为了避免 where 关键字后面的第一个词直接就是 and 而导致语法错误。where 后面总要有语句,加上了1=1后就可以保证语法不会出错。-->
<select id="getBlogByIf" 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>
@Test
public void getBlogByIf(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();    //只创建对象,不插入键值对,会查询出所有记录。
    map.put("title","Java");
    map.put("author","Guest997");
    List<Blog> blogList = blogMapper.getBlogByIf(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}
//结果为 Blog(id=ec982a0455964dfc9c6ae486ddf45719, title=Java, author=Guest997, createTime=Sat Jun 12 11:57:51 CST 2021, views=9999)

choose、when、otherwise 标签

MyBatis 提供了 choose 元素,它类似于 Java 中的 switch 语句。

List<Blog> getBlogByChoose(Map map);
<select id="getBlogByChoose" 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 = #{views}
        </otherwise>
    </choose>
</select>
@Test
public void getBlogByChoose(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    //会选择其中的一个条件进行筛选,可以分别注释掉下面的代码来测试。
    map.put("title","Java");
    map.put("author","Guest997");
    map.put("views",9999);
    List<Blog> blogList = blogMapper.getBlogByChoose(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}
//结果为 Blog(id=ec982a0455964dfc9c6ae486ddf45719, title=Java, author=Guest997, createTime=Sat Jun 12 11:57:51 CST 2021, views=9999)

where、set、trim 标签

where 元素只会在子元素返回任何内容的情况下才插入 WHERE 子句。而且,若子句的开头为 AND 或 OR,where 元素也会将它们去除。(where 1=1 这种语句实际中很少有,都是用 where 标签,故修改上面的 if 标签的配置文件)

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

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

int updateBlog(Map 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 updateBlog(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    //至少得传入一个 set 的字段键值对,否则构不成 update 语句。
    map.put("title","Java02");
    map.put("author","zyp997");
    map.put("id","ec982a0455964dfc9c6ae486ddf45719");    //id 如果不要,会修改全部记录。
    int result = blogMapper.updateBlog(map);
    if(result > 0){
        System.out.println("修改成功!");
    } else {
        System.out.println("修改成功!");
    }
    sqlSession.close();
}

trim 元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix。可以把包含内容的首部或尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides。正因为 trim 元素有这样的功能,所以也可以非常简单地利用 trim 来代替 where 和 set 元素的功能。尝试修改上面的 where 和 set 的配置文件。

<select id="getBlogByIf" parameterType="map" resultType="blog">
    select * from blog
    <trim prefix="where" prefixOverrides = "and | or">
        <if test="title != null">
            title =#{title}
        </if>
        <if test="author != null">
            and author =#{author}
        </if>
    </trim>
</select>
<update id="updateBlog" parameterType="map">
    update blog
    <trim prefix="set" suffixOverrides=",">
        <if test="title != null">
            title =#{title},
        </if>
        <if test="author != null">
            author =#{author}
        </if>
    </trim>
    where id = #{id}
</update>

SQL 片段

有的时候,我们可能会将一些功能的部分抽取出来,方便复用!就比如上面的 if 判断,多个标签中都使用到了,我们就能抽取出来实现复用。

<sql id="if-title-author">
    <if test="title != null">
        title =#{title}
    </if>
    <if test="author != null">
        and author =#{author}
    </if>
</sql>
<select id="getBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author"/>
    </where>
</select>

foreach标签

foreach 元素主要用在构建 in 条件中,它可以在 sql 语句中迭代一个集合。

foreach 元素的属性主要有 item、index、collection、open、separator、close。

  • item:表示集合中每一个元素进行迭代时的别名。
  • index:指定一个名字,用于表示在迭代过程中每次迭代到的位置。
  • open:表示语句以什么开始。
  • separator:表示在每次进行迭代之间以什么符号作为分隔符。
  • close:表示语句以什么结束。
//查询前三个博客信息(为了方便测试,把博客表的 id 改为1)
List<Blog> getBlogByForeach(Map map);
<!--
下面的两个语句都能用,选其中一种进行测试。
select * from blog where id=1 or id=2 or id=3
select * from blog where id in (1,2,3)
-->
<select id="getBlogByForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" separator="or">
            id = #{id}
        </foreach>
    </where>

    select * from blog where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        id = #{id}
    </foreach>
</select>
@Test
public void getBlogByForeach(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    map.put("ids",ids);
    List<Blog> blogList = blogMapper.getBlogByForeach(map);
    for (Blog blog : blogList) {
        System.out.println(blog);
    }
    sqlSession.close();
}