背景介绍

Mybatis 是一个 Data Mapper Framework,归于 ORM 结构;旨在提供更简略,更方便地完结操作数据库功能,减轻开发⼈员的⼯作量,消除程序冗余代码。

功能结构

主要从功能⽅面配合源码进行解析 Mybatis 的完结过程和原理,功能结构图如下:

Mybatis源码主流程分析

接⼝层:

  1. 运用 Mybatis 提供 API 操作数据库,传递 statementId 和参数 map 给 sqlSession;

  2. 使⽤ mapper 接⼝⽅式操作数据库,每个接⼝口⽅办法对应⼀一个 mapper 节点<insert/select/update/delete>;

处理层:

  1. 处理传入参数,一起进行类型转化 —— ParameterHandler 剖析

  2. 依据传入参数,使⽤ ognl 动态生成 SQL 语句 —— StatementHandler 剖析

  3. 将 SQL 句子和参数交于执行器进⾏获取成果 —— Executor 剖析

  4. 处理成果集,类型转化 —— ResultSetHandler 剖析

结构层:

  1. 数据源和连接池办理理 —— 数据源与连接池剖析

  2. 业务办理理 —— 业务剖析

  3. 缓存办理理(⼀级缓存和二级缓存) —— 缓存剖析

  4. SQL 语句装备⽅式管理(XML和注解) —— 装备⽅方式剖析

Mybatis 中⽐较中心重要且常⽤的类

  • Configuration MyBatis 一切的装备信息都维持在 Configuration 目标之中。
  • TypeHandler 担任 java 数据类型和 jdbc 数据类型之间的映射和转化。
  • MappedStatement 保护了一条 <select|update|delete|insert> 节点的封装。
  • StatementHandler 封装了 JDBC Statement 操作,担任对 JDBC statement 的操作,如设置参数、将 Statement 成果集转化成 List 调集。
  • ParameterHandler 担任对⽤户传递的参数转化成 JDBC Statement 所需要的参数。
  • SqlSource 担任依据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 目标中,并回来。
  • BoundSql 表示动态生成的 SQL 句子以及相应的参数信息。
  • Executor Mybatis 执行器,是 Mybatis 调度的中心,担任 SQL 句子的生成和查询缓存的保护作业。
  • SqlSession 作为 Mybatis 作业的主要顶层 API,表示和数据库交互的会话,完结必要数据库增修改查功能。
  • ResultSetHandler 担任将 JDBC 回来的 ResultSet 成果集目标转化成 List 类型的调集。

Mybatis 运用

 // 1.初始化
 String resource = "mybatis-config.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); inputStream.close(); 
 // 2.获取sqlSession目标
 SqlSession session = sqlSessionFactory.openSession(); 
 // 3.获取dao署理目标
 DspUserDao dspUserDao = session.getMapper(DspUserDao.class); 
 // 4.执行sql
 DspUser user = dspUserDao.selectUserByName("test"); System.out.println(user.getUserName()); 

源码剖析

装备文件初始化流程

Mybatis源码主流程分析

1. SqlSessionFactoryBuilder

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

SqlSessionFactoryBuilder 为⼊口点进行传⼊解析 xml。

2. XMLConfigBuilder

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
build(parser.parse()); 

将 XML 进⾏节点解析,封装为 configuration 目标。

 private void parseConfiguration(XNode root) {
     try { 
         // 解析properties
         this.propertiesElement(root.evalNode("properties"));
         // 解析settings,设置默认值
         Properties settings = this.settingsAsProperties(root.evalNode("settings")); 
         this.loadCustomVfs(settings);
         // 解析typeAliases,设置别名 
         this.typeAliasesElement(root.evalNode("typeAliases"));
         // plugins,设置拦截器链
         this.pluginElement(root.evalNode("plugins")); 
         this.objectFactoryElement(root.evalNode("objectFactory")); 
         this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectorFactoryElement(root.evalNode("reflectorFactory")); this.settingsElement(settings);
         // environments,设置数据源,tx相关信息 
         this.environmentsElement(root.evalNode("environments")); 
         this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
         // 解析typeHandlers,类型处理类 
         this.typeHandlerElement(root.evalNode("typeHandlers"));
         // 解析mapper,处理sql相关信息
         this.mapperElement(root.evalNode("mappers")); 
         } catch (Exception var3) {
             throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); 
         } 
} 

3.sql.xml解析⼊口

 public void parse() {
     if (!this.configuration.isResourceLoaded(this.resource)) { 
         this.configurationElement(this.parser.evalNode("/mapper"));//Map<String, MappedStatement> mappedStatements 
         this.configuration.addLoadedResource(this.resource);
         this.bindMapperForNamespace();//Map<Class<?>, MapperProxyFactory<?>> knownMappers 
     } 
     this.parsePendingResultMaps(); 
     this.parsePendingCacheRefs(); 
     this.parsePendingStatements(); 
 }  

获取 sqlSession 流程

Mybatis源码主流程分析

1. DefaultSqlSessionFactory

 public SqlSession openSession() {
     return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); 
 }
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 
     Transaction tx = null; 
     DefaultSqlSession var8; 
     try { 
         Environment environment = this.configuration.getEnvironment();
         TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
         tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); 
         Executor executor = this.configuration.newExecutor(tx, execType);
         var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); 
     } catch (Exception var12) {
         this.closeTransaction(tx);
         throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); 
     } finally {
         ErrorContext.instance().reset(); 
     } 
     return var8; 
} 

2. configuration创立executor目标

 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
 executorType = executorType == null ? this.defaultExecutorType : executorType; 
 executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Object executor; 
     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); 
     } 
     if (this.cacheEnabled) {
         executor = new CachingExecutor((Executor)executor); 
     } 
     // 此处拦截器调用链加载
     Executor executor = (Executor)this.interceptorChain.pluginAll(executor); 
     return executor; 
}  

获取 dao 署理理目标流程

Mybatis源码主流程分析

1. DefaultSqlSession获取mapper署理理目标

 public <T> T getMapper(Class<T> type) {
     return this.configuration.getMapper(type, this); 
 }
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 
     return this.mapperRegistry.getMapper(type, sqlSession); 
 } 
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); 
     if (mapperProxyFactory == null) { 
         throw new BindingException("Type " + type + " is not known to the MapperRegistry."); 
     } else { 
         try {
             return mapperProxyFactory.newInstance(sqlSession); 
         } catch (Exception var5) {
             throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); 
         } 
 } 

2. MapperProxyFactory 创立 mapperProxy 的署理理目标

 protected T newInstance(MapperProxy<T> mapperProxy) {
     return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); 
 } 
 public T newInstance(SqlSession sqlSession) {
     MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); 
     return this.newInstance(mapperProxy); 
 } 

执行查询流程

Mybatis源码主流程分析

1. mapperProxy 署理理目标调⽤用 invoke

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     try { 
         // 若执⾏的object类的办法,直接调⽤
         if (Object.class.equals(method.getDeclaringClass())) { 
             return method.invoke(this, args); 
          } 
         if (this.isDefaultMethod(method)) {
             return this.invokeDefaultMethod(proxy, method, args); 
         }
      } catch (Throwable var5) { 
         throw ExceptionUtil.unwrapThrowable(var5); 
      } 
      // 缓存中获取MapperMethod
     MapperMethod mapperMethod = this.cachedMapperMethod(method);
     return mapperMethod.execute(this.sqlSession, args); 
} 

2. 创立 MapperMethod 执⾏操作

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new MapperMethod.SqlCommand(config, mapperInterface, method); 
    this.method = new MapperMethod.MethodSignature(config, mapperInterface, method); 
 } 
 // 依据sql类型进⾏执行
 public Object execute(SqlSession sqlSession, Object[] args){ 
     ....省掉 
     param = this.method.convertArgsToSqlCommandParam(args); 
     result = sqlSession.selectOne(this.command.getName(), param);
      ....省掉 
 } 

3. DefaultSqlSession 执⾏ select 操作

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5; 
     try {
         // 从configuration中获取MappedStatement目标 
         MappedStatement ms = this.configuration.getMappedStatement(statement);
         // 经过executor执行MappedStatement
         var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 
     } catch (Exception var9) {
         throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); 
     } finally { 
         ErrorContext.instance().reset(); 
     } 
     return var5; 
 } 

4. Executor

baseExecutor类

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 获取动态⽣成的SQL句子以及相应的参数信息
     BoundSql boundSql = ms.getBoundSql(parameter);
     // 缓存当时查询 
     CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql); 
     return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql); 
 } 
 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
     ....省掉
     // simpleExecutor执⾏查询 
     list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
      ....省掉 
 }

simpleExecutor类

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null; 
     List var9; 
     try { 
         Configuration configuration = ms.getConfiguration(); 
         // 创立PreparedStatementHandler预编译处理类,包含parameterHandler和resultSetHandler
         StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 
         // 设置参数信息
         stmt = this.prepareStatement(handler, ms.getStatementLog());
         // 执⾏行行sql,并处理理成果集
         var9 = handler.query(stmt, resultHandler); 
     } finally { 
         this.closeStatement(stmt); 
     } 
     return var9; 
 } 

5.创立StatementHandler

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
     // 创立PreparedStatementHandler预编译处理类,包含parameterHandler和resultSetHandler
     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 
     StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
     return statementHandler; 
 } 

6.parameterHandler设置参数信息

public void parameterize(Statement statement) throws SQLException {
    this.parameterHandler.setParameters((PreparedStatement)statement); 
 } 

7. PreparedStatementHandler执行sql,并处理成果集

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
     PreparedStatement ps = (PreparedStatement)statement;
     ps.execute();
     return this.resultSetHandler.handleResultSets(ps); 
 }

总结

Mybatis源码主流程分析

参加我们

我们来自字节跳动飞书商业应用研发部(Lark Business Applications),目前我们在北京、深圳、上海、武汉、杭州、成都、广州、三亚都设立了办公区域。我们重视的产品范畴主要在企业经验办理软件上,包含飞书 OKR、飞书绩效、飞书招聘、飞书人事等 HCM 范畴系统,也包含飞书批阅、OA、法务、财政、收购、差旅与报销等系统。欢迎各位参加我们。

扫码发现职位&投递简历

Mybatis源码主流程分析

官网投递:job.toutiao.com/s/FyL7DRg