初识mybatis源码

mybatis源码我已经下载好了,给大家放到了我的gitee上了,里面可能还有一些理解不到位的地方,请大家多多谅解,也请各位大佬给我提出问题,我好及时改正。

https://gitee.com/studyzhai/mybatis-source-code-analysis.git

那么我们看看mybatis是如何执行的吧

  			// 1. 读取配置
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            // 2. 创建SqlSessionFactory工厂
            SqlSessionFactory sqlSessionFactory = new  SqlSessionFactoryBuilder().build(inputStream);
            // 3. 获取sqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 4. 获取Mapper
            AuthorMapper mapper = sqlSession.getMapper(AuthorMapper.class);
            List<Author> authors = mapper.selectAllAuthors("1");
            System.out.println(authors);
            // 6. 提交事物
            sqlSession.commit();
            // 7. 关闭资源
            sqlSession.close();
            inputStream.close();

首先我们看第一行代码,就是加载资源文件下的一个文件,mybatis-config.xml这也是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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://换成自己的数据库"/>
                <property name="username" value="用户名"/>
                <property name="password" value="密码"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mappers/AuthorMapper.xml"/>
    </mappers>
</configuration>

这是一个很简单的的配置,就配置了一下数据库的连接,和一个mapper那我们看看mybatis还有那些配置吧!地址:https://mybatis.net.cn/configuration.html 大家自行到官网看。我就不具体描述了。
在这里插入图片描述
那么我们回到第一行代码,这个没有啥好说的,就是去加载一个xml配置文件,获取一个输入流。

第二行代码,创建SqlSessionFactory工厂

// 调用重载方法
 public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // 解析mybatis配置文件(mybatis-config.xml)
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //通过这个方法去解析的mybati核心配置文件
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
  // 构建SqlSessionFactory工厂
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

1.将读到的流,转化成一个XMLConfigBuilder的对象
2.build方法构建SqlSessionFactory工厂,默认是DefaultSqlSessionFactory
3.parser.parse()方法将核心配置文件,转化成一个大的配置对象,然后交给SqlSessionFactory工厂

配置文件的解析

public Configuration parse() {
    // 防止配置文件被多次解析
    if (parsed) {
      throw new BuilderException("Each MapperConfigParser can only be used once.");
    }
    parsed = true;
    // 解析核心配置文件
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  // 分别解析root下的每一个根标签
  private void parseConfiguration(XNode root) {
    try {
        // 这些属性就是root标签下可配置的所有属性
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 核心配置文件对应的mappper处理
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

大家看这些标签和mybatis官网给的配置是不是一一对应,看上面那张图。
具体每个标签如何解析的我就不不墨迹了,大家自己看。这样我们就获取了一个SqlSessionFactory工厂,并且把配置文件封装成一个类传给了工厂。

从工厂中获取sqlSession

 SqlSession sqlSession = sqlSessionFactory.openSession();

在这里插入图片描述
大家看这里在上面bulid的时候new 的是一个DefaultSqlSessionFactory工厂,所以这边我们进入DefaultSqlSessionFactory来看一下

 public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

设置默认的执行器是SIMPLE

  /**
   *
   * @param execType 执行器
   * @param level  设置事务隔离级别
   * @param autoCommit  数据库是否自动提交
   * @return
   */
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 从配置文件种获取使用数据源的环境
      final Environment environment = configuration.getEnvironment();
      // 工厂设计模式设置事务管理
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor);
    } 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();
    }
  }

那么就从工厂中获取到了SqlSession,并且设置执行器、事务管理、autoCommit

生成代理对象

  AuthorMapper mapper = sqlSession.getMapper(AuthorMapper.class);
  
 // 这个地方是在解析配置文件的时候赋值的
  private Set<Class<?>> knownMappers = new HashSet<Class<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  // 通过代理生成代理mappper实体
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 如果这个地方拿不到类类型对于的mapper接口,这个mapper 就没被注册进去 抛出异常结束
    if (!knownMappers.contains(type))
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    try {
      // 重点代理方法生产代理对象  如果找到了,就去创建对应的代理对象.
      return MapperProxy.newMapperProxy(type, sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  // 代理对象的生成
  /**
   * 通过jdk动态代理生成对应的代理对象
   */
  @SuppressWarnings("unchecked")
  public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
    // 动态代理所必须使用的类加载器
    ClassLoader classLoader = mapperInterface.getClassLoader();
    // 这个目前没有看懂为啥是要一个类类型的数组
    Class<?>[] interfaces = new Class[]{mapperInterface};
    // 构建mapper代理
    MapperProxy proxy = new MapperProxy(sqlSession);
    // 使用jdk动态代理生成代理对象
    /**
     * 第一个参数 是一个是类加载器
     * 的二个是要被代理的接口类类型
     * 第三个是代理对象 也就是MapperProxy
     */
    return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); 
  }

关于动态代理的设计模式,我希望大家去学习一下,这个对于我个人而言,是mybais源码中最难的一块了,理解了这个地方其他的就很简单了,那么我们接着往下看,在调用我们的查询方法的时候就会调用

  List<Author> authors = mapper.selectAllAuthors("1");
  
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (method.getDeclaringClass() == Object.class) {
      return method.invoke(this, args);
    }
    // 获取接口类
    final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
    // 封装 mapperMethod对象
    final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
    // 执行sql
    final Object result = mapperMethod.execute(args);
    if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
      throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

哈哈这就差不多是整个mybatis的设计思路,具体的如何执行的查询,大家可以按着我这个思路往下看看,那么今天的初识mybatis源码就到这里了。

最后把自己的用到的表结构给大家
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值