2

Mybatis源码主流程分析

 2 years ago
source link: https://juejin.cn/post/7143188589164609543
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Mybatis源码主流程分析

2022年09月14日 11:02 ·  阅读 811

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

主要从功能⽅面配合源码进行解析 Mybatis 的实现过程和原理,功能框架图如下:

b5a17e6e7a2447a68f68e8c6e85f6776~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp
  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()); 
复制代码

配置文件初始化流程

b62e99b1388b42ab932907e39a0e2e22~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp

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 流程

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 代理理对象流程

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); 
 } 
复制代码

执行查询流程

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); 
 }
复制代码
513ebe4974064e0c9d7479a0d7ffcd4c~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp

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

扫码发现职位&投递简历

ba66bc3f804f426e88b6e7af6064aaae~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.awebp

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK