一、查询操作🍭
1、单表查询🍉
下面我们来实现⼀下根据用户 id 查询用户信息的功能
UserController 实现代码如下:
//url 路径名直接全部小写即可 @RequestMapping("/getuserbyid") public Userinfo geUserById(Integer id){ if (id==null) return null; return userService.getUserById(id); }
UserMapper 实现代码如下:
/** * 根据用户id查询用户信息 * @param id * @return */ Userinfo getUserById(@Param("id") Integer id);
UserMapper.xml 实现代码如下:
<select id="getUserById" resultType="com.example.ssmdemo1.entity.Userinfo"> select * from userinfo where id=${id} select>
Ⅰ、参数占位符 #{} 和 ${}🍓
- #{}:预编译处理。
- ${}:字符直接替换。
预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使用 PreparedStatement 的 set 方法来赋值。直接替换:是MyBatis 在预处理 时,∗∗就会把{} 时,**就会把 时,∗∗就会把{} 替换成变量的值。**
上面代码我们使用的是${},去传递Integer(整数)类型的参数时,是没有问题的,但如果传递的是String类型的话,程序就会报错。
下面我们通过 根据用户名查询用户(getUserByName)来看看
这就直接报错了,说是没有admin这个用户,这是因为${}是直接替换值(不会管你是什么类型,都直接替换),而SQL语句中字符串需要使用单引号,这就会查询不到,报错。
正确SQL:
两者区别总结:
1、``#{}
:安全参数占位符
- #{}是MyBatis的预编译语句中的参数占位符,用于传递参数值。它会自动进行参数值的类型转换和防止SQL注入攻击。
- 在使用#{}时,MyBatis会将参数值通过JDBC的PreparedStatement接口进行预编译,参数值会被当做字符串类型处理,然后由JDBC驱动来负责将其转换成对应的数据库类型,这样可以避免SQL注入问题。
- 例子:SELECT * FROM users WHERE id = #{userId}
2、``${}
:字符串替换占位符
- 是字符串替换占位符,用于直接将参数的值替换到SQL语句中。在使用{}是字符串替换占位符,用于直接将参数的值替换到SQL语句中。在使用是字符串替换占位符,用于直接将参数的值替换到SQL语句中。在使用{}时,参数值会被直接替换进SQL语句中,不会进行预编译或类型转换。
- 由于直接替换参数值到SQL语句中,可能存在SQL注入的风险,因此不建议在动态SQL中使用{}直接替换参数值到SQL语句中,可能存在SQL注入的风险,因此不建议在动态SQL中使用直接替换参数值到SQL语句中,可能存在SQL注入的风险,因此不建议在动态SQL中使用{}来传递用户输入的参数。
- 例子:SELECT * FROM users WHERE id = ${userId}
那这为什么还有${}去传递参数呢?全部使用#{}不是更好?
Ⅱ、${}优点🍓
在进行排序时(需要传递关键字时)需要使用到${},而 #{sort} 就不能实现排序查询了,因为使用 #{sort} 查询时, 如果传递的值为 String 则会加单引号,就会导致 sql 错误。
UserMapper接口:
//根据id查询用户 并且进行排序 List getAllByOrder(@Param("order") String order);
UserMapper.xml:
<select id="getAllByOrder" resultType="com.example.ssmdemo1.entity.Userinfo"> select * from userinfo order by id ${order} select>
单元测试:
@Test void getAllByOrder() { List list = userMapper.getAllByOrder("asc"); System.out.println(list); }
单元测试成功:
Ⅲ、SQL 注入问题 🍓
UserMapper接口:
Userinfo login(@Param("username")String username,@Param("password")String password);
UserMapper.xml:
<select id="login" resultType="com.example.ssmdemo1.entity.Userinfo"> select *from userinfo where usernaem='${username}' and password='${password}' select>
因为${}是直接引用,所以我们加上了单引号。 这样就和使用#{}是一样的了
单元测试:
@Test void login() { String username="2"; String password="2"; Userinfo userinfo=userMapper.login(username,password); System.out.println("用户登录"+(userinfo==null?"失败":"成功")); }
可以看到此时用户是登录成功的:
但是这样写有SQL注入的风险,我们修改代码如下,然后运行代码
@Test void login() { String username="2"; String password="'or 1 ='1"; Userinfo userinfo=userMapper.login(username,password); System.out.println("用户登录"+(userinfo==null?"失败":"成功")); }
单元测试:
可以看到上面单元测试失败了,但仔细看,是因为返回了5个Userinfo对象,但我只需要接收一个
所以报错了,如果接受的是List
UserMapper接口:
List login(@Param("username")String username, @Param("password")String password);
单元测试:
@Test void login() { String username="2"; String password="' or 1='1"; List userinfo=userMapper.login(username,password); System.out.println("用户登录"+(userinfo==null?"失败":"成功")); }
单元测试成功:
可以看到这是非常可怕的,居然把我所有用户信息返回了(数据库中一共有五个用户),也就是说,你想使用哪个用户登录就可以使用哪个用户登录。
如果使用#{},可能存在这个问题吗?
<select id="login" resultType="com.example.ssmdemo1.entity.Userinfo"> select *from userinfo where username=#{username} and password=#{password} select>
单元测试失败:
结论:用于查询的字段,尽量使用 #{} 预查询的方式,而需要传递关键字时,使用${}
Ⅳ、like查询🍓
在使用like查询时,使用#{}会报错,下面我们来看看是怎么回事。
UserMapper接口:
List<Userinfo> getListByName(@Param("username")String username);
UserMapper.xml:
<select id="getListByName" resultType="com.example.ssmdemo1.entity.Userinfo"> select * from userinfo where username like '%#{username}%' select>
单元测试:
@Test void getListByName() { String username="n"; List list=userMapper.getListByName(username); System.out.println("list:"+list); }
运行报错:
这是因为使用#{}会当作字符串进行替换,就变成下面这样了
select * from userinfo where username like '%'n'%'
我们替换${}试试:
但是前面说了使用有SQL注入的风险,所有这是不能直接使用 {}有SQL注入的风险,所有这是不能直接使用 有SQL注入的风险,所有这是不能直接使用 {},可以考虑使用 mysql 的内置函数 concat() 来处理,实现代码如下:
<select id="findUserByName3" resultType="com.example.demo.model.User"> select * from userinfo where username like concat('%',#{username},'%') select>
单元测试成功:
在使用like查询时应该搭配concat()函数使用。
MyBatis查询数据库(3)(二)https://developer.aliyun.com/article/1393185