您现在的位置是:网站首页> 编程资料编程资料
Mybatis多表查询与动态SQL特性详解_Mysql_
                    
                
                2023-05-26
                350人已围观
            
简介 Mybatis多表查询与动态SQL特性详解_Mysql_
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 ListgetOrderList(@Param(value = "order") String order); 
我们再去xml文件中去写sql语句:首先我们使用$进行演示
我们进行一个单元测试,单元测试代码很简单,就是调用sql,然后得到结果集:
 @Test void getOrderList() { List userMappers = userMapper.getOrderList("desc"); System.out.println(userMappers); }  单元测试结果:
可以正常查询,得到的结果与预期也是相同的。

我们再来试一试使用#{}来构造sql语句:
单元测试逻辑与代码不变,我们再来看看单元测试执行的一个结果:

我们发现程序报错了,这是因为使用了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语句。
第三步,编写测试类,我们在这个测试类中,传入有注入问题的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 ListgetListByName(@Param("username") String username); 
第二步,xml写sql。
第三步,单元测试。
 @Test void getListByName() { String username = "a"; List list = userMapper.getListByName(username); for (UserInfo userInfo : list) { System.out.println(userInfo); } }  运行结果:报错了,因为#{}会被替换成一个字符串,而在这个%#{username}%语句中#{username}不能带上引号,带上就违背SQL语法,造成错误。

这个时候,由于#{}多出一对引号,${}无法枚举所有情况进行验证会产生SQL注入,所以不能直接使用#{},我们需要搭配MySQL内置的字符串拼接语句concat。
我们将sql改为concat进行字符串拼接:
重新测试一下,发现可以正常执行了:

1.4resultType与resultMap
resultType表示数据库返回的数据映射在java程序中所对应的类型,只要定义类中的字段名与数据库中表的字段名字一致就没有任何问题,但是如果字段名存在冲突,则冲突的字段无法获取到数据库查询的结果。
比如用户名属性在数据库中的名字是username,而在java程序类中的属性名为name,此时通过mybatis将数据传递到程序中的对象时,获取到的name属性为null,就不能正确地获取到对应的属性值,为了解决这个数据库字段与类中中字段不匹配的问题,我们需要使用到resultMap。
resultMap的使用方式就是在xml文件中设置标签,至少需要设置两个属性,一个是id表示你这个resultMap标签的名字,还有一个是type属性,它表示映射到程序中类的类型,需包含包名。
这个标签里面需要设置至少两个子标签,一个是id标签,另外一个是result标签,前者表示主键,后者表示数据库表中普通的列,这两种标签也是至少需要设置两个属性,一个是column表示数据库表中的字段名,另外一个是property表示程序类中的字段名,如果只是在单表进行查询,只设置不同字段名的映射就可以了,但是如果是多表查询,必须将数据表中所有的字段与类中所有的字段生成映射关系。
就像下面这样,图中类与数据表字段是相同的,实际情况会存在不同的字段名:

1.4多表查询
1.4.1一对一表映射
一对一关系就是对于一个属性只与另外一个属性有关系的映射,这就是一对一的关系,举个例子,对于一篇博客,它只会对应到一个用户,则博客与用户的关系是一对一的关系,下面我们尝试在mybatis中实现一对一多表联查。
那么,首先我们先将数据库的博客表与查程序中的博客类对应起来,就是按照数据库中的博客表建立一个类:

@Data public class Articleinfo { private Integer id; private String title; private String content; private String createtime; private String updatetime; private Integer uid; private Integer rcount; private Integer state; //不妨多一个属性,用户表 private UserInfo userInfo; } 目前文章表中只有一条数据,如下图:
相关内容
- MySQL insert死锁问题解决详细记录_Mysql_
 - MySQL中增删改查操作与常见陷阱详解_Mysql_
 - MySQL全文索引like模糊匹配查询慢解决方法_Mysql_
 - 关于SqlServer中datediff用法_Mysql_
 - MySQL系列连载之XtraBackup 备份原理解析_Mysql_
 - update.where无索引导致MySQL死锁问题解决_Mysql_
 - MySQL ClickHouse常用表引擎超详细讲解_Mysql_
 - SQL中如何将行转成列详解_Mysql_
 - MySQL ClickHouse不同于SQL的语法介绍_Mysql_
 - MySQL5.7主从复制详细配置教程_Mysql_
 
                                
                                                        
                                
                                                        
                                
                                                        
    