【源码分析-Spring Boot】-12.Spring Boot Mybatis 查询流程及实现原理


Spring Boot Mybatis:【从零开始学Spring Boot】-12.Spring Boot Mybatis操作数据库

以 UserServiceImpl#findUserByName 为例,分析一下相关源码。

1.mybatis 的加载

SqlSessionFactoryBean 实现了 InitializingBean 接口,重写了 afterPropertiesSet 方法,在这个方法中对 sqlSessionFactory 进行了初始化

public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
      "Property 'configuration' and 'configLocation' can not specified with together");

  this.sqlSessionFactory = buildSqlSessionFactory();
}

在 buildSqlSessionFactory 中解析 config 和 mapper 配置文件、扫描注解,将所有配置信息保存到 configuration 中。

// 解析 mybatis-config.xml
private void parseConfiguration(XNode root) {
    try {
        // issue #117 read properties first
        // 解析 properties 节点
        propertiesElement(root.evalNode("properties"));
        // 解析 settings 节点
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(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"));
        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);
    }
}
// 解析 UserMapper.xml
public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
        // 解析 mapper 节点
        configurationElement(parser.evalNode("/mapper"));
        configuration.addLoadedResource(resource);
        // 生成 MappedStatement 对象,保存到 configuration 中
        bindMapperForNamespace();
    }

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

2.UserMapper 的动态代理

MapperFactoryBean 是 UserMapper 实例化是对应的 beanClass,它继承自 DaoSupport 间接实现了 InitializingBean 接口,同时自己实现了 FactoryBean 接口,在 UserMapper 实例化时,会调用它的 getObject 方法,按照自定义逻辑创建对象。

在 getObject 中,它要返回一个 Mapper 对象

@Override
public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}

这里的 getSqlSession 实际上调用父类 SqlSessionDaoSupport 的方法,返回一个 SqlSessionTemplate。它是 SqlSession 的一个实现类,重写了 SqlSession 中一些操作数据库的常用方法,相当于一个工具类。

@Override
public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

getMapper 是从解析配置文件后保存的 mapperRegistry 中获取一个 mapper 对象

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // MapperProxyFactory 工厂类
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    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);
    }
}

它通过 MapperProxyFactory 工厂类来创建具体的对象,创建时,最终生成为 Mapper 的代理对象

protected T newInstance(MapperProxy<T> mapperProxy) {
    // jdk 动态代理
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

3.mybatis查询执行流程

由于 UserMapper 实际上是一个 MapperProxy 的代理对象,所以 UserServiceImpl 在调用 UserMapper 中方法时,实际调用到 MapperProxy 中的 invoke 方法。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            // 返回一个 PlainMethodInvoker 对象,并调用 invoke 方法
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
}

PlainMethodInvoker 是 MapperMethodInvoker 的一个实现,它的 invoke 方法如下

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    return mapperMethod.execute(sqlSession, args);
}

最终调用 sqlSession.selectOne 方法,这里的 sqlSession 是 SqlSessionTemplate

@Override
public <T> T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy.selectOne(statement, parameter);
}

而 sqlSessionProxy 是 sqlSession 的一个代理对象,它在 SqlSessionTemplate 构造函数中创建

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                          PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    // jdk 代理,代理对象是 SqlSessionInterceptor,被代理对象时 SqlSessionFactory 的实例    
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}

所以执行 SqlSessionInterceptor 中的 invoke 方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 获取 sqlSession,返回一个 DefaultSqlSession
    SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
                                          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
    try {
        // 调用 selectOne 方法
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
            // force commit even on non-dirty sessions because some databases require
            // a commit/rollback before calling close()
            sqlSession.commit(true);
        }
        return result;
    } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
            // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
            closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            sqlSession = null;
            Throwable translated = SqlSessionTemplate.this.exceptionTranslator
                .translateExceptionIfPossible((PersistenceException) unwrapped);
            if (translated != null) {
                unwrapped = translated;
            }
        }
        throw unwrapped;
    } finally {
        if (sqlSession != null) {
            closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
    }
}

通过反射调用 selectOne 方法,目标对象是 DefaultSqlSession,

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        // MappedStatement 中封装了每个 sql 操作中各项参数
        MappedStatement ms = configuration.getMappedStatement(statement);
        // 使用 executor 进行查询
        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 获取 DefaultSqlSession 时创建

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // 执行器类型,默认是 Simple
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        // 创建一个 SimpleExecutor
        executor = new SimpleExecutor(this, transaction);
    }
    // cacheEnabled 默认为 true
    if (cacheEnabled) {
        // 使用 CachingExecutor 对 SimpleExecutor 进行装饰
        executor = new CachingExecutor(executor);
    }
    // 这里使用插件对 executor 进行代理,生成拦截器链。当前没配置插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

获取到的 executor 类型为 CachingExecutor,它装饰了 SimpleExecutor,SimpleExecutor 继承于 BaseExecutor。

所以执行查询时先调用 CachingExecutor#query,调用 delegate.query 时,又调用 BaseExecutor#query,这个 query 方法为模板 方法,最终调用到 SimpleExecutor#queryFromDatabase。

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    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;
}

在 doQuery 方法中,调用 handler.query

@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();
        // 创建 handler
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        // 构建 statement,包括获取 connection、设置参数
        stmt = prepareStatement(handler, ms.getStatementLog());
        // 使用 handler 查询
        return handler.query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}

这里的 handler 是被 RoutingStatementHandler 装饰的 PreparedStatementHandler

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

最终调用 PreparedStatement 的 execute 方法完成查询。


文章作者: Soulballad
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Soulballad !
评论
 上一篇
【从零开始学Spring Boot】-13.Spring Boot Jpa多数据源 【从零开始学Spring Boot】-13.Spring Boot Jpa多数据源
1.简介1.1 概述在实际项目中一般是一个数据源,但是在某些特殊场景可能需要多个数据源,这里以 spring boot jpa 为例演示一下多数据源的配置和使用。 2.演示环境 JDK 1.8.0_201 Spring Boot 2.2.0
下一篇 
【从零开始学Spring Boot】-12.Spring Boot Mybatis操作数据库 【从零开始学Spring Boot】-12.Spring Boot Mybatis操作数据库
1.简介1.1 概述 The MyBatis-Spring-Boot-Starter help you build quickly MyBatis applications on top of the Spring Boot. By usi
  目录