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配置文件--\>
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

XML 复制代码
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中的写入(这里我只想要查询一条数据)

java 复制代码
 @Select("select username, `password`, age, gender, phone from userinfo where username= '${name}' ")
 List<UserInfo> queryByName(String name);

测试代码:

java 复制代码
@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层

java 复制代码
@RequestMapping("/user")
@RestController
public class UserController {
//注入Mybatis生成的代理对象
    @Autowired
    private UserInfoMapper userInfoMapper;
    
    @Autowired
    private 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层

java 复制代码
@Service
public class UserService {

    //直接注入  这个Mapper对象
    @Autowired
    public UserInfoMapper userInfoMapper;

    public UserInfo queryUserByPassword(String name, String password) {
        // 这里Service层就不做处理了,直接返回查询到的这个用户
        // 如能查到这个用户(用户名和密码都对的话,就让这个用户成功登录)
        return userInfoMapper.queryUserByPassword(name,password);
    }
}

Dao层

java 复制代码
 @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实现

java 复制代码
 @Select("select id, username, age from userinfo order by id ${sort} ")
    List<UserInfo> queryAllUserBySort(String sort);

使用${sort} 可以实现排序查询, 而使用 #{sort} 就不能实现排序查询了.

注意: 此处 sort 参数为String类型, 但是SQL语句中, 排序规则是不需要加引号 '' 的, 所以此时的 ${sort} 也不加引号

我们把 ${} 改成 #{}

java 复制代码
 @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方式的全部内容啦,不知道您对文章中的问题和思想是否都学会理解了呢?

能看到这里相信您一定对小编的文章有了一定的认可。

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~
您的支持就是我最大的动力!!!

相关推荐
NullPointerExpection几秒前
LLM大语言模型不适合统计算数,可以让大模型根据数据自己建表、插入数据、编写查询sql统计
数据库·人工智能·sql·算法·llm·llama·工作流
玩代码2 分钟前
观察者设计模式
java·设计模式·观察者设计模式
m0_6203551916 分钟前
线程学习day1---基础知识+pthread_create、self、exit、cancle、join
java·开发语言
武子康39 分钟前
Java-74 深入浅出 RPC Dubbo Admin可视化管理 安装使用 源码编译、Docker启动
java·分布式·后端·spring·docker·rpc·dubbo
Boop_wu1 小时前
【Java SE】抽象类
java·开发语言
lemon3106241 小时前
Linux Java环境配置
java·开发语言
Brookty1 小时前
Java线程创建与运行全解析
java·开发语言·后端·学习·java-ee·线程
野生程序员y2 小时前
spring容器的bean是单例还是多例的?线程安全吗?
java·后端·spring
bing_1582 小时前
我写的 @Service 类就是一个 Bean 吗?
spring·bean·ioc
星辰大海的精灵2 小时前
Java 线程池的工作原理及实践
java·后端·架构