在动态sql解析过程,#{}与${}有本质差别
1. #{} 是基于JDBC的preparedStaement ,${} 是基于JDBC的Statement
2. #{} 表示的是预编译的参数,就是 替代在SQL语句中的占位符‘?’,并会将参数作为字符串处理;如果要动态传入 表名或者字段名,不能使用#{}
3. #{} 是使用预编译 传参,可以预防SQL注入,${} 是直接将值输出,是不安全的
4. ${}只能获得参数池的值, 而#{}可以获得接口方法参数列表的值,也可以获取参数池的值,如果使用${}获取参数类别中的值,这个参数必须使用 @Param 强制绑定
可以看到#{}被解析为一个参数占位符?
select * from user where name = #{name};
会被解析为:
select * from user where name = ?;
${} 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换
select * from user where name = ${name};
当我们传递参数“sprite”时,sql会解析为:
select * from user where name = sprite; 注意要传值,需要加引号'sprite',不然报错,而传表名字段名则不需要
SQL注入风险
${}在预编译之前已经被变量替换了
select * from ${tableName} where name = ${name}
如果传入的参数tableName为user; delete user; --,那么sql动态解析之后,预编译之前的sql将变为:
select * from user; delete user; -- where name = ?;
查询语句变为删除数据,所以尽量选择使用#{}更为安全些。
实例
public List<Map<String, Object>> getDatasByMap(Map<String> map);
<select id="getDatasByMap" resultType="java.util.Map" parameterType="java.util.HashMap" statementType="STATEMENT" >
select
${columns}
from ${tableName}
group by ${columns}
</select>
@Test
public List<Map<String, Object>> testByMap(Map<String> map){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
HashMap<String, Object> map = new HashMap();
map.put("columns", "name,age,id");
map.put("tableName", "users");
User user = mapper.getDatasByMap(map);
System.out.println(user);
}
要实现动态调用表名和字段名,就不能使用预编译了,需添加statementType="STATEMENT"" 。
statementType:STATEMENT(非预编译),PREPARED(预编译)或CALLABLE中的任意一个,这就告诉 MyBatis 分别使用Statement,PreparedStatement或者CallableStatement。默认:PREPARED。这里显然不能使用预编译,要改成非预编译。
其次,sql里的变量取值是${xxx},不是#{xxx}。
因为${}是将传入的参数直接显示生成sql,如${xxx}传入的参数为字符串数据,需在参数传入前加上引号,如:
String name = "sprite";
name = "'" + name + "'";
这样,sql就变成:
select * from user where name = 'sprite';
标签:name,sql,减轻,user,表名,MyBatis,参数,where,select
From: https://www.cnblogs.com/hbym/p/17549170.html