mybatis源码分析

一、ORM 框架简介

对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。

二、Mybatis

官方文档(Mybatis官方文档)中有这么一句话 :

什么是 MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

作用域(Scope)和生命周期

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式

SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。
映射器实例

映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // 你的应用逻辑代码
}

三、分析事例

接下来我们通过分析一个简单的mybatis的例子,来分析mybatis的运行过程,例子如下

        String resource = "mybatis-config.xml";
        Reader inputStream = null;
        try {
           //使用了门面模式(包装) 定义了一个Resource类  包装了 jdk中 InputStreamReader对象 做io操作 获取到一个Reader输入流
           inputStream = Resources.getResourceAsReader(resource);
           SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
           SqlSession  sqlSession = sqlSessionFactory.openSession();
           IBlogMapper blogMapper = sqlSession.getMapper(IBlogMapper.class); 
           List<blog> blogs = blogMapper.queryAll();
           System.out.println(blogs.size());
           sqlSession.close();
       } catch (IOException e) {
           e.printStackTrace();
       }
  • mybatis的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <properties resource="db.properties"/>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
         <package name="nuc.yznl.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
        <mappers>
            <mapper resource="nuc/yznl/pojo/blogMapper.xml" />
        </mappers>
</configuration>
  • 对应的接口与接口映射的xml文件
package nuc.yznl.pojo;
import java.util.HashMap;
import java.util.List;

public interface IBlogMapper {
 
    List<blog>  queryAll();
    blog selectBlogById(int id);
}
<?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.xml映射文件的唯一标识-->
<mapper namespace="nuc.yznl.pojo.IBlogMapper">

<!--    输入参数是基本类型 占位符号可以是任意的-->
    <select id="selectBlogById" resultType="nuc.yznl.pojo.blog" parameterType="int">
      select * from blog where id = #{id}
    </select>

    <select id="queryAll" resultType="nuc.yznl.pojo.blog">
      select * from blog
    </select>
    <cache type= "nuc.yznl.cache.MybatisRedisCache" ></cache>
</mapper>
  • 实体类
package nuc.yznl.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class blog implements Serializable {
    private String id;
    private String title;
    private String authOr;
    private String create_time;
    private Integer views;
    private Boolean sex;
}

四、分析mybatis的运行过程

我们分析的过程,主要是通过断点调试,来逐步分析程序的运行过程,这里我们将分成五个部分来讲,对应运行程序的五行,废话不多说,开始分析!

1:   String resource = "mybatis-config.xml";
      Reader inputStream = null;
      inputStream = Resources.getResourceAsReader(resource);
2:  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
3:  SqlSession  sqlSession = sqlSessionFactory.openSession();
4:  IBlogMapper blogMapper = sqlSession.getMapper(IBlogMapper.class); 
5:  List<blog> blogs = blogMapper.queryAll();
  • inputStream = Resources.getResourceAsReader(resource);
    首先我们要使用mybatis,我们第一步先是要配置mybatis的核心配置文件 mybatis-config,我们第一步就是将我们的配置文件,转换为输入流的形式,将配置文件加载到内存中
    在这里插入图片描述这里的charset是设置字符集的意思,我们将mybatis-config.xml这个配置文件,以IO流的形式读入

在这里插入图片描述

这里的类加载器是null
在这里插入图片描述在这里插入图片描述

  • SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

这一步是mybatis的核心步骤,我们通过得到配置文件的流,将mybatis-config.xml文件转换为我们加载到内存中的Configuration类,并且逐步去解析我们的配置我们的xml文件(包括了映射文件blogMapper.xml ),接口类等等…之后创建出我们生成SqlSession的工厂。
在这里插入图片描述创建SqlSessionFactory工厂,并且调用build 方法
在这里插入图片描述这里我们创建了XMLConfigBuilder类,这个类负责解析我们的配置文件和接口,调用方法parse来解析

在这里插入图片描述在这里插入图片描述

我们进入到这个类中,可以看到构造方法中的 parsed变量,在类初始化的时候,赋值为false,这里的目的是防止,多次解析我们的mybatis-config文件,并且在这个时候,创建出我们最关键的配置类,configuration
在这里插入图片描述从配置文件的根节点开始解析
在这里插入图片描述通过parseConfiguration我们来解析,我们将配置文件中的每一个节点逐个进行解析,我们以mapper节点为例子
在这里插入图片描述我们知道在mybatis中的mapper节点,来标识我们的配置文件 ,它有两种方式一种是以包的形式,package,一种是以resource的方式,在mybatis中它都进行了处理
在这里插入图片描述

这里我们创建出另一个用来解析xml的类,XMLMapperBuilder
在这里插入图片描述在这里插入图片描述在这里插入图片描述这里的XMLConfigBuilder也是和XMLMapperBuilder同样继承BaseBuilder

在这里插入图片描述调用parse方法 进行解析我们的xml
在这里插入图片描述
configuration.isResourceLoaded(resource) 查看我们的configuration 是否已经加载过这个xml,这里的保存使用的是Set集合
在这里插入图片描述
在这里插入图片描述

在这里我们加载我们的二级缓存
在这里插入图片描述
在这里插入图片描述bindMapperForNamespace()方法 用来将我们blogMapper.xml中的信息与我们写的接口IBlogmapper对应起来
在这里插入图片描述addMapper是用来添加我们的接口信息的,在Configuration中封装了一个对象mapperRegistry,在该对象中封装了一个Map集合存放我们的接口信息
在这里插入图片描述
在这里插入图片描述在这里插入图片描述至此我们的Mapper解析完成,回到我们XMLBuilder
在这里插入图片描述这时候我们已经解析完成我们的mybatis-config文件, 调用build方法
在这里插入图片描述这时候我们才创建了DefaultSqlSessionFactory
在这里插入图片描述
在这里插入图片描述

  • SqlSession sqlSession = sqlSessionFactory.openSession();

在这里插入图片描述在我们的DefaultSqlSessionFactory中创建 我们的DefaultSqlSession
在这里插入图片描述在这里插入图片描述在创建DefaultSqlSession的同时,我们也需要创建执行器,用来执行Sql的类
在这里插入图片描述cacheEnabled就是我们在mybatis-config.xml中配置的二级缓存,默认是开启的
在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • IBlogMapper blogMapper = sqlSession.getMapper(IBlogMapper.class);
    在这里插入图片描述在getMapper方法中,我们创建我们的代理对象,来真正执行我们接口中的方法,这里的代理在mybatis源码中,使用的是jdk动态代理技术,如果对动态代理不清除的可以看一下这篇博客: java静态代理模式和动态代理模式(深入源码)
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在MapperProxyFactory中,我们创建我们的代理对象
    在这里插入图片描述在这里插入图片描述
  • List blogs = blogMapper.queryAll();
    现在我们有了代理对象,我们调用接口中的方法时候,代理类就会替我们去执行方法,调用到代理类中的invoke()方法
    在这里插入图片描述在这里插入图片描述在MapperProxy中methodCache 用来做方法的缓存
    在这里插入图片描述在这里插入图片描述mapperMethod 方法中存放的是我们调用方法的一些信息,并且将这个对象存放在methodCache这个方法缓存中
    在这里插入图片描述在MapperMethod中进行方法的调用
    在这里插入图片描述调用我们select * from blog
    在这里插入图片描述在这里插入图片描述回到DefaultSqlSession
    在这里插入图片描述在这里插入图片描述在这里插入图片描述调用完成,返回我们sql的结果集
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值