MyBatis动态表名和字段,减轻很大工作

在动态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';

Mybatis学习教程链接

posted @ 2023-07-12 23:23  堕落先锋  阅读(130)  评论(0编辑  收藏  举报