Mybatis组件概念及其基本执行流程与源码解析

Mybatis组件概念及其基本执行流程与源码解析

Mybatis组件核心概念

在开始学习源码之前要先学习Mybatis组件的概念。

  1. Configuration
    Configuration是管理 mysql-config.xml 全局配置关系类,通俗理解就是通过mysql-config.xml封装成为Configuration供Mybatis配置使用。
  2. SqlSessionFactory
    SqlSessionFactory是用来管理与数据库会话的接口。即通过SqlSessionFactory创建与数据库的会话(SqlSession)。
  3. Session
    Session即SqlSession是面向程序员的接口。SqlSession中提供了对数据库的操作。
  4. Executor
    Executor是一个接口,执行器有两大类基本执行器和缓存执行器。SqlSession内部通过执行器去操作数据库。
  5. MappedStatement
    MappedStatement是底层封装对象,就是对操作数据库存储封装,包括 sql 语句、输入输出参数。如一个xxxx,就可以封装成一个MappedStatement。
  6. StatementHandler
    StatementHandler具体实现对数据库的操作的接口。
  7. ResultSetHandler
    ResultSetHandler是对数据库返回结果操作的接口。

Mybatis执行操作拆解

对源码的学习,首先要从整体执行流程拆解开始,以下面的Mybatis传统使用Demo为例:

//本例未使用mapper接口只是mapper.xml
 public static void main(String[] args) throws IOException {
 	  //1·获取mybatis配置文件输入流
      InputStream configInputStream = Resources.getResourceAsStream("mybatis-config.xml");
      //2·SqlSessionFactoryBuilder创建SqlSessionFactory 
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configInputStream );
      //3·sqlSessionFactory创建与数据库的会话
      SqlSession sqlSession = sqlSessionFactory.openSession();
      //4·SqlSession执行Mapper返回结果集
      User user = sqlSession.selectOne("com.zzr.mybatis.UserMapper.selectUser", 1);
  }

接下来进行分步详细拆解

1·获取mybatis配置文件输入流

基本的IO操作不做讲解。

2·SqlSessionFactoryBuilder创建SqlSessionFactory

以下代码只包含了关键代码。

public class SqlSessionFactoryBuilder {
	//1·SqlSessionFactoryBuilder.build(inputStream);是读取配置文件的入口
	public SqlSessionFactory build(InputStream inputStream) {
	    return build(inputStream, null, null);
	}
	
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
	//2·XMLConfigBuilder 是Configuration的构建器。在其内部会将inputStream构建成XPathParser
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //3.parser.parse()解析配置文件并创建Configuration
      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.
      }
    }
  }
}
  //4.使用SqlSessionFactory子类DefaultSqlSessionFactory构建SqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

parser.parse()解析
public class XMLConfigBuilder extends BaseBuilder {

  protected final Configuration configuration;
  
  //1·构建Configuration先判断配置文件是否已经加载到内存
  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //2·从配置文件生成的XPathParser中获取节点configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  //3·通过解析XNode,组装Configuration(重点关注获取mappers节点的解析)
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      //4·从/configuration节点封装的XNode中获取/properties节点的XNode
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
}
mapperElement(root.evalNode(“mappers”))解析
public class XMLConfigBuilder extends BaseBuilder {
//可以看出获取mapper.xml的方式除了包扫描,还有通过资源文件,通过url和通过class的方式。
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            //在此将mapper保存到configuration的MapperRegistry缓存集合knownMappers中
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            //在此将mapper保存到configuration的MapperRegistry缓存集合knownMappers中
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            //在此将mapper保存到configuration的MapperRegistry缓存集合knownMappers中
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
}
mapperParser.parse();解析

关于mapperParser.parse();解析涉及到以下使用方式
在这里插入图片描述使用Mapper接口方式操作数据库。

public class XMLMapperBuilder extends BaseBuilder {
	//1·mapperParser.parse()
  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
     //此处会遍历Mapper中的所有标签如select,insert等,将其封装成MappedStatement,
    //放到Configuration的Map<String, MappedStatement> mappedStatements中。
    //此处更加印证了Configuration为mybatis的统一配置类
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        //2·解析命名空间如com.zzr.mybatis.xxx
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        //ignore, bound type is not required
      }
      if (boundType != null) {
        if (!configuration.hasMapper(boundType)) {
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          //3·将其加入到mapperRegistry的集合中
          configuration.addMapper(boundType);
        }
      }
    }
  }

}
//4·主要用来缓存Mapper接口的代理工厂类
public class MapperRegistry {
  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
 
  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
      //5·用namespace的接口创建MapperProxyFactory
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

}
//6·Mapper.java的代理工厂类
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
  //7·创建mapperProxy ,mapper代理类,执行具体逻辑使用。Mapper中的每一个method都可以创建mapperProxy 。
  //此处代码量比较多就不展示代码了。就是利用mapper的接口地址和方法名找到mapper.xml中的标签,
  //并根据标签类型(select,update,insert等)创建MapperProxy。
  //使用UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.selectUser(1)这种方式执行时才会使用MapperProxy。
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

SqlSessionFactory至此创建完成。

3·sqlSessionFactory创建与数据库的会话

上面已经讲解过,实际使用的sqlSessionFactory为其子类DefaultSqlSessionFactory。所以从DefaultSqlSessionFactory入手。

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  @Override
  public SqlSession openSession() {
  	//1·获取会话连接,从中可以看出无参openSession,获取session默认使用SimpleExecutor,不开启自动提交
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //2·创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //3·通过事务和执行器类别获取执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      //4·使用SqlSession子类DefaultSqlSession创建Session
      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();
    }
  }
}
configuration.newExecutor(tx, execType)解析

因为在创建sqlSessionFactory之前就已经创建好了configuration,所以通过configuration中的一些配置创建Executor

public class Configuration {
	//默认开启一级缓存
  protected boolean cacheEnabled = true;
	//创建执行器
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //1·判断执行器类型,因为我们使用的是默认的执行器,所以是SimpleExecutor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //2·一级缓存开启,创建缓存执行器
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //3·将缓存执行器加入到mybatis拦截器链中。
    //缓存执行器虽然与基本执行器都实现了Executor接口,但是缓存执行器是作为mybatis插件存在的也就是拦截器,
    //在执行真正的sql之前会先去执行缓存执行器中的代码
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}

//Mybatis拦截器链相关代码,责任链模式拦截器
public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();
	//增加拦截器(插件),Mybatis的分页插件也是使用这个类实现添加
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

以上就是获取SqlSession的代码讲解。

4·SqlSession执行Mapper返回结果集

从以上讲解得知,实际使用的SqlSession为DefaultSqlSession。

public class DefaultSqlSession implements SqlSession {

  @Override
  public <T> T selectOne(String statement) {
  	//1·调用selectOne
    return this.<T>selectOne(statement, null);
  }
  
    @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
   	//2·调用selectselectList。
   	//因为这里我们的Demo使用的selectOne,所以如果查出多条记录会报出异常
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    //3·调用selectList
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }
  //4·真正执行逻辑的selectList
    @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    //5·获得MappedStatement,即sql标签的封装(statement=命名空间+id(方法名))
      MappedStatement ms = configuration.getMappedStatement(statement);
      //6·此处代码过于繁琐。简单讲解一下。
      //首先会先执行缓存执行器去查询是否存在缓存,不存在就会去执行基本执行器
      //基本执行器会创建StatementHandler并使用其query查询执行结果。
      //此时就会看见与传统jdbc类似的代码。之后ResultSetHandler操作结果集返回。
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  
}

以上就是执行Mapper返回结果集。

附上基本的执行流程:

SqlSessionFactoryBuilder创建SqlSessionFactory 
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
 >org.apache.ibatis.builder.xml.XMLConfigBuilder 构造函数
   >org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
     >org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration mybatis-config.xml内容
       >org.apache.ibatis.parsing.XPathParser.evaluate
        >org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement
         >org.apache.ibatis.session.SqlSessionFactoryBuilder.build(org.apache.ibatis.session.Configuration)
          >org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.DefaultSqlSessionFactory


拿到SqlSession 进行对我们的执行器进行初始化 SimpleExecutor
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession()
 >org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource
  >org.apache.ibatis.transaction.TransactionFactory.newTransaction(javax.sql.DataSource, org.apache.ibatis.session.TransactionIsolationLevel, boolean)
    >org.apache.ibatis.session.Configuration.newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)
      >org.apache.ibatis.executor.SimpleExecutor
        >org.apache.ibatis.executor.CachingExecutor  一级缓存 自动
          >org.apache.ibatis.plugin.InterceptorChain.pluginAll 责任链模式拦截器


sqlSessionFactory创建与数据库的会话,SqlSession执行Mapper返回结果集
org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(java.lang.String, java.lang.Object)
 >org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(java.lang.String, java.lang.Object)
   >org.apache.ibatis.session.Configuration.getMappedStatement(java.lang.String)
    >org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
     >org.apache.ibatis.executor.CachingExecutor.createCacheKey 缓存的key
      >org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
         >org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
           >org.apache.ibatis.executor.BaseExecutor.doQuery
             >org.apache.ibatis.executor.statement.PreparedStatementHandler.query
               >org.apache.ibatis.executor.resultset.ResultSetHandler.handleResultSets
                >org.apache.ibatis.executor.resultset.DefaultResultSetHandler
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值