⭐️前面的话⭐️
本篇文章将介绍使用MyBatis进行多表查询以及MyBatis的动态SQL特性。
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2022年10月20日🌴
✉️坚持和努力一定能换来诗与远方!
💭参考书籍:无
💬参考在线编程网站:🌐牛客网🌐力扣
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
📌导航小助手📌
1.较复杂的查询操作
1.1 参数占位符 #{} 和 ${}
#{}
:预处理符,如将id=#{2}
替换为id=?
,然后使用2
替换?
。
${}
:替换符,如将id=${2}
替换为id=2
。
两种占位符都可以正常使用的场合:传入的参数类型是数值类型
使用${}
:
select * from userinfo where id=${
id}
select * from userinfo where id=2
使用#{}
:
select * from userinfo where id=#{
id}
select * from userinfo where id=?
对于这两种参数占位符,个人建议能使用#{}
就使用#{}
,因为${}
存在SQL注入的问题,以及如果传入的类型是字符串也会出类型。
只能使用#{}
而不能使用${}
的场合:传入参数类型为String
使用${}
:
select * from userinfo where username=${
username}
//实际执行的语句
Preparing: select * from userinfo where username=张三
但是在sql中通过字符串来查询数据,是需要加上引号的,而使用${}
生成的sql并没有带引号,因此不适用于字符串参数的sql。
使用#{}
:
select * from userinfo where username=#{
username}
//实际执行语句
select * from userinfo where username=?
由于使用#{}
是将目标参数替换为占位符,然后利用JDBC中的占位符机制实现sql语句的填充,所以使用#{}
构造的sql是可以正常运行的,并且没有SQL注入的问题。
所以,#{}
相比于${}
,它支持所有类型的参数,包括数值类与字符串类,而${}
支持数值类型,不支持字符串类型的参数,但也可以在原来sql里面为${}
外面加上一对引号。
select * from userinfo where username='${
username}'
//实际执行语句
Preparing: select * from userinfo where username='张三'
当传递的参数为字符串类型的时候,虽然加上一对引号,使用${}
也可以做到,但是${}
存在SQL注入问题,所以仍然不推荐,有关SQL注入后面我们会介绍到。
大部分场合下,使用#{}
都可以解决,但还是存在一小部分只能是${}
来处理的。
如当我们需要按照升序或者逆序得到数据库查询结果的时候,这种场合就只能使用${}
,使用#{}
会报错,我们来演示一下。
首先,我们在Mapper
接口中声明一个方法:作用就是按照排序获取结果集
public List<UserInfo> getOrderList(@Param(value = "order") String order);
我们再去xml
文件中去写sql语句:首先我们使用$
进行演示
<select id="getOrderList" resultType="com.example.demo.model.UserInfo">
select * from userinfo order by createtime ${
order};
</select>
我们进行一个单元测试,单元测试代码很简单,就是调用sql,然后得到结果集:
@Test
void getOrderList() {
List<UserInfo> userMappers = userMapper.getOrderList("desc");
System.out.println(userMappers);
}
单元测试结果:
可以正常查询,得到的结果与预期也是相同的。
我们再来试一试使用#{}
来构造sql语句:
<select id="getOrderList" resultType="com.example.demo.model.UserInfo">
select * from userinfo order by createtime #{
order};
</select>
单元测试逻辑与代码不变,我们再来看看单元测试执行的一个结果:
我们发现程序报错了,这是因为使用了desc
的字符串替换了占位符,而我们所需要的不是一个desc
字符串,而是直接一个desc
的关键字,所以sql也抛出了语法错误,最终执行的sql为:
select * from userinfo order by createtime ‘desc’;
期望执行的sql为:
select * from userinfo order by createtime desc;
所以在传递sql关键字的时候,不能使用#{}
,只能使用${}
。
1.2SQL注入
SQL注入就是使用${}
,使用一些特殊的语句,来达到非法获取数据的目的,如不通过正确的密码获取某账户的信息,下面我们来演示一下,就以登录的例子来演示,SQL注入可以在不知道密码的前提下登录成功,并且获取到用户的相关信息。
首先我将数据库只保留一个用户信息,目的是为了方便演示SQL注入问题:
第一步,在Mapper
接口中定义方法login
,返回登录成功的用户对象。
public UserInfo login(@Param("username") String username, @Param(("password")) String password);
第二步,在xml
文件中编写SQL语句,我们需要演示SQL注入,所以我们使用${}
来构造sql语句。
<select id="login" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username='${
username}' and password='${
password}';
</select>
第三步,编写测试类,我们在这个测试类中,传入有注入问题的SQL语句' or 1='1
,使得不需要密码就能拿到相关的用户信息。
@Test
void login() {
String username = "admin";
String password = "' or 1='1";
UserInfo userInfo = userMapper.login(username, password);
System.out.println(userInfo);
}
单元测试运行结果:
我们在不知道用户密码的情况下,登录成功,并拿到了用户的信息。
最终执行的一段sql语句为:
select * from userinfo where username='admin' and password='' or 1='1';
相当于它在原来条件判断的语句下,后面有加上一个或的逻辑,并且或后面的表达式为true
,这样就使得原来的SQL语句中的条件判断部分一定为真,所以就在密码不知道的情况下拿到了用户的基本信息。
所以我们能不使用#{}
就不使用${}
,因为存在SQL注入问题,如果必须使用${}
则需要验证一下传递的参数是否合法,比如上面定义排序的sql,传递的参数只能是desc
或者是asc
,如果不是就不能执行这条SQL,防止SQL注入的发生。
1.3like查询
在Mybatis中使用like
查询比较特殊,因为直接使用#{}
会报错,而使用${}
,由于输入的字符串情况很多,无法做到枚举,验证比较困难,无法避免SQL注入问题。
首先,我们来演示使用#{}
进行like
查询,步骤我就不详细写了,就是查询的步骤。
第一步,声明方法。
public List<UserInfo> getListByName(@Param("username") String username);
第二步,xml写sql。
<select id="getListByName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like '%#{
username}%'
</select>
第三步,单元测试。
@Test
void getListByName() {
String username = "a";
List<UserInfo> list = userMapper.getListByName(username);
for (UserInfo userInfo : list) {
System.out.println(userInfo)