夺命连环问——Mybatis篇上
1、什么是Mybatis/简单说说Mybatis?
答:
Mybatis是一个持久层框架,封装了原始的JBDC程序。我们只需要关心sql语句的编写就行了,至于参数的处理、结果的封装这些都由框架来完成。
2、ORM框架是什么?有什么优点?说说你知道的ORM框架。
答:
ORM[Object Relationship Mapping 对象关系映射]框架是用于将面向对象的程序和关系型数据库之间进行数据转换和映射的持久层框架。
优点:
简化开发、便于维护、提供多种数据库的支持。
常用的持久层框架有:
MyBatis、Hibernate、Spring Data JPA
拓展:
使用ORM框架的好处包括:
- 简化开发:ORM框架通过自动化数据库操作和对象映射,减少了开发人员编写重复、繁琐的SQL语句的工作量,简化了数据访问层的开发过程。
- 提高可维护性:ORM框架提供了良好的抽象和封装,使代码更易于理解和维护。开发人员可以专注于业务逻辑,而无需关注底层数据库细节。
- 跨数据库支持:ORM框架通常提供对多种数据库的支持,可以方便地切换或兼容不同的数据库系统,降低了数据库平台的耦合性。
- 对象导航和关联操作:ORM框架允许开发人员使用对象导航和关联操作来处理数据之间的关系,使关系型数据库的数据操作更符合面向对象的思维方式。
常见的持久层框架包括以下几种:
- Hibernate:Hibernate是一个开源的、基于Java的ORM框架,它提供了高效的对象关系映射、查询语言和事务管理等功能。Hibernate广泛用于Java应用程序的数据持久化开发。
- MyBatis:MyBatis是一个简化的持久层框架,它通过将SQL语句与Java代码分离,提供了灵活的SQL映射配置和强大的动态SQL支持。MyBatis以其简单性和可定制性而受到广泛使用。
- Spring Data JPA:Spring Data JPA是Spring框架的一部分,它提供了简化的方式来访问关系型数据库。Spring Data JPA基于JPA(Java Persistence API)规范,通过注解和自动生成的查询方法,减少了开发人员编写重复的数据访问代码。
3、ORM[Object Relationship Mapping 对象关系映射]框架能对非关系型数据库进行映射吗?
答:
ORM主要是将对象模型与关系数据库之间进行映射,但一些ORM框架也提供了对非关系型数据库的支持,比如Hibernate、Spring Data和MongoDB的Object-Document Mapping (ODM)等,提供了对非关系型数据库的支持。它们可以将文档型数据库(如MongoDB)中的文档映射到Java对象,并提供方便的CRUD操作和查询功能。
4、JDBC是什么?有什么用?使用JBDC有什么问题?怎么解决的?你有研究过它的底层原理吗?
答:
JDBC(Java Database Connectivity),就是使用Java语言操作关系型数据库的一套API。
提供了一种通用的方式来连接、查询和更新数据库。
研究过|没研究过。
问题:
- 数据库链接的四要素(驱动、链接、用户名、密码)全部硬编码在java代码中
- 查询结果的解析及封装非常繁琐
- 每一次查询数据库都需要获取连接,操作完毕后释放连接, 资源浪费, 性能降低
解决方案:使用Mybatis
- 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件 application.yml中
- 查询结果的解析及封装,由mybatis自动完成映射封装。
- 在mybatis中使用了数据库连接池技术,来避免频繁的创建、销毁连接而带来的资源浪费。
拓展:
以下是一些JDBC的主要用途:
- 连接数据库:JDBC提供了用于建立与数据库之间连接的类和方法。开发人员可以通过JDBC连接到数据库,并使用数据库提供的功能执行各种操作。
- 执行SQL查询和更新:JDBC允许开发人员执行SQL查询和更新操作,例如SELECT、INSERT、UPDATE和DELETE语句。通过JDBC,可以将这些操作嵌入到Java应用程序中,与数据库进行交互并处理结果。
- 处理结果集:JDBC提供了处理查询结果集的方法。开发人员可以使用JDBC检索和处理从数据库返回的数据,并将其转换为Java对象进行进一步处理。
- 事务管理:JDBC支持事务管理,允许开发人员在多个数据库操作之间进行事务控制。开发人员可以使用JDBC的事务功能确保数据库操作的一致性和完整性。
5、刚才提到了数据库连接池,来谈一谈它。数据库连接池技术的好处有哪些?它的原理是什么?
答:
数据库连接池本质是一个容器,运用了池化思想,开发中默认用的是追光者(Hikari),也可以选择性能更优的Druid(德鲁伊)。
最大的特点是实现Connection对象的复用。它通过对连接对象的空闲时间和连接池中预设的最大空闲时间的比较来判断是否自动释放这个连接对象。当大于的时候连接池就自动释放。这样可以避免因为没有释放连接而引起的数据库连接遗漏。
-
程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象
-
允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
客户端在执行SQL时,先从连接池中获取一个Connection对象,然后在执行SQL语句,SQL语句执行完之后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)
-
释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象
数据库连接池的好处:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
6、谈一谈JDBC的底层原理。
答:
JBDC的底层主要是三个接口对象,Connection、Statement、ResultSet。
Connection用于建立与数据库的连接,Statement用于向数据库发送sql语句,ResultSet用于封装sql查询语句的结果。
原始的JDBC操作数据库主要有以下几个步骤:
1.注册驱动
使用 Class.forName() 方法加载数据库驱动程序类。
2.获取连接对象
JDBC的底层其实是使用Socket进行连接数据库的。
3.执行SQL语句,返回执行结果
通过获取Statement实例执行SQL语句。
4.处理执行结果
最后返回的结果集是ResultSetImpl。
5.释放资源
拓展:
简单实现JDBC的代码
import com.itheima.pojo.User;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class JdbcTest {
@Test
public void testJdbc() throws Exception {
//1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取数据库连接
String url="jdbc:mysql://127.0.0.1:3306/mybatis";
String username = "root";
String password = "1234";
Connection connection = DriverManager.getConnection(url, username, password);
//3. 执行SQL
Statement statement = connection.createStatement(); //操作SQL的对象
String sql="select id,name,age,gender,phone from user";
ResultSet rs = statement.executeQuery(sql);//SQL查询结果会封装在ResultSet对象中
List<User> userList = new ArrayList<>();//集合对象(用于存储User对象)
//4. 处理SQL执行结果
while (rs.next()){
//取出一行记录中id、name、age、gender、phone下的数据
int id = rs.getInt("id");
String name = rs.getString("name");
short age = rs.getShort("age");
short gender = rs.getShort("gender");
String phone = rs.getString("phone");
//把一行记录中的数据,封装到User对象中
User user = new User(id,name,age,gender,phone);
userList.add(user);//User对象添加到集合
}
//5. 释放资源
statement.close();
connection.close();
rs.close();
//遍历集合
for (User user : userList) {
System.out.println(user);
}
}
}
感兴趣的同学可以参考文章——(1条消息) JBDC底层原理解析_如果我是枫的博客-CSDN博客
7、Mybatis中#{}和${}的区别是什么?各有哪些特点?
答:
这两个都是用于mybatis封装参数的,
我们开发中大多数使用的都是#{},进行参数的封装。相对于 ${} ,#{} 可以防止sql注入。
#占位符的特点:
1、#{} 使用 ? 占位符的形式,底层使用的 PreparedStament生成预编译SQL,执行sql语句的效率更高,而且能够避免sql注入,使得sql语句的执行更加安全。
2、使用时机:参数传递,都使用#{…}
$占位符的特点 :
1、 ${ } 使用的是字符串连接的方式,底层使用的Statement 对象,执行sql语句的效率相对于 #{ } 占位符要更低。有 sql 注入的风险,同时也存在代码安全的问题。
2、${ } 占位符中的数据是原模原样的,不会区分数据类型。
3、使用时机:如果对表名、列表进行动态设置时使用
静态SQL语句,通常通过Statement实例实现。
动态SQL语句,通常通过PreparedStatement实例实现。
拓展:
JDBC(Java DataBase Conection)核心对象回顾:
Conection:连接对象
Stament |PrepareStatement:执行sql语句的
ResultSet:封装查询结果集的
8、#{}里面能传变化的值吗?
可以传变化的值,但一些情况是不行的。
#{}传参的举例:
select * from tb_user where username = #{username}
例如: username 参数的取值为 ‘zhangsan’ or 1=1
首先预编译sql语句:select * from tb_user where username = ?
然后将问号占位符替换为参数值,最终sql如下:
select * from tb_user where username = "'zhangsan' or 1=1"
9、为什么预编译后参数值带引号?为什么用预编译?
在预编译过程中,查询参数被视为字符串值,并将其作为文本进行处理。为了确保查询参数的正确解析和匹配,引号用于标识字符串值。引号的使用可以确保参数的类型和格式正确,避免解析错误或数据损坏。
预编译SQL有两个优势:
- 性能更高
- 更安全(防止SQL注入)
性能更高:预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译。(只是输入的参数不同)
更安全(防止SQL注入):将敏感字进行转义,保障SQL的安全性。
10、什么时候不能用#{ }而用${ }?
常见的使用${}的情况:
1.当sql中表名不固定情况 。
2.当sql中查询字段不固定。
变化的表名和变化的字段名不能加#{}
${} sql语句中关键字部分,需要通过参数传递,此时必须使用。
场景一:表名不固定:
例如我有一张用户统计表,是按照日期进行划分的,每天一张表,表明规则如下:
tb_statics_user_20221107 tb_statics_user_20221108 tb_statics_user_20221109 tb_statics_user_20221110
如果我们要查询每日用户统计数据,那我们的表名是随着日期变化的,如下
使用#{}传参是不行的:
==> select * from tb_statics_user_#{datetime}
预编译==> select * from tb_statics_user_? 例如:datetime = 20221107
==> select * from tb_statics_user_'20221107' 错误的sql语句
表名中不可能存在引号这种关键字,这样是当成固定值了。
使用${}传参是可以的
==> select * from tb_statics_user_${datetime} 20221107
==> select * from tb_statics_user_20221107 正确的sql语句
场景二、查询字段不固定
==> select #{cols} from tb_xxx 例如: cols = username,password,age
==> select ? from tb_xxx
==> select 'username,password,age' from tb_xxx 错误的sql语句
==> select ${cols} from tb_xxx 例如: cols = username,password,age
==> select username,password,age from tb_xxx 正确的sql语句
11、sql注入问题是什么?
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
${}传参举例【就是sql的字符串拼接】:
select * from tb_user where username = ${username} 例如: username 参数的取值为 'zhangsan' or 1=1
参数变量直接和sql字符串拼接在一起,最终sql如下: select * from tb_user where username = 'zhangsan' or 1=1
注意:此处就发生了sql注入的问题了,因为上面的sql执行会查询出用户表的所有数据,而不再是根据应用户名进行查询。
12、对于字段很多的大宽表应该什么处理?
对于字段很多的大宽表我们必须使用${},但是又会存在安全风险,此时我们可以在代码中对参数进行校验,如果包含一些非法的sql关键字,就不继续执行代码。