#占位符的特点
1)使用的PrepareStatement对象,执行sql语句,效率高
2)使用的PrepareStatement对象,能避免sql语句,sql语句执行更安全
3)#{}常作为【列值】使用的,一般用来传递列值。【重点】
$占位符的特点
1) 使用$()传参时,在dao接口必须使用@Param命名参数 2)使用Statement对象,执行sql语句,效率低 3)${}占位符的值,使用的是字【符串连接的方式】,有sql注入的风险,有代码安全的问题 4)${}数据是【原样使用的,不会区分数据类型】 5)${}【适合用作表名或列名】,不适合用作 列值 使用,在能保证数据安全的情况下使用${}【重点】
对比:
dao接口方法:
List<Student> queryStudent(@Param("studentName") String name);
mapper内的对应sql语句:
<!--${}的sql语句-->
<select id="queryStudent" resultType="com.gys.domain.Student">
select * from User where name=${studentName}
</select>
<!--#{}的sql语句-->
<select id="queryStudent" resultType="com.gys.domain.Student">
select * from User where name=#{studentName}
</select>
测试:
//#{}的执行sql语句方法
@Test
public void testSelectByName(){
SqlSession session = MyBatisUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> students = dao.queryStudent("李四");
students.forEach(student -> System.out.println("学生: "+student));
session.close();
}
//${}的执行sql语句方法
@Test
public void testSelectByName(){
SqlSession session = MyBatisUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
//这里因为${}是原样使用,字符串拼接,所以 必须传入的是 ”‘李四’“,而不能是 “李四”
List<Student> students = dao.queryStudent("'李四'");
students.forEach(student -> System.out.println("学生: "+student));
session.close();
}
mybatis根据上述代码自动生成的sql语句:
//#{}自动生成的sql语句
Preparing: String Sql =“select * from User where name= ? ”
Parameters: 李四(String)
//${}自动生成的sql语句
Preparing: String Sql =“select * from User where name= '李四' ”
Parameters:
【重点】
这里#{} 是把 李四 这个 sting类型的值 传递给了 name,
而${} 是把 李四 和 前面的 sql语句 作为字符串拼接在了一起。
上述这种查询写法自然是看不出#{}和${}都用作列值的区别
但!下面举例把 ${} 用作列值的不安全性!
用户使用正常数据输入:李四。 #{}和${}两者返回的数据都没问题。
用户使用错误数据输入:李四 or 1=1 。此时,#{}和${}返回的结果就不想同了。
此时,#{} 是把 (李四 or 1=1) 作为一个整体,把这个整体作为一个值传给了 name,通过匹配数据库User表的name列,查找name = (李四 or 1=1) 的这个学生
但是,${} 是把 李四 or 1=1 拼接在了 sql语句后面,此时 mybatis自动生成的sql语句变成了:
String sql = " select * from User where name='李四' or 1=1 "
整个sql语句的意思就被用户自己改变:
本来是查找名字为:李四 or 1=1 的这个学生,最多只会产生一条返回结果;
现在变成了 查找名字为 李四 或者 1 代表真,查询User所有用户,则一定会暴露出数据库User表中的所有数据。
这就体现了${}作为列表的不安全性
${} 适用领域:用作 表名 或 列名
对比 #{} 和 ${} 用作 表名 或 列名
dao接口方法:
//#{}和${}
List<Student> queryStudentOrderByColName(@Param("ColName") String name);
mapper的sql语句:
<!--#{}-->
<select id="queryStudentOrderByColName" resultType="com.gys.domain.Student">
select * from User order by #{ColName}
</select>
<!--${}-->
<select id="queryStudentOrderByColName" resultType="com.gys.domain.Student">
select * from User order by ${ColName}
</select>
测试语句:
#{}测试语句:
//#{}
@Test
public void testSelectOrderByColName(){
SqlSession session = MyBatisUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> students = dao.queryStudentOrderByColName("name");
students.forEach(student -> System.out.println("学生: "+student));
session.close();
}
mybatis内部解释:
Preparing: String sql = "select * from User order by ?"
Parameters: name(String)
结果:
转化为 数据库中的语句: select * from User order by 'name'
注意,这是错误的 通过 name列排序, 正确的为 order by name
这是因为 #{} 记录的 name 是String 类型,再进行赋值操作后,传入数据库就是 ‘name’,而不是 name
${}测试语句:
//${}
@Test
public void testSelectOrderByColName(){
SqlSession session = MyBatisUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> students = dao.queryStudentOrderByColName("name");
students.forEach(student -> System.out.println("学生: "+student));
session.close();
}
mybatis内部解释:
Preparing: String sql = " select * from User order by name "
Parameters:
结果:
转化为 数据库中的语句: select * from User order by name
这才是正确的sql语法
这是因为 ${} 是把 ${} 的数据 拼接 到了 sql语句后面,而不是 赋值。
一般都是#{}和${}混合使用:
dao接口的方法:
List<Student> queryStudentOrderByColName(@Param("tableName") String tableName,
@Param("dataName") String dataName,
@Param("ColName") String ColName);
mapper内的sql语句:
<!--* 处也可以用 ${}代替-->
<select id="queryStudentOrderByColName" resultType="com.gys.domain.Student">
select * from ${tableName} where ${dataName} order by ${ColName}
</select>
测试语句:
@Test
public void testSelectOrderByColName2(){
SqlSession session = MyBatisUtil.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> students = dao.queryStudentOrderByColName2("User","李四","name");
students.forEach(student -> System.out.println("学生: "+student));
session.close();
}
mybatis内部解释:
Preparing: select * from User where name=? order by ?
Parameters: 李四(String), name(String)
结果:
搜索 User表 中 name=李四 的 所有用户,并按照 name 列 排序