「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战
上一节给大家去介绍了什么是框架,为什么要使用框架以及常见的框架都有哪些。
这一小节我们就正式来介绍关于 Mybatis 的相关概念。当然在介绍 Mybatis 相关概念之前,我们要先来回顾一下原始 JDBC 操作。我们在之前的 Web 阶段,我们的持久层技术选型是不是一直都采用 JDBC。说到这个 JDBC,先请大家去思考一个问题了,什么问题呢?就是我们已经可以使用 JDBC 来完成数据库数据的增删改查操作了,那为什么后续还会再出现一个 Mybatis 框架,大家思考一下,为什么后续还会再出现一个 Mybatis 框架呢?
是不是意味着在我们去使用原始 JDBC,在对数据库数据进行操作时,它本身还是存在一些问题的。所以我们就先来分析一下在使用原始的 JDBC 操作数据库,它都存在哪些问题,并对这些问题来给出一个解决思路,从而来引出 Mybatis 框架。
基于原始 JDBC 链接数据库
那我们现在先来看看一下有关于 JDBC 相关代码。我们先简单回顾一下在我们去编写 JDBC 的相关代码时,它的步骤相对来说比较固定。
private void testJDBC() {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String url = "jdbc:mysql://localhost:3306/db_mybatis?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "root";
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection(url, username, password);
String sql = "select * from t_user";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery(sql);
while (resultSet.next()) {
//System.out.println(resultSet.getInt("id")+'-'+resultSet.getString("username")+','+resultSet.getString("password"));
UserEntity userEntity = new UserEntity();
userEntity.setId(resultSet.getInt("id"));
userEntity.setUsername(resultSet.getString("username"));
userEntity.setPassword(resultSet.getString("password"));
System.out.println(userEntity);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (resultSet == null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement == null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection == null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第一步:先来完成注册驱动。
第二步:并且来获取到一个数据库链接。
第三步:拿到数据库链接之后,编写我们要执行的 SQL,并获取到一个 preparedStatement 预编译对象。
第四步:接下来执行 SQL,拿到一个 resultset。这个 resultset 就是我们的一个查询结果集。
第五步:接下来我们对结果集进行处理,也就是遍历这个结果。
第六步:集遍历的过程中,再取出每一个值,封装成一个实体,并且输出结果。
第七步:最后完成资源的释放。
这个就是我们编写 JDBC 比较固定的这么一个步骤。
分析问题
接下来我们就对代码进行一个分析,看一下它都存在什么样的问题。
我们先来看第一步和第二步,这两步的代码存在什么问题呢?大家想一下,通过前面的学习,详细大家一眼就能看得出来,当前这两步代码其实存在一个数据库配置信息硬编码问题。也就是我们在注册驱动时所编写的驱动类,它现在是被写死在这个代码中以及我们在获取连接时,这个连接信息当前也是被写死在代码中的。大家想,我们当前底层数据库连接的是 MySQL 数据库,当前的配置信息是没有问题。如果一旦我们现在底层的数据库由 MySQL 数据库切换成 Oracle 数据库或者其他关系型数据,那么这个驱动类以及连接的配置信息是不是都要发生改变。而当前这个配置类以及我们的一个连接配置信息是被写死在我们的程序中的。我们现在想对它进行修改,我们就要去修改这个源代码。而一旦源代码发生了修改,那我们的项目就要重新的打包部署,所以这是非常麻烦的。
除了这个问题以外,大家还能不能再去分析出一个其他的问题。大家还要在想,我们当前两步的 JDBC 代码,它是被编写在哪?它是不是经常要被编写在一个持久层的方法中,而一个持有层的方法,它是可能会被调用多次的。testJDBC() 方法举例,当这个方法它被重复调用多次,每一次它被调用时,它都怎么样呢?它都要重新来获取一个数据库连接,经过执行 SQL 之后,它再把这个链接释放掉。如果一旦这个方法它被调用多次,那么也就意味着这个连接它是不断的在被频繁的创建和释放掉的。大家要知道对于数据库连接,它其实是一个非常宝贵的资源,同时也是很耗费资源的。因为我们在获取数据库的连接时,它的底层要进行 TCP 连接,要进行三段式握手。所以如果我们频繁的去创建释放数据库连接的话,是会造成资源的浪费,会影响系统的性能的。所以第二个问题,那么就是频繁创建释放数据库连接。
同时,第三步也是存在 SQL 语句硬编码问题。当前 SQL 语句也是写死在程序中了。而对于 SQL 语句来说,可能会经常发生改变,所以把经常发生改变的内容直接写死在程序中,肯定是不好的。
第四步、第五步和第六步,大家看有没有什么问题呢?这部分其实我们可以分析出来的问题是,在执行完 SQL 语句获取到 resultSet 之后,我们做了什么事呢?我们是对 resultSet 进行了遍历,遍历的过程中我们去 new 一个 userEntity 对象,然后再向 userEntity 对象里面的每一个属性来设置值。这个值就是从 resultSet 中获取到的,也就是部分它存在的问题是需要我们手动封装返回结果集。这个结果集它封装起来是比较麻烦的。因为一旦我们这个 userEntity 里面有多个属性,比如它对应的这张表有的这张表里面有多个字段,假设有 100 个字段,那难道我要手动设置 100 属性值吗?这个肯定是比较麻烦。
所以经过我们当前的分析,我们现在可以分析出如下问题:
- 数据库配置信息硬编码问题。
- 频繁创建释放数据库链接。
- 存在 SQL 语句硬编码问题。
- 手动动封装结果集。
以上是我们当前分析出来的问题。针对于这些问题,大家想一下有没有什么解决方案或者一个解决思路。
解决思路
当前数据库配置信息硬编码问题。其实大家一看到硬编码能够想到的一个解决方案,就是用配置文件来解决。也就是把硬编码这一部分信息要抽取到配置文件中。所以这个就是解决硬编码的一个思路。
还有就是频繁创建释放数据库连接。针对于这个问题,我们现在能够想到的一个解决方案。可以采用数据库连接池来解决掉频繁创建释放数据库连接问题。
存在 SQL 语句硬编码引起,又是一个硬编码。一看到硬编码问题,那就要想到配置文件,所以我们同样的也可以再采用配置文件来解决 SQL 语句硬编码问题。
针对手动封装返回结果问题。我们怎么解决呢?其实我们现在可以去采用反射。大家之前接触过反射,采用 反射 + 内省来解决手动封装结果。借助这样的技术,去完成查询结果与我们实体对象属性的自动映射。
这个就是我们当前针对使用原始 JDBC 分析出来的一些问题所能给出的一个解决思路。
大家现在能够知道,既然使用 JDBC 去操作数据库,存在这么多问题的。那我们接下来就要怎么办呢?我们接下来就要去针对于这些问题啊进行一个解决。其实我们现在也不需要自己去解决这些问题了,此时我们就要去引出谁呢?我们现在就是要去引出 MyBatis 框架。
Mybatis 是一个持久层框架,它本身就是对原始 JDBC 进行了封装。我们在使用 Mybatis 的过程中,不但可以简化开发,同时它也把原始 JDBC 操作数据库所存在的这些问题规避。
对于这一小节,大家先简单去知道,当我们使用原始的 JDBC 操作数据库,它都存在哪些问题,以及一个解决思路。