MyBatis工作原理剖析

简介

最近参加了一个课程,感触颇深,收获颇多,干开发工作4、5年了,还停留在框架的使用,编写业务代码层次,对原理一窍不通。课程内容包含各种框架使用、原理分析、自定义mini框架,框架整合、分布式,容器技术等十二个阶段,涵盖架构师所有需要掌握的知识。到现在已经把第一阶段学习完了,简单谈谈第一阶段第一个模块我对持久层框架MyBatis原理理解,分享下自己学习成果,后续有时间会分享自己对其他框架原理的理解,与大家共同学习进步。

1 MyBatis的架构原理

Mybatis是一款优秀的基于ORM半自动轻量级的持久层框架。其主要完成2件事:

  1. 封装JDBC操作;
  2. 通过反射解析配置文件来完成动态SQL语句的装配。

1.1 主要组件及相互关系

  • Configuration
    MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中
  • SqlSession
    作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
  • Executor
    MyBatis执行器,是MyBatis调度的核心,负责SQL语句的生成和查询缓存的维护
  • StatementHandler 封装了JDBCStatement操作,负责对JDBC statement 的操作,如设置参数等
  • ParameterHandler
    负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
  • ResultSetHandler
    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
  • TypeHandler
    负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
  • MappedStatement
    MappedStatement维护一条<select|update|delete|insert>节点的封装
  • SqlSource
    负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql
    表示动态生成的SQL语句以及相应的参数信息

在这里插入图片描述

1.2 总体流程

1. 加载配置文件并初始化
Mybatis的配置文件主要有mybatis-config.xml、mapper.xml以及java类中的注解,将配置文件的内容解析封装到Configuration,将sql的配置信息加载成为一个mappedstatement对象,存储在内存中。

2. 接收调用请求
调用Mybatis的API,通过传入SQL的ID,参数对象将请求传递给下层的请求处理层进行处理

3. 处理操作请求
根据SQL的ID查找对应的MappedStatement,
根据传入的参数对象解析MappedStatement对象,得到最终要执行的SQL语句和执行传入参数,
获取数据库连接,根据得到的SQL语句和参数执行数据库操作,并得到返回结果
根据MappedStatement对象中配置的结果映射配置对得到的结果进行处理,并得到最终的结果,
最后释放连接资源

4. 返回结果
将得到的最终处理结果返回。

2 源码剖析

首先我们看下Mybatis传统方式执行的大概的一个时序图,从加载配置文件,到调用执行SQLAPI,最后处理返回结果:
在这里插入图片描述
Mybatis除了传统方式调用方法还有通过Mapper代理方式来调用,下面时序图展示的是获取Mapper动态代理对象执行方法:
在这里插入图片描述
我们根据时序图的步骤来进行源码剖析:

2.1 加载配置文件并初始化

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//初始化工作的开始
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

进入new SqlSessionFactoryBuilder().build(inputStream)方法:

// 1.我们最初调用的build
    public SqlSessionFactory build(InputStream inputStream) {
        //调用了重载方法
        return build(inputStream, null, null);
    }
// 2.调用的重载方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 执行 XML 解析
            // 创建 DefaultSqlSessionFactory 对象
            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.
            }
        }
    }

Mybatis在初始化的的时候,会把配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration实例来维护,下面进入对配置文件解析上述方法:parser.parse()中

/**
 * 解析 XML 成 Configuration 对象。
 *
 * @return Configuration 对象
 */
public Configuration parse() {
    // 若已解析,抛出 BuilderException 异常
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    // 标记已解析
    parsed = true;
    ///parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签
    // 解析 XML configuration 节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
 
 /**
  * 解析 XML
  *
  * 具体 MyBatis 有哪些 XML 标签,参见 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
  *
  * @param root 根节点
  */
 private void parseConfiguration(XNode root) {
     try {
         //issue #117 read properties first
         // 解析 <properties /> 标签
         propertiesElement(root.evalNode("properties"));
         // 解析 <settings /> 标签
         Properties settings = settingsAsProperties(root.evalNode("settings"));
         // 加载自定义的 VFS 实现类
         loadCustomVfs(settings);
         // 解析 <typeAliases /> 标签
         typeAliasesElement(root.evalNode("typeAliases"));
         // 解析 <plugins /> 标签
         pluginElement(root.evalNode("plugins"));
         // 解析 <objectFactory /> 标签
         objectFactoryElement(root.evalNode("objectFactory"));
         // 解析 <objectWrapperFactory /> 标签
         objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
         // 解析 <reflectorFactory /> 标签
         reflectorFactoryElement(root.evalNode("reflectorFactory"));
         // 赋值 <settings /> 到 Configuration 属性
         settingsElement(settings);
         // read it after objectFactory and objectWrapperFactory issue #631
         // 解析 <environments /> 标签
         environmentsElement(root.evalNode("environments"));
         // 解析 <databaseIdProvider /> 标签
         databaseIdProviderElement(root.evalNode("databaseIdProvider"));
         // 解析 <typeHandlers /> 标签
         typeHandlerElement(root.evalNode("typeHandlers"));
         // 解析 <mappers /> 标签
         mapperElement(root.evalNode("mappers"));
     } catch (Exception e) {
         throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
     }
 }

上述步骤解析完mybatis-config.xml,下面进入mapper.xml解析,先说一下MappedStatement:
MappedStatement与Mapper.xml配置文件中的select | insert | update | delete 标签节点相对应,mapper配置中的信息都封装到此对象中,主要用途就是描述一条SQL语句。在mybatis-config.xml中会有mappers标签来引入mapper.xml文件,在上述方法parseConfiguration最后一步就是解析 mappers 标签。
下面就进入:mapperElement(root.evalNode(“mappers”));

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        // 遍历子节点
        for (XNode child : parent.getChildren()) {
            // 如果是 package 标签,则扫描该包
            if ("package".equals(child.getName())) {
                // 获得包名
                String mapperPackage = child.getStringAttribute("name");
                // 添加到 configuration 中
                configuration.addMappers(mapperPackage);
            // 如果是 mapper 标签,
            } else {
                // 获得 resource、url、class 属性
                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);
                    // 获得 resource 的 InputStream 对象
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    // 创建 XMLMapperBuilder 对象
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                    // 执行解析
                    mapperParser.parse();
                // 使用完全限定资源定位符(URL)
                } else if (resource == null && url != null && mapperClass == null) {
                    ErrorContext.instance().resource(url);
                    // 获得 url 的 InputStream 对象
                    InputStream inputStream = Resources.getUrlAsStream(url);
                    // 创建 XMLMapperBuilder 对象
                    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                    // 执行解析
                    mapperParser.parse();
                // 使用映射器接口实现类的完全限定类名
                } else if (resource == null && url == null && mapperClass != null) {
                    // 获得 Mapper 接口
                    Class<?> mapperInterface = Resources.classForName(mapperClass);
                    // 添加到 configuration 中
                    configuration.addMapper(mapperInterface);
                } else {
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}

到此初始化配置文件就结束。

2.2 执行SQL流程

Mybatis执行SQL语句是通过SqlSession对象调用执行方法,SqlSession是一个接口,他的一个重要实现类DefaultSqlSession,有两个重要的属性Configuration初始化封装的对象和Executor执行器

/**
 * 默认的 SqlSession 实现类
 * @author Clinton Begin
 */
public class DefaultSqlSession implements SqlSession {

    private final Configuration configuration;
    private final Executor executor;
}

执行SQL,需要先获取sqlSession,跳转到DefaultSqlSessionFactory类的openSession方法

 //6. 进入openSession方法
@Override
public SqlSession openSession() {
    //getDefaultExecutorType()传递的是SimpleExecutor
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

//7. 进入openSessionFromDataSource。
//ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务
//openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        // 获得 Environment 对象
        final Environment environment = configuration.getEnvironment();
        // 创建 Transaction 对象
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 创建 Executor 对象
        final Executor executor = configuration.newExecutor(tx, execType);
        // 创建 DefaultSqlSession 对象
        return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
        // 如果发生异常,则关闭 Transaction 对象
        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的api,在DefaultSqlSession类中

//8.进入selectList方法,多个重载方法
@Override
 public <E> List<E> selectList(String statement) {
     return this.selectList(statement, null);
 }

 @Override
 public <E> List<E> selectList(String statement, Object parameter) {
     return this.selectList(statement, parameter, RowBounds.DEFAULT);
 }

 @Override
 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
     try {
         // 获得 MappedStatement 对象
         MappedStatement ms = configuration.getMappedStatement(statement);
         // 执行查询
         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();
     }
 }
 

继续进入,看executor.query()执行器如何执行方法:

//此方法在SimpleExecutor的父类BaseExecutor中实现
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示
    BoundSql boundSql = ms.getBoundSql(parameter);
    //为本次查询创建缓存的Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    // 查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

//进入重载方法
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    // 已经关闭,则抛出 ExecutorException 异常
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
        clearLocalCache();
    }
    List<E> list;
    try {
        // queryStack + 1
        queryStack++;
        // 从一级缓存中,获取查询结果
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        // 获取到,则进行处理
        if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
        // 获得不到,则从数据库中查询
        } else {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    } finally {
        // queryStack - 1
        queryStack--;
    }
    if (queryStack == 0) {
        // 执行延迟加载
        for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
        }
        // issue #601
        // 清空 deferredLoads
        deferredLoads.clear();
        // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理
        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
        }
    }
    return list;
}

// 从数据库中读取操作
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    // 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 `DeferredLoad#canLoad()` 方法
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        // 执行读操作
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        // 从缓存中,移除占位对象
        localCache.removeObject(key);
    }
    // 添加到缓存中
    localCache.putObject(key, list);
    // 暂时忽略,存储过程相关
    if (ms.getStatementType() == StatementType.CALLABLE) {
        localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}
//从SimpleExecutor中实现父类doQuery抽象法
 @Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        // 传入参数创建StatementHanlder对象来执行查询
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 创建jdbc中的statement对象
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 执行 StatementHandler  ,进行读操作
        return handler.query(stmt, resultHandler);
    } finally {
        // 关闭 StatementHandler 对象
        closeStatement(stmt);
    }
}
// 创建 StatementHandler 对象
 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
     // 创建 RoutingStatementHandler 对象
     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
     // 应用插件
     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
     return statementHandler;
 }
 
// 初始化 StatementHandler 对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
     Statement stmt;
     // 获得 Connection 对象
     Connection connection = getConnection(statementLog);
     // 创建 Statement 或 PrepareStatement 对象
     stmt = handler.prepare(connection, transaction.getTimeout());
     // 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符
     handler.parameterize(stmt);
     return stmt;
 }
 
 @Override
 public void parameterize(Statement statement) throws SQLException {
     //使用ParameterHandler对象来完成对Statement的设值
     parameterHandler.setParameters((PreparedStatement) statement);
 }

@SuppressWarnings("Duplicates")
@Override
public void setParameters(PreparedStatement ps) {
   ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
   // 遍历 ParameterMapping 数组
   List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
   if (parameterMappings != null) {
       for (int i = 0; i < parameterMappings.size(); i++) {
           // 获得 ParameterMapping 对象
           ParameterMapping parameterMapping = parameterMappings.get(i);
           if (parameterMapping.getMode() != ParameterMode.OUT) {
               // 获得值
               Object value;
               String propertyName = parameterMapping.getProperty();
               if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                   value = boundSql.getAdditionalParameter(propertyName);
               } else if (parameterObject == null) {
                   value = null;
               } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                   value = parameterObject;
               } else {
                   MetaObject metaObject = configuration.newMetaObject(parameterObject);
                   value = metaObject.getValue(propertyName);
               }
               // 获得 typeHandler、jdbcType 属性
               TypeHandler typeHandler = parameterMapping.getTypeHandler();
               JdbcType jdbcType = parameterMapping.getJdbcType();
               if (value == null && jdbcType == null) {
                   jdbcType = configuration.getJdbcTypeForNull();
               }
               // 设置 ? 占位符的参数
               try {
                   typeHandler.setParameter(ps, i + 1, value, jdbcType);
               } catch (TypeException | SQLException e) {
                   throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
               }
           }
       }
   }
}

// 获得 Connection 对象
protected Connection getConnection(Log statementLog) throws SQLException {
    // 获得 Connection 对象
    Connection connection = transaction.getConnection();
    // 如果 debug 日志级别,则创建 ConnectionLogger 对象,进行动态代理
    if (statementLog.isDebugEnabled()) {
        return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
        return connection;
    }
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
     PreparedStatement ps = (PreparedStatement) statement;
     // 执行查询
     ps.execute();
     // 处理返回结果
     return resultSetHandler.handleResultSets(ps);
 }

2.3 处理返回结果

// 处理 {@link java.sql.ResultSet} 结果集
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List<Object> 对象。
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。
    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 获得 ResultMap 数组
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount); // 校验
    while (rsw != null && resultMapCount > resultSetCount) {
        // 获得 ResultMap 对象
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 处理 ResultSet ,将结果添加到 multipleResults 中
        handleResultSet(rsw, resultMap, multipleResults, null);
        // 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象
        rsw = getNextResultSet(stmt);
        // 清理
        cleanUpAfterHandlingResultSet();
        // resultSetCount ++
        resultSetCount++;
    }

    // 因为 `mappedStatement.resultSets` 只在存储过程中使用,本系列暂时不考虑,忽略即可
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
        while (rsw != null && resultSetCount < resultSets.length) {
            ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
            if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                handleResultSet(rsw, resultMap, null, parentMapping);
            }
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
    }

    // 如果是 multipleResults 单元素,则取首元素返回
    return collapseSingleResultList(multipleResults);
}

2.4 Mapper代理方式

进入sqlSession.getMapper(UserMapper.class)中:

//DefaultSqlSession中的getMapper
 @Override
 public <T> T getMapper(Class<T> type) {
     return configuration.getMapper(type, this);
 }
//configuration中的getMapper
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     return mapperRegistry.getMapper(type, sqlSession);
 }
//MapperRegistry中的getMapper 
 @SuppressWarnings("unchecked")
 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
     // 获得 MapperProxyFactory 对象
     final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
     // 不存在,则抛出 BindingException 异常
     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类中的方法了,但代理对象调用方法,执行的是在MapperProxy中的invoke方法

 @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 如果是 Object 定义的方法,直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);

        } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    // 获得 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 重点在这:MapperMethod最终调用了执行的方法
    return mapperMethod.execute(sqlSession, args);
}

进入execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判断mapper中的方法类型,最终调用的还是SqlSession中的方法
    switch (command.getType()) {
        case INSERT: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 执行 INSERT 操作
            // 转换 rowCount
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
        }
        case UPDATE: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 转换 rowCount
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
        }
        case DELETE: {
            // 转换参数
            Object param = method.convertArgsToSqlCommandParam(args);
            // 转换 rowCount
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
        }
        case SELECT:
            // 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理
            if (method.returnsVoid() && method.hasResultHandler()) {
                executeWithResultHandler(sqlSession, args);
                result = null;
            // 执行查询,返回列表
            } else if (method.returnsMany()) {
                result = executeForMany(sqlSession, args);
            // 执行查询,返回 Map
            } else if (method.returnsMap()) {
                result = executeForMap(sqlSession, args);
            // 执行查询,返回 Cursor
            } else if (method.returnsCursor()) {
                result = executeForCursor(sqlSession, args);
            // 执行查询,返回单个对象
            } else {
                // 转换参数
                Object param = method.convertArgsToSqlCommandParam(args);
                // 查询单条
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional() &&
                        (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + command.getName());
    }
    // 返回结果为 null ,并且返回类型为基本类型,则抛出 BindingException 异常
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
                + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    // 返回结果
    return result;
}

3 总结

经过源码分析后,对Mybatis是不是有更深的了解。总之,Mybatis的底层还是对JDBC的封装,所以它主要的流程就是通过解析配置文件来组装JDBC执行所需要的SQL、执行SQL参数、以及数据库连接等对象。我们可以根据上述时序图自己跟一下源码印象会更深刻。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值