本章节主要讲解mybatis中#{}和${}的区别
首先我们先通过代码事例查看下二者的区别:
在EmployeeMapper接口中有这么一个方法:
Employee getEmpByIdAndLastName(@Param("id")Integer id, @Param("lastName") String lastName);
在对应的EmployeeMapper.xml 映射文件中的sql语法:
<select id="getEmpByIdAndLastName" resultType="employee">
select id,last_name ,email,gender from tbl_employee where id = ${id} and last_name = #{lastName}
</select>
测试代码:
public class MybatisTest {
public SqlSessionFactory sessionFactory = null;
@BeforeEach
public void test()throws Exception{
// 根据全局配置文件(xml)创建一个SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void selectTest(){
// 获取 SqlSession 的实例 。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。通过 SqlSession 实例来直接执行已映射的 SQL 语句
SqlSession sqlSession = null;
try {
sqlSession = sessionFactory.openSession();
// 通过获取接口代理对象来执行sql语句
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(2,"BB");
System.out.println(employee.getLastName()); // AA
} finally {
// 资源关闭,放在finally中确保一定会执行
sqlSession.close();
}
}
}
最终的执行结果为:
DEBUG 05-07 22:52:42,700 ==> Preparing: select id,last_name ,email,gender from tbl_employee where id = 2 and last_name = ? (BaseJdbcLogger.java:145)
DEBUG 05-07 22:52:42,801 ==> Parameters: BB(String) (BaseJdbcLogger.java:145)
DEBUG 05-07 22:52:42,823 <== Total: 1 (BaseJdbcLogger.java:145)
BB
请注意:在日志记录中的sql语句为
select id,last_name ,email,gender from tbl_employee where id = 2 and last_name = ?
其中${id}直接被传入的参数所替代,而#{lastName}则是以预编译的方式变成?
总结:
相同点:
- #{}:可以获取map中的值或者pojo对象属性的值;
- ${}:可以获取map中的值或者pojo对象属性的值;
区别:
- #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
- ${}:取出的值直接拼装在sql语句中;会有安全问题;
注意:大多情况下,我们去参数的值都应该去使用#{};
什么情况下使用${}:
原生jdbc不支持占位符的地方我们就可以使用${}进行取值,比如列名、分表、排序。。。
// 按照年份分表
select * from ${year}_salary where xxx;
// 排序
select * from tbl_employee order by ${f_name} ${order}
#{}更丰富的用法
首先,像 MyBatis 的其他部分一样,参数也可以指定一个特殊的数据类型。
#{property,javaType=int,jdbcType=NUMERIC}
像 MyBatis 的其它部分一样,javaType 几乎总是可以根据参数对象的类型确定下来,除非该对象是一个 HashMap。这个时候,你需要显式指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。
提示 JDBC 要求,如果一个列允许 null 值,并且会传递值 null 的参数,就必须要指定 JDBC Type。阅读 PreparedStatement.setNull()的 JavaDoc 文档来获取更多信息。
要更进一步地自定义类型处理方式,你也可以指定一个特殊的类型处理器类(或别名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
尽管看起来配置变得越来越繁琐,但实际上,很少需要如此繁琐的配置。
对于数值类型,还有一个小数保留位数的设置,来指定小数点后保留的位数。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode 属性允许你指定 IN,OUT 或 INOUT 参数。如果参数的 mode 为 OUT 或 INOUT,就像你在指定输出参数时所期望的行为那样,参数对象的属性实际值将会被改变。 如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 引用来将结果集 ResultMap 映射到参数的类型上。要注意这里的 javaType 属性是可选的,如果留空并且 jdbcType 是 CURSOR,它会被自动地被设为 ResultMap。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持很多高级的数据类型,比如结构体(structs),但是当使用 out 参数时,你必须显式设置类型的名称。比如(再次提示,在实际中要像这样不能换行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
尽管所有这些选项很强大,但大多时候你只须简单地指定属性名,其他的事情 MyBatis 会自己去推断,顶多要为可能为空的列指定 jdbcType。
#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}