目录
前言
mybaits 在 ORM 框架中,可算是半壁江山了,由于它是轻量级,半自动加载,灵活性和易拓展性。深受广大公司的喜爱,所以我们程序开发也离不开 mybatis 。但是我们有对 mabtis 源码进行研究吗?或者想看但是不知道怎么看的苦恼吗? 归根结底,我们还是需要知道为什么会有 mybatis ,mybatis 解决了什么问题? 想要知道 mybatis 解决了什么问题,就要知道传统的 JDBC 操作存在哪些痛点才促使 mybatis 的诞生。 我们带着这些疑问,再来一步步学习吧。
上面的一份详细的mybatis的学习思维导图已整理好,还有一些mybatis的技术资料文档,需要领取可以 点我领取 。
原始 JDBC 存在的问题
所以我们先来来看下原始 JDBC 的操作
我们知道最原始的数据库操作。分为以下几步
1、获取 connection 连接
2、获取 preparedStatement
3、参数替代占位符
4、获取执行结果 resultSet
5、解析封装 resultSet 到对象中返回。
如下是原始 JDBC 的查询代码,存在哪些问题?
public static void main(String[] args) {
String dirver="com.mysql.jdbc.Driver";
String url="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8";
String userName="root";
String password="123456";
Connection connection=null;
List<User> userList=new ArrayList<>();
try {
Class.forName(dirver);
connection= DriverManager.getConnection(url,userName,password);
String sql="select * from user where username=?";
PreparedStatement preparedStatement=connection.prepareStatement(sql);
preparedStatement.setString(1,"张三");
System.out.println(sql);
ResultSet resultSet=preparedStatement.executeQuery();
User user=null;
while(resultSet.next()){
user=new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
userList.add(user);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (!userList.isEmpty()) {
for (User user : userList) {
System.out.println(user.toString());
}
}
}
小伙伴们发现了上面有哪些不友好的地方?
我这里总结了以下几点:
1、数据库的连接信息存在硬编码,即是写死在代码中的。 2、每次操作都会建立和释放 connection 连接,操作资源的不必要的浪费。 3、sql 和参数存在硬编码。 4、将返回结果集封装成实体类麻烦,要创建不同的实体类,并通过 set 方法一个个的注入。
存在上面的问题,所以 mybatis 就对上述问题进行了改进。 对于硬编码,我们很容易就想到配置文件来解决。mybatis 也是这么解决的。 对于资源浪费,我们想到使用连接池,mybatis 也是这个解决的。 对于封装结果集麻烦,我们想到是用 JDK 的反射机制,好巧,mybatis 也是这么解决的。
设计思路
既然如此,我们就来写一个自定义持久层框架,来解决上述问题,当然是参照 mybatis 的设计思路,这样我们在写完之后,再来看 mybatis 的源码就恍然大悟,这个地方这样配置原来是因为这样啊。
我们分为使用端和框架端两部分。
使用端
我们在使用 mybatis 的时候是不是需要使用 SqlMapConfig.xml 配置文件,用来存放数据库的连接信息,以及 mapper.xml 的指向信息。mapper.xml 配置文件用来存放 sql 信息。
所以我们在使用端来创建两个文件 SqlMapConfig.xml 和 mapper.xml。
框架端
框架端要做哪些事情呢?
如下:
1、获取配置文件。也就是获取到使用端的 SqlMapConfig.xml 以及 mapper.xml 的文件
2、解析配置文件。对获取到的文件进行解析,获取到连接信息,sql,参数,返回类型等等。这些信息都会保存在 configuration 这个对象中。
3、创建 SqlSessionFactory,目的是创建 SqlSession 的一个实例。
4、创建 SqlSession ,用来完成上面原始 JDBC 的那些操作。
那在 SqlSession 中 进行了哪些操作呢?
1、获取数据库连接
2、获取 sql ,并对 sql 进行解析
3、通过内省,将参数注入到 preparedStatement 中
4、执行 sql
5、通过反射将结果集封装成对象
使用端实现
好了,上面说了一下,大概的设计思路,主要也是仿照 mybatis 主要的类实现的,保证类名一致,方便我们后面阅读源码。我们先来配置好使用端吧,我们创建一个 maven 项目。
在项目中,我们创建一个 User 实体类
public class User {
private Integer id;
private String username;
private String password;
private String birthday;
//getter()和 setter()方法
}
创建 SqlMapConfig.xml 和 Mapper.xml SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false"></property>
<property name="userName" value="root"></property>
<property name="password" value="123456"></property>
<mapper resource="UserMapper.xml">
</mapper>
</configuration>
可以看到我们 xml 中就配置了数据库的连接信息,以及 mapper 一个索引。mybatis 中的 SqlMapConfig.xml 中还包含其他的标签,只是丰富了功能而已,所以我们只用最主要的。
mapper.xml 是每个类的 sql 都会生成一个对应的 mapper.xml 。我们这里就用 User 类来说吧,所以我们就创建一个 UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="cn.quellanan.dao.UserDao">
<select id="selectAll" resultType="cn.quellanan.pojo.User">
select * from user
</select>
<select id="selectByName" resultType="cn.quellanan.pojo.User" paramType="cn.quellanan.pojo.User">
select * from user where username=#{username}
</select>
</mapper>
可以看到有点 mybatis 里面文件的味道,有 namespace 表示命名空间,id 唯一标识,resultType 返回结果集的类型,paramType 参数的类型。 我们使用端先创建到这,主要是两个配置文件,我们接下来看看框架端是怎么实现的。
加油哈哈。
框架端实现
框架端,我们按照上面的设计思路一步一步来。
获取配置
怎么样获取配置文件呢?我们可以使用 JDK 自带自带的类 Resources 加载器来获取文件。我们创建一个自定义 Resource 类来封装一下:
import java.io.InputStream;
public class Resources {
public static InputStream getResources(String path){
//使用系统自带的类 Resources 加载器来获取文件。
return Resources.class.getClassLoader().getResourceAsStream(path);
}
}
这样通过传入路径,就可以获取到对应的文件流啦。
解析配置文件
上面获取到了 SqlMapConfig.xml 配置文件,我们现在来解析它。 不过在此之前,我们需要做一点准备工