手写 Mybatis,“整整” Mybatis源码

本文介绍了如何手写一个简单的Mybatis框架,通过分析传统JDBC的弊端,提出设计思路,包括使用端和框架端的实现。在实现过程中,详细解释了配置文件的获取和解析,SqlSession的具体操作,以及如何创建SqlSessionFactory。通过这个过程,有助于理解Mybatis源码的工作原理。
摘要由CSDN通过智能技术生成

前言

前两天写了一个 手写Spring ioc 框架,“撸撸”Spring 源码我们今天来整整Mybatis。

mybaits 在 ORM 框架中,可算是半壁江山了,由于它是轻量级,半自动加载,灵活性和易拓展性。深受广大公司的喜爱,所以我们程序开发也离不开 mybatis 。

很多朋友对 mybatis 源码没什么了解,或者想看但是不知道怎么看的苦恼吗?

归根结底,我们还是需要知道为什么会有 mybatis ,mybatis 解决了什么问题? 想要知道 mybatis 解决了什么问题,就要知道传统的 JDBC 操作存在哪些痛点才促使 mybatis 的诞生。 我们带着这些疑问,再来一步步学习吧。

内容稍微有点长!耐心阅读!

另外本人整理收藏了20年多家公司面试知识点整理 ,以及各种Java核心知识点免费分享给大家,下方只是部分截图
想要资料的话也可以点击直接进入:暗号:csdn,免费获取。

在这里插入图片描述

传统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 的源码就恍然大悟,这个地方这样配置原来是因为这样啊。

我们分为使用端和框架端两部分。

学海无涯,我们一起勉力前行!

Ps:有需要的小伙伴可以点击直接进入:暗号:csdn,免费获取。

在这里插入图片描述

使用端

我们在使用 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&amp;characterEncoding=utf8&amp;useUnicode=true&amp;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 配置文件,我们现在来解析它。 不过在此之前,我们需要做一点准备工作,就是解析的内存放到什么地方?

所以我们来创建两个实体类 Mapper 和 Configuration 。

Mapper Mapper 实体类用来存放使用端写的 mapper.xml 文件的内容,我们前面说了里面有 id、sql、resultType 和 paramType .所以我们创建的 Mapper 实体如下:

public class Mapper {
   
    private String id;
    private Class<?> resultType;
    private Class<?> parmType;
    private String sql;
    //getter()和 setter()方法
}

这里我们为什么不添加 namespace 的值呢? 聪明的你肯定发现了,因为 mapper 里面这些属性表明每个 sql 都对应一个 mapper , 而 namespace 是一个命名空间,算是 sql 的上一层,所以在 mapper 中暂时使用不到,就没有添加了。

Configuration Configuration 实体用来保存 SqlMapConfig 中的信息。所以需要保存数据库连接,我们这里直接用 JDK 提供的 DataSource 。还有一个就是 mapper 的信息。每个 mapper 有自己的标识,所以这里采用 hashMap 来存储。如下:

public class 
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值