Mybatis的SQL编写—XML方式
各位看官,大家早安午安晚安呀~~~
如果您觉得这篇文章对您有帮助的话
欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦
今天我们来学习:Mybatis的SQL编写—XML方式
1.yml(配置文件)和XML文件的配置
Mybatis对JDBC的封装体现这里再阐述一下
资源自动管理:自动释放数据库连接、语句和结果集资源。
SQL 与代码解耦:将 SQL 独立到 XML 或注解中,支持动态 SQL。
对象关系映射(ORM):通过约定或者配置,自动将查询结果映射到 Java 对象(POJO)。
简化事务管理:通过SqlSession提供事务控制,支持声明式事务(结合 Spring)
SQL 与代码解耦:将 SQL 独立到 XML 或注解中,支持动态 SQL。只是一种把SQL独立到XML文件中了,没什么新奇的
1.XML配置
<!-- 每一个接收数据库的Mapper接口都对应着自己的XML配置文件-->
<!-- 每一个接收数据库的Mapper接口都对应着自己的XML配置文件-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.Spring.j37mybatis.Mapper.UserInfoXmlMapper"></mapper>
每一个接收数据库的Mapper接口都对应着自己的XML文件
2.yml配置文件如下
另外:需要告诉Mybatis我们的XML文件在哪里(SQL语句在哪里),Mybatis帮我们生成这个接口方法的时候,肯定需要我们的sql语句,然后访问MySQL
mybatis:# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件mapper-locations: classpath:mybatis/**Mapper.xml #这些表的名称都是Mapper.xml结尾的(
# 以**开头就不用一个一个指定所有的xml文件了(在这个路径下,以这个Mapper.xml结尾的都是Mapper接口的配置文件
2.XML文件中SQL语句
用这个XML中的SQL语句,注解就不需要了
2.1.插入(insert),删除(delete),修改(update)
这三步不需要怎么修改,直接把SQl语句放到XML中就行了
解释一下:
2.2.查询(select)
<!-- select返回值的类型我们要确定一下,毕竟这个返回值的类型有两个-->可以是对象,也可以是影响的行数
返回值为Integer
XML
测试:
结果就返回查到的行数了
3.XML中,数据库字段和对象属性怎么映射?
1.还是约定:字段和名字相同的直接就映射了
2.两个配置
- 2.1结果配置
- 2.2.驼峰自动转换——yml配置文件中配置
3.3.起别名(不建议,耦合太强,就是利用Mysql的as关键字把字段的名字和对象属性变得一样)
总之:
3.$ {}和 #{}的区别
使用MyBatis进行数据库操作在进行参数传递时,有#{ } 与 ${ }两种方式。
3.1. 以基本类型参数为例测试
以参数为Integer类型
在UserInfoMapper文件中创建selectOne方法,分别使用#{ } 与 ${ }传递参数:
1.先使用 #{ }进行赋值
测试:
看一下我们的日志:可以看到我们的参数被一个?占位(这个问号就是相当于一个占位符)
我们输入的参数并没有在后面拼接,id的值是使用? 占位. 这种SQL 我们称之为"预编译SQL"
2.使用 ${ } 进行赋值
所有都一样,只是把# 换成了 $,看一下我们的日志,可以看到,根本就没有占位符这一说法
这次的参数是直接拼接在SQL语句中了.一整个SQL语句都是等这个参数过来之后一起进行编译的
3.2.我们再看一下,传String类型时
日志1:
换成 ${ } 日志:直接报错了
可以看到, 这次的参数依然是直接拼接在SQL语句中了, 但是字符串作为参数时, 需要添加引号 '' , 使⽤ ${} 不会拼接引号 '' , 导致程序报错
修改如下:
毕竟外面有双引号(我们加上单引号就行了)(程序就正常跑起来了)
3.3总结二者的区别:
3.3.1.#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别.(预编译效率更高)
预编译SQL:
编译⼀次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同), 省去了解析优化等过程, 以此来提高效率
绝大多数情况下, 某⼀条 SQL 语句可能会被反复调用执行, 或者每次执行的时候只有个别的值不同(比如: select 的 where 字句值不同, update 的 set 字句值不同, insert 的 values 值不同). 如果每次都需要经过上面的语法解析, SQL优化、SQL编译等,则效率就明显不行了
图解
3.3.2 #{}更安全(防止SQL注入)
那什么是SQL注入呢?
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句(把SQL语句原来的意思改变了),以达到执行代码对服务器进行攻击的方法。(譬如本来我要查询一条语句,结果意思改变查询所有了)
原因:
由于没有对用户输⼊进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加⼀些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
譬如:
"select * from userinfo where username= '${name}' "
${ }变成 ' or 1='1(这个时候拼接成一个新的SQL语句):
select * from userinfo where username= '' or 1='1'(这个意思就是查询表里的所有数据)
3.4.SQL注入
演示SQL注入场景
UserInfoMapper中的写入(这里我只想要查询一条数据)
@Select("select username, `password`, age, gender, phone from userinfo where username= '${name}' ")List<UserInfo> queryByName(String name);
测试代码:
@Test
void queryByName() {
List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");
System.out.println(userInfos);
}
结果:
很明显的SQL注入(改变了原来的SQL语义)
可以看出来, 查询的数据并不是我们想要的数据. 所以用于查询的字段,尽量使用#{} 预查询的方式,SQL注⼊是⼀种非常常见的数据库攻击手段, SQL注⼊漏洞也是⽹络世界中最普遍的漏洞之⼀
如果发⽣在⽤⼾登录的场景中, 密码输⼊为 ' or 1='1 , 就可能完成登录(不是⼀定会发⽣的场景, 需要看登录代码如何写)
3.4.SQL注入导致登录BUG
模拟用户登录的场景
Controller层
@RequestMapping("/user")
@RestController
public class UserController {
//注入Mybatis生成的代理对象@Autowiredprivate UserInfoMapper userInfoMapper;@Autowiredprivate UserService userService;@RequestMapping("/login")public boolean login(String name, String password) {// 这里就是用户登录的界面,用户输入name和password// 接受service返回的用户信息UserInfo userInfo = userService.queryUserByPassword(name, password);// 如能查到这个用户(用户名和密码都对的话,就让这个用户成功登录)if (userInfo != null) {return true;}return false;}
}
Service层
@Service
public class UserService {//直接注入 这个Mapper对象@Autowiredpublic UserInfoMapper userInfoMapper;public UserInfo queryUserByPassword(String name, String password) {// 这里Service层就不做处理了,直接返回查询到的这个用户// 如能查到这个用户(用户名和密码都对的话,就让这个用户成功登录)return userInfoMapper.queryUserByPassword(name,password);}
}
Dao层
@Select("select * from userinfo where name = ${name} and password = ${password}")UserInfo queryUserByPassword(String name, String password);
然后测试:
正常结果:
.
错误结果:
如果我返回的是List,那么Controller层直接返回的就是True了(直接就登录成功了)
Controller层条件变成 if list==null 然后return false;
3.5.${} 也有自己的用处(有的地方 #{ } 完成不了)
从上面的例子中, 可以得出结论: ${} 会有SQL注入的风险, 所以我们尽量使用#{}完成查询 既然如此, 是不是 ${} 就没有存在的必要性了呢?
当然不是.下面是${ }的使用场景
Mapper实现
@Select("select id, username, age from userinfo order by id ${sort} ")List<UserInfo> queryAllUserBySort(String sort);
使用${sort} 可以实现排序查询, 而使用 #{sort} 就不能实现排序查询了.
注意: 此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 '' 的, 所以此时的 ${sort} 也不加引号
我们把 ${} 改成 #{}
@Select("select id, username, age from userinfo order by id #{sort} ")List<UserInfo> queryAllUserBySort1(String sort);
结果:
可以发现, 当使用 #{sort} 查询时,asc 前后自动给加了引号,导致 sql 错误。#{} 会根据参数类型判断是否拼接引号 ''如果参数类型为String,就会加上引号.
除此之外, 还有表名作为参数时, 也只能使用${}
3.5.like 查询
like 使用#{} 报错
把 #{} 改成 ${} 可以正确查出来, 但是${}存在SQL注入的问题(这可是where条件(' or 1='1)), 所以不能直接使用 ${}.
解决办法: 使用mysql 的内置函数 concat() 来处理,实现代码如下:
结果:
总之:
能使用 #{} 就不使用 ${} ,参数不需要加 ''(引号)的时候再用 ${}(譬如升降序) (还要确保不会产生SQL注入的问题,特别where条件的时候最容易产生SQL注入:譬如模糊查询)
4. 数据库连接池
在上面Mybatis的讲解中, 我们使用了数据库连接池技术, 避免频繁的创建连接, 销毁连接。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用⼀个现有的数据库连接,而不是再重新建立⼀个.(JDBC就需要我们每次和MySQL取得连接)
没有使用数据库连接池的情况: 每次执行SQL语句, 要先创建⼀个新的连接对象, 然后执行SQL语句, SQL语句执行完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接比较消耗资源
使用数据库连接池的情况: 程序启动时, 会在数据库连接池中创建⼀定数量的Connection对象, 当客户请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执行SQL, SQL语句执行完, 再把Connection归还给连接池.(省资源,效率高)
上述就是Mybatis的SQL编写—XML方式的全部内容啦,不知道您对文章中的问题和思想是否都学会理解了呢?
能看到这里相信您一定对小编的文章有了一定的认可。
有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~