# MyBatis教程 - 7 特殊SQL的执行

下面来介绍一下 MyBatis 中,一些特殊 SQL 的执行,下面这些情况,和普通的处理有些区别。

# 7.1 模糊查询

在前面我们推荐能使用 #{} 的地方就不要使用 ${},但是模糊查询这种场景,直接使用 #{} 就无法解决,下面有三种处理方式。


举个栗子:

根据用户名模糊查询,定义接口,UserMapper.java 如下:

/**
 * 根据用户名模糊查询用户
 */
List<User> selectByUsernameLike(@Param("username") String username);
1
2
3
4

定义 SQL 映射,UserMapper.xml 如下:

<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE '%#{username}%'
</select>
1
2
3
4

但是直接写 '%#{username}%' ,在执行的时候是会报错的,通过日志发现,执行的 SQL 是:SELECT * FROM tb_user WHERE username LIKE '%?%' ,因为 #{} 使用的是占位符的方式,所以 '%?%' 被处理成了字符串。

针对模糊查询,下面有三种处理方式。

# 1 使用${}拼接

UserMapper.xml

<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE '%${username}%'
</select>
1
2
3
4

直接使用 ${username} 就可以解决。

# 2 使用contact拼接

UserMapper.xml

<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
1
2
3
4

使用 CONCAT 进行字符串拼接,这样就可以将 %#{username} 拼接起来。

# 3 使用双引号(推荐)

UserMapper.xml

<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE "%"#{username}"%"
</select>
1
2
3
4

注意 % 左右都有双引号。

推荐使用这种方式。

# 7.2 批量删除

有时候批量删除元素,使用的是将ID或条件拼接起来,然后使用 in 来进行删除。

例如根据多个用户名删除用户,那么 SQL 如下:

DELETE FROM tb_user WHERE username in ('doubi','niubi')
1

使用 MyBatis 定义接口如下,在 UserMapper.java 中:

/**
 * 批量删除用户信息
 */
int deleteByUsernames(@Param("usernames") String usernames);
1
2
3
4

那么对应的 UserMapper.xml 编写如下:

<delete id="deleteMore">
    DELETE FROM tb_user
    WHERE username in (${usernames})
</delete>
1
2
3
4

这里需要使用 ${} ,只需要传递 'doubi','niubi' 格式的参数,自己添加引号即可。

不能使用 #{} 了,因为参数是字符串, #{} 会自动在最外层添加引号,变成 ''doubi','niubi''

所以编写测试方法如下:

@Test
public void deleteMore() {
    // 获取SqlSession连接
    SqlSession sqlSession = MyBatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    String usernames = "'doubi','niubi'";   // 传递多个用户名

    // 删除用户
    int result = userMapper.deleteByUsernames(usernames);
    log.info("result: {}", result);
}
1
2
3
4
5
6
7
8
9
10
11
12

注意:这里值针对字符串类型的字段,使用 in 来删除,如果是整形的ID,那么直接使用 String ids = "1,2"; 即可,不用引号。

后面在动态SQL章节,会通过foreach标签来讲解批量删除。

# 7.3 动态设置表名

有时候,一张表的数据太多了,我们会进行数据的拆分,将数据保存在多张表中,这个时候查询数据库,我们就需要将表的名称也传递给 XxxMapper.xml 映射文件。

使用 #{} 传递字符串类型,会自动添加上 '',但是表的名称在 SQL 中是不能用引号 '' 括起来的,所以这个时候只能使用 ${}

举个栗子:

根据表名和用户名查询用户,UserMapper.java

/**
 * 根据表名和用户名查询用户信息
 */
List<User> selectByTableName(@Param("tableName") String tableName, @Param("username") String username);
1
2
3
4

对应的 UserMapper.xml 的 SQL 映射如下:

<select id="selectByTableName" resultType="User">
    SELECT * FROM ${tableName}
    WHERE username = #{username}
</select>
1
2
3
4

这样就可以实现动态的设置表名称。

测试的时候可以传递表的名称:

String tableName = "tb_user";
String username = "doubi";

// 查询用户列表
List<User> userList = userMapper.selectByTableName(tableName, username);
1
2
3
4
5

# 7.4 获取自增的主键ID值

在实际的开发中,经常会遇到这样的情况,就是两张表存在关联,一张表中有一列外键,存储另一张表的ID。

在插入数据的时候,可能会存在同时插入两张表的数据,此时就需要知道插入后,数据的主键ID,这样好把 ID 作为外键保存到另一张表中。

下面就介绍一下如何获取插入后的自增主键的ID值。

UserMapper.java

/**
 * 插入用户信息
 */
int insert(User user);
1
2
3
4

接口是没有变化的,返回值并不是主键的ID,返回值表示插入数据的条目数。


所以需要修改 SQL 映射文件,UserMapper.xml

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO tb_user (username, password, email, age, create_time, update_time)
    VALUES (#{username}, #{password}, #{email}, #{age}, #{createTime}, #{updateTime})
</insert>
1
2
3
4
  • useGeneratedKeys="true" 来告诉MyBatis这个插入操作可能会生成一个键值;
  • 通过 keyProperty 属性指定一个Java对象的属性名,MyBatis会将生成的主键ID值设置到这个Java对象的属性中。也就是插入的时候,传入了一个 User 对象,keyProperty="id" 表示插入完成后,MyBatis 会将生成的主键 ID 设置到 User 对象的 id 属性。

编写测试方法:

@Test
    public void testInsertUser() {
    // 获取SqlSession连接
    SqlSession sqlSession = MyBatisUtils.getSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    User user = new User();
    user.setUsername("foooor");
    user.setPassword("123456");
    user.setEmail("foooor@qq.com");
    user.setAge(20);
    user.setCreateTime(new Date());
    user.setUpdateTime(new Date());

    // 插入用户
    int result = userMapper.insert(user);
    log.info("user id: {}", user.getId());  // 可以获取到插入后的ID
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这样就可以获取到插入后的自增主键的ID了。