MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然

时间:2022-09-26 12:57:44

我们知道 MyBatis 中对于参数的赋值有两种方式, 一种是使用 #{}, 一种是使用 ${}, 这两种参数赋值的方式对于有些情况都可以正常使用, 但是针对某些场景, ${} 却会报错. 本篇文章主要介绍两者的区别, 通过例子的方式来说明什么场景下 ${} 不适用, 什么场景下又不得不使用 ${}.

1 例子

????1.1: 根据 id 来查询一条用户的数据

首先在 mapper 层写一个接口, 根据用户的 id 来查询用户;

@Mapper
public interface UserMapper {
    // 根据用户 Id 查询用户
    // @Param("id") 代表 Integer id 这个参数在 xml 文件中使用 id 就能获取到
    public UserInfo getUserById(@Param("id") Integer id);
}

xml 代码如下:

<?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.example.demo.mapper.UserMapper">
    <!-- 根据 id 查询用户信息 -->
    <select id="getUserById" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where id=#{id}
    </select>
</mapper>

关于数据库的表格(数据库表格中已经有数据)以及实体类不再描述, 先看一个单元测试的结果:

@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;

    @Test
    void getUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        Assertions.assertNotNull(userInfo);
    }
}

运行结果:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
然后将xml 文件中改成 ${}:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
运行结果则如下:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
根据上面的两种运行结果, 总结如下:
#{} 是预处理执行, 而 ${} 是直接替换的方式进行查询.

????1.2: 根据用户姓名来查询一条用户的数据

首先在 mapper 层写一个接口, 根据用户的姓名来查询用户;

@Mapper
public interface UserMapper {
    // 根据用户 username 查询用户
    public UserInfo getUserByName(@Param("username") String username);
}

xml 代码如下:

<?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.example.demo.mapper.UserMapper">
    <!-- 根据 username 查询用户信息 -->
    <select id="getUserByName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username=#{username}
    </select>
</mapper>

关于数据库的表格(数据库表格中已经有数据)以及实体类不再描述, 先看一个单元测试的结果:

@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;

    @Test
    void getUserByName() {
        UserInfo userInfo = userMapper.getUserByName("张三");
        Assertions.assertNotNull(userInfo);
    }
}

运行结果如下:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
然后将xml 文件中改成 ${}:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
运行结果则如下:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
根据上面的两种结果, 可以总结如下:
#{} 对于根据姓名来查找一条用户信息没有任何影响, 而 ${} 则报错, 原因我们可以看的出来张三没有加上" ", 因此查不到数据. 这时候两种赋值方式就看出了区别.

????1.3: ${} 使用场景

虽然我们大部分都是使用 #{} 来进行参数赋值, 但是 ${} 的出现也不是一无是处, 下面的这个场景就能展示出 ${} 的作用, 此场景又不得不使用 ${}.
查询数据库中的列表信息, 并且按照升序或者降序打印, 代码如下:
首先在 mapper 层写一个接口, 根据创建时间的升序来查询用户列表;

@Mapper
public interface UserMapper {
    // 获取列表, 根据创建时间进行升序或倒序
    public List<UserInfo> getOrderList(@Param("order") String order);

}

xml 代码如下(使用${}):

<?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.example.demo.mapper.UserMapper">
        <!-- 查询所有的用户信息(按照升序或者降序查找) -->
    <select id="getOrderList" resultType="com.example.demo.model.UserInfo">
        select * from userinfo order by createtime ${order}
    </select>
</mapper>

关于数据库的表格(数据库表格中已经有数据)以及实体类不再描述, 先看一个单元测试的结果:

@SpringBootTest
class UserMapperTest {
    @Resource
    private UserMapper userMapper;

    @Test
    void getOrderList() {
        List<UserInfo> list = userMapper.getOrderList("asc");
        log.info("列表: " + list);
    }
}

运行结果如下:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
此时结果可以查得到;
然后将xml 文件中改成 #{}:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
运行结果则如下(报错):
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
原因如下:
当传递的是一个 SQL 关键字 (SQL 命令) 的时候, 只能使用 ${}, 此时如果使用 #{}, 就会认为传递的是一个普通的值, 而不是 SQL 命令, 也就是说会自主的将 asc / desc 加上’ ', 因此将会报错, 可以看下面我在数据库中用关键字查询的命令, 如下:
MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然
如果对 asc 加上 ’ ', 则会报错:MyBatis 中 #{} 和 ${} 的区别看完这篇文章一目了然

2 总结

  • 定义不同: #{} 是预处理, 而 ${} 是直接替换, 也可以说 ${} 是即时查询;
  • 使用不同: #{} 适用于所有类型的参数匹配, 但 ${} 只适用于数值类型;
  • 安全性不同: #{} 性能比较高, 并且没有安全问题, 但 ${} 存在 SQL 注入的安全问题;
  • ${} 一定要慎用, 如果非用不可, 那么一定要注意把前端用户输入的值进行安全校验 / 过滤.