# MyBatis教程 - 7 特殊SQL的执行
下面来介绍一下 MyBatis 中,一些特殊 SQL 的执行,下面这些情况,和普通的处理有些区别。
# 7.1 模糊查询
在前面我们推荐能使用 #{} 的地方就不要使用 ${},但是模糊查询这种场景,直接使用 #{} 就无法解决,下面有三种处理方式。
举个栗子:
根据用户名模糊查询,定义接口,UserMapper.java 如下:
/**
 * 根据用户名模糊查询用户
 */
List<User> selectByUsernameLike(@Param("username") String username);
2
3
4
定义 SQL 映射,UserMapper.xml 如下:
<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE '%#{username}%'
</select>
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>
2
3
4
直接使用 ${username} 就可以解决。
# 2 使用contact拼接
UserMapper.xml :
<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE CONCAT('%', #{username}, '%')
</select>
2
3
4
使用 CONCAT 进行字符串拼接,这样就可以将 % 和 #{username} 拼接起来。
# 3 使用双引号(推荐)
UserMapper.xml :
<select id="selectByUsernameLike" resultType="User">
    SELECT * FROM tb_user
    WHERE username LIKE "%"#{username}"%"
</select>
2
3
4
注意 % 左右都有双引号。
推荐使用这种方式。
# 7.2 批量删除
有时候批量删除元素,使用的是将ID或条件拼接起来,然后使用 in 来进行删除。
例如根据多个用户名删除用户,那么 SQL 如下:
DELETE FROM tb_user WHERE username in ('doubi','niubi')
使用 MyBatis 定义接口如下,在 UserMapper.java 中:
/**
 * 批量删除用户信息
 */
int deleteByUsernames(@Param("usernames") String usernames);
2
3
4
那么对应的 UserMapper.xml 编写如下:
<delete id="deleteMore">
    DELETE FROM tb_user
    WHERE username in (${usernames})
</delete>
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);
}
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);
2
3
4
对应的 UserMapper.xml 的 SQL 映射如下:
<select id="selectByTableName" resultType="User">
    SELECT * FROM ${tableName}
    WHERE username = #{username}
</select>
2
3
4
这样就可以实现动态的设置表名称。
测试的时候可以传递表的名称:
String tableName = "tb_user";
String username = "doubi";
// 查询用户列表
List<User> userList = userMapper.selectByTableName(tableName, username);
2
3
4
5
# 7.4 获取自增的主键ID值
在实际的开发中,经常会遇到这样的情况,就是两张表存在关联,一张表中有一列外键,存储另一张表的ID。
在插入数据的时候,可能会存在同时插入两张表的数据,此时就需要知道插入后,数据的主键ID,这样好把 ID 作为外键保存到另一张表中。
下面就介绍一下如何获取插入后的自增主键的ID值。
UserMapper.java :
/**
 * 插入用户信息
 */
int insert(User user);
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>
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
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这样就可以获取到插入后的自增主键的ID了。
← 06-查询结果 08-多对一和一对多 →
