MyBatis核心组件以及运行原理

概要

想要全面深入的理解mybatis框架,首要前提是学习它的核心组件,我们先来看看MyBatis的“表面现象”——mybatis的核心组件包括:SqlSessionFactoryBuilder(构造器)、SqlSessionFactory(工厂接口)、SqlSession(会话接口)、SQL Mapper(映射器)。通过一张图来整体展示四个核心组件的关系:
在这里插入图片描述

构建SqlSessionFactory过程

使用MyBatis首先是使用配置或者代码去生成SqlSessionFactory,而MyBatis提供了构造器SqlSessionFactoryBuilder,它采用Builder模式来负责构建SqlSessionFactory,通过源码分析,该类下提供了多个build的重载方法。
在这里插入图片描述
但真正重载build方法只有三种:InputStream(字节流)、Reader(字符流)、Configuration(类)。在MyBatis中既可以通过读取XML文件的形式生成SqlSessionFactory,也可以通过Java代码的形式去生成SqlSessionFactory。一般我们常用的是读取XML配置文件的形式。读取XML配置文件的方式构造SqlSessionFactory,构造过程中注入了Configuration的实例对象,之后Configuration实例对象解析XML配置文件来构建SqlSessionFactory:

  SqlSessionFactory sqlSessionFactory = null;
  String resource = "mybatis-config.xml";
        try {
   
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
   
            e.printStackTrace();
        }

在MyBatis读取XML配置文件后,通过Configuration类对象构建整个MyBatis的上下文,而Configuration采用的是单例模式 (SqlSessionFactory唯一的作用就是生产SqlSession,所以它的责任是唯一的)几乎所有的MyBatis配置都存放在这个单例对象中,以便后续使用。

在MyBayis中SqlSessionFactory是一个接口,它存在两个实现类:和DefaultSqlSessionFactory。SqlSessionManager多使用在多线程环境中。它的具体实现依靠的是DefaultSqlSessionFactory,它们之间的关系如下:
在这里插入图片描述
这种创建方式是一种Builder模式,对于复杂的对象而言,使用构造参数很难实现。这时使用一个类(Configuration)作为统领,一步步地构建所需的内容,然后通过它去创建最终的对象(SqlSessionFactory)。

在构建SqlSessionFactory中,Configuration是最重要的,它的作用是:

  • 读入配置文件。
  • 初始化一些配置。
  • 提工单例,为后续创建SessionFactory服务提供配置参数。
  • 执行一些重要对象的初始化方法。

显然Configuration不是一个简单的类,MyBatis的配置信息都来源于此,Configuration是通过XMLConfigBuilder去构建的,它会读出所有的XML配置信息,把它们解析并保存在Configuration单例中。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
   
    try {
   
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        return build(parser.parse()); // 开始进行解析了 :)
    } catch (Exception e) {
   
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
   
        ErrorContext.instance().reset();
        try {
   
            inputStream.close();
        } catch (IOException e) {
   
        }
    }
}

当XMLConfigBudiler解析XML时,会将每一个SQL和其配置的内容保存起来,在MyBatis中一条SQL和它相关的配置信息是由3部分组成的,它们分别是MappedStatement、SqlSource和BoundSql

  • MappedStatement MappedStatement维护一条<select|update|delete|insert>节点的封装
  • SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql 表示动态生成的SQL语句以及相应的参数信息

通过以上分析,知道了MyBatis会根据文件流生成Configuration对象,进而构建SqlSessionFactory对象得到SqlSession对象了。得到SqlSession对象后就是去执行sql语句。

SqlSession构建

以上我们构建了SqlSessionFactory,在MyBtis中SqlSession是其核心接口,它有两个实现类,DefaultSqlSession和SqlSessionManager。DefaultSqlSession是单线程使用的,而SqlSessionManager在多线程环境下使用。SqlSession的作用类似于JDBC中的Connection对象,代表着一个连接资源的启用,它的作用:

  • 获取Mapper接口。
  • 发送SQL给数据库。
  • 控制数据库事物。

前面我们构建了SqlSessionFactory,有了SqlSessionFactory创建的SqlSession就十分简单了。如下:

SqlSession sqlSession= sqlSessionFactory.openSession();

继续往下看,DefaultSqlSessionFactory实现了openSession()方法,之后调用了openSessionFromDataSource() 方法。

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    
    Transaction tx = null;
    try {
   
      //传入的configuration获取环境变量对象、Environment可以配置多个环境配置
      final Environment environment = configuration.getEnvironment();
      //从环境对象中获取事务工厂对象
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //根据DataSource、事务隔离级别、自动提交创建事务对象
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //新建执行者 
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建默认SqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
   
      //异常情况关闭事务
      closeTransaction(tx); // may have fetched a connection so lets call close() (可能已经获取到数据库连接,因此执行关闭)
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
   
      //重置错误上下文
      ErrorContext.instance().reset();
    }
  }

最后调用了new DefaultSqlSession()创建了默认SqlSession。

SqlSession运行过程

SqlSession是一个接口,使用它并不复杂,它给出了查询、插入、更新、删除的方法。但运行过程是MyBatis中最难理解的部分。
先来看看MyBatis的源码是如何实现getMapper方法的。在MyBatis中,通过MapperProxy动态代理Dao, 也就是说当执行自己写的Dao里面的方法的时候,其实是对应的mapperProxy在代理。

通过SqlSession从Configuration中获取

  @Override
  public <T> T getMapper(Class<T> type) {
   
    return configuration.<T>getMapper(type, this);
  }

显然应用到了Configuration对象的getMapper方法来获取对应接口对象,所以继续往下看。

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
   
    return mapperRegistry.getMapper(type, sqlSession);
  }

它有应用到了映射器的注册器Mapperregistry来获取对应的接口对象。

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
   
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
   
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
   
      //关键在这儿
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
   
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

这里首先它判断是否注册一个Mapper,如果没有则会抛出异常信息,如果有就会启用MapperProxyFactory工厂来生成一个代理实例。

@SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
   
    //动态代理我们写的 Dao接口
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {
    mapperInterface }, mapperProxy);
  }
  
  public T newInstance(SqlSession sqlSession) {
   
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

在这里可以看到Mapper映射是通过动态代理来实现对接口的绑定的,它的作用就是生成动态代理对象,而代理的方法则被放到了MapperProxy类中。我们再往下看MapperProxy的源码。

  
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值