Mybatis源码解读

        MyBatis是我们工作中常见的ORM持久层框架,对于MyBatis并不能仅仅局限于会使用的阶段,更需要了解它的工作原理,想要了解原理,源码是必须要读的,这篇文章是我个人在阅读MyBatis的源码过程中的一些简单的总结,MyBatis的流程大体上可以分为一下几步:加载配置文件 --> 创建SqlSessionFactory --> 创建SqlSession --> 执行SQL语句 --> 返回结果。步骤代码如下:

public static void main(String[] args) {  
    String resource = "mybatis-config.xml";  
    SqlSession sqlSession = null;  
    // 1、读取mybatis-config.xml  
    try (InputStream inputStream = Resources.getResourceAsStream(resource)) {  
        // 2、解析mybatis-config.xml配置文件,通过SqlSessionFactoryBuilder.build创建sqlSessionFactory  
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
        // 3、创建sqlSession  
        sqlSession = sqlSessionFactory.openSession();  
        Map<String, Object> paramsMap = new HashMap<>(2);  
        paramsMap.put("id", 1);  
        paramsMap.put("name", "jams");  
        //4、执行sql  
        List<User> list = sqlSession.selectList("com.c.mybatis.mapper.UserMapper.selectByName", paramsMap);  
        log.info("响应结果:{}", JSONUtil.toJsonStr(list));  
    } catch (Exception e) {  
        e.printStackTrace();  
    } finally {  
        assert sqlSession != null;  
        sqlSession.close();  
    }  
}

这段代码是一个整体的过程展示,下面对这些步骤进行逐步拆解。

1、加载配置信息

        加载配置信息的目的是获取数据源信息和Mapper.xml等文件配置信息。
源码如下:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

这部分是一个文件读取信息,将配置信息读取为InputStream的过程。

2、创建SqlSessionFactory

        SqlSessionFactory是MyBatis中一个重要的接口,SqlSessionFactory的创建是通过SqlSessionFactoryBuilder的build方法中进行,其源码如下:

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

其中构建的详细逻辑在build方法中,build方法的源码如下:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
    try {
        //创建XMLConfigBuilder对象
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        //创建SqlSessionFactory对象
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
        
        }
    }
    return var5;
}

上面的源码中有两处是需要继续阅读的,一个是new XMLConfigBuilder,一个是pase()方法,new XMLConfigBuilder将读取的文件流转成XMLConfigBuilder对象并进行了一系列的初始化操作,其中最重要的是进行了Configuration的创建,Configuration是一个极为重要的上下文对象。我们对这两个步骤的源码进行继续跟进。new XMLConfigBuilder的源码如下:
 

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        //创建Configuration对象
        super(new Configuration());
        this.localReflectorFactory = new DefaultReflectorFactory();
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
}

其中super(new Configuration());操作进行了各类的属性初始话,源码如下:

public Configuration() {
    this.safeResultHandlerEnabled = true;
    this.multipleResultSetsEnabled = true;
    this.useColumnLabel = true;
    this.cacheEnabled = true;
    this.useActualParamName = true;
    this.localCacheScope = LocalCacheScope.SESSION;
    this.jdbcTypeForNull = JdbcType.OTHER;
    this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString"));
    this.defaultExecutorType = ExecutorType.SIMPLE;
    this.autoMappingBehavior = AutoMappingBehavior.PARTIAL;
    this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
    this.variables = new Properties();
    this.reflectorFactory = new DefaultReflectorFactory();
    this.objectFactory = new DefaultObjectFactory();
    this.objectWrapperFactory = new DefaultObjectWrapperFactory();
    this.lazyLoadingEnabled = false;
    this.proxyFactory = new JavassistProxyFactory();
    this.mapperRegistry = new MapperRegistry(this);
    this.interceptorChain = new InterceptorChain();
    this.typeHandlerRegistry = new TypeHandlerRegistry();
    this.typeAliasRegistry = new TypeAliasRegistry();
    this.languageRegistry = new LanguageDriverRegistry();
    this.mappedStatements = (new Configuration.StrictMap("Mapped Statements collection")).conflictMessageProducer((savedValue, targetValue) -> {
        return ". please check " + savedValue.getResource() + " and " + targetValue.getResource();
    });
    this.caches = new Configuration.StrictMap("Caches collection");
    this.resultMaps = new Configuration.StrictMap("Result Maps collection");
    this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection");
    this.keyGenerators = new Configuration.StrictMap("Key Generators collection");
    this.loadedResources = new HashSet();
    this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers");
    this.incompleteStatements = new LinkedList();
    this.incompleteCacheRefs = new LinkedList();
    this.incompleteResultMaps = new LinkedList();
    this.incompleteMethods = new LinkedList();
    this.cacheRefMap = new HashMap();
    this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    this.typeAliasRegistry.registerAlias("LRU", LruCache.class);
    this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
    this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
    this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
    this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
    this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
    this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    this.languageRegistry.register(RawLanguageDriver.class);
}

到这里,做的最重要的操作是创建了一个Configuration对象,XMLConfigBuilder对象创建完成后,调用XMLConfigBuilder的pase()方法,该方法的主要作用是解析配置文件,并对配置文件中的各个节点进行处理,例如生成数据源对象DataSource,将Mapper.xml文件和持久层接口映射,保存Mapper.xml中的各个sql语句,以便后续执行sql时,获取对应的sql。其源码如下:

public Configuration parse() {
    //parsed用来标识是否已经在解析中,parsed = true表示已经有线程在启动了,无需再次加载信息,抛出异常
    if (this.parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    } else {
        //标识正在加载 
        this.parsed = true;
        //进行配置文件信息的解析,处理配置文件中的各个节点
        this.parseConfiguration(this.parser.evalNode("/configuration"));
        return this.configuration;
    }
}

继续跟踪parseConfiguration源码,其源码如下:
 

//XNode是配置文件流转化的对象,里面保存了配置文件中的各个节点信息,然后依次解析
private void parseConfiguration(XNode root) {
    try {
        //解析properties信息
        this.propertiesElement(root.evalNode("properties"));
        //解析settings信息
        Properties settings = this.settingsAsProperties(root.evalNode("settings"));
        this.loadCustomVfs(settings);
        this.loadCustomLogImpl(settings);
        //解析typeAliases信息
        this.typeAliasesElement(root.evalNode("typeAliases"));
        //解析plugins信息
        this.pluginElement(root.evalNode("plugins"));
        //解析objectFactory信息
        this.objectFactoryElement(root.evalNode("objectFactory"));
        //解析objectWrapperFactory信息
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        //解析reflectorFactory信息
        this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
        this.settingsElement(settings);
        //解析environments信息,得到数据源对象DataSource,后面执行sql语句连接数据库时会用到(重点理解)
        this.environmentsElement(root.evalNode("environments"));
        //解析databaseIdProvider信息
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        //解析typeHandlers信息
        this.typeHandlerElement(root.evalNode("typeHandlers"));
        //解析mappers信息,用于接口和Mapper.xml映射,解析xml中的sql语句(重点理解)
        this.mapperElement(root.evalNode("mappers"));
    } catch (Exception var3) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    }
}

这些文件的解析方法中this.environmentsElement(root.evalNode("environments"))和this.mapperElement(root.evalNode("mappers"))是需要重点关注的地方,源码分别如下:
this.environmentsElement(root.evalNode("environments"))源码:
 

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
        if (this.environment == null) {
            this.environment = context.getStringAttribute("default");
        }

        Iterator var2 = context.getChildren().iterator();

        while(var2.hasNext()) {
            XNode child = (XNode)var2.next();
            String id = child.getStringAttribute("id");
            if (this.isSpecifiedEnvironment(id)) {
                TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
                //获取dataSource信息
                DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
                //创建DataSource
                DataSource dataSource = dsFactory.getDataSource();
                Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
                //将数据源信息保存在Configuration对象中
                this.configuration.setEnvironment(environmentBuilder.build());
            }
        }
    }
}
//解析dataSource节点信息
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
        String type = context.getStringAttribute("type");
        //将dataSource节点转化为Properties对象
        Properties props = context.getChildrenAsProperties();
        DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
        factory.setProperties(props);
        return factory;
    } else {
        throw new BuilderException("Environment declaration requires a DataSourceFactory.");
    }
}
public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    Iterator var2 = this.getChildren().iterator();
    //解析数据源属性
    while(var2.hasNext()) {
        XNode child = (XNode)var2.next();
        String name = child.getStringAttribute("name");
        String value = child.getStringAttribute("value");
        if (name != null && value != null) {
            properties.setProperty(name, value);
        }
    }
    return properties;
}

执行的效果如下:

到这里可以看出来,我们配置在cogfig.xml文件中的数据库信息已经被解析出来了,并保存在上下文对象configuration中,解析到了数据源信息,后续在连接数据库的时候可以使用了。但是最终的目的是为了执行sql。sql语句必然也需要进行解析并保存起来,这部分操作就在this.mapperElement(root.evalNode("mappers"))中进行,其源码如下:

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
        Iterator var2 = parent.getChildren().iterator();
        //进行mapper的解析
        while(true) {
            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String resource;
                //优先解析package的配置形式,如果没有使用package的配置形式,再解析其他的配置形式
                //所以package形式的优先级是最高的
                if ("package".equals(child.getName())) {
                    resource = child.getStringAttribute("name");
                    this.configuration.addMappers(resource);
                } else {
                    //解析resource,url,class的配置形式,三者人选其一进行配置,我使用的是resourc的形式
                    resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    XMLMapperBuilder mapperParser;
                    InputStream inputStream;
                    //resource形式的配置方式解析
                    if (resource != null && url == null && mapperClass == null) {
                        ErrorContext.instance().resource(resource);
                        inputStream = Resources.getResourceAsStream(resource);
                        mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) {
                        ErrorContext.instance().resource(url);
                        inputStream = Resources.getUrlAsStream(url);
                        mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                        mapperParser.parse();
                    } else {
                        if (resource != null || url != null || mapperClass == null) {
                            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                        }

                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        this.configuration.addMapper(mapperInterface);
                    }
                }
            }
            return;
        }
    }
}

 由于我使用的是resource的形式,我就继续看resource的解析相关的源码,其解析源码如下:
 

if (resource != null && url == null && mapperClass == null) {
    ErrorContext.instance().resource(resource);
    //读取为流信息
    inputStream = Resources.getResourceAsStream(resource);
    //先将Mapper.xml转InputStream流,在将流为XMLMapperBuilder对象
    mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
    //对Mapper.xml进行解析
    mapperParser.parse();
}

继续跟进源码,进入mapperParser.parse()的源码进行阅读,源码如下:

public void parse() {
    if (!this.configuration.isResourceLoaded(this.resource)) {
        //解析sql的xml信息
        this.configurationElement(this.parser.evalNode("/mapper"));
        //将解析后的这个路径添加到loadedResources中
        this.configuration.addLoadedResource(this.resource);
        //将xml中namespace的接口和xml进行关联绑定,保存到mapperRegistry(map),
        //key是namespace反射得到的Class信息,value是一个MapperProxyFactory代理对象
        this.bindMapperForNamespace();
    }

    this.parsePendingResultMaps();
    this.parsePendingCacheRefs();
    this.parsePendingStatements();
}

这个方法只有几行代码,但是每一行都作用很大,重点看this.configurationElement的源码,详情如下:
 

//对Mapper.xml进行解析
private void configurationElement(XNode context) {
    try {
        //得到namespace的值
        String namespace = context.getStringAttribute("namespace");
        if (namespace != null && !namespace.equals("")) {
            this.builderAssistant.setCurrentNamespace(namespace);
            //获取二级缓存信息
            this.cacheRefElement(context.evalNode("cache-ref"));
            //一级缓存信息
            this.cacheElement(context.evalNode("cache"));
            //配置的parameterMap信息
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            //配置的resultMap信息
            this.resultMapElements(context.evalNodes("/mapper/resultMap"));
            //返回sql字段配置信息
            this.sqlElement(context.evalNodes("/mapper/sql"));
            //sql语句信息
            this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } else {
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
    } catch (Exception var3) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
    }
}
private void buildStatementFromContext(List<XNode> list) {
    if (this.configuration.getDatabaseId() != null) {
        this.buildStatementFromContext(list, this.configuration.getDatabaseId());
    }
    //解析逻辑
    this.buildStatementFromContext(list, (String)null);
}
private void buildStatementFromContext(List<XNode> list) {
    if (this.configuration.getDatabaseId() != null) {
        this.buildStatementFromContext(list, this.configuration.getDatabaseId());
    }
    //解析逻辑
    this.buildStatementFromContext(list, (String)null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    Iterator var3 = list.iterator();

    while(var3.hasNext()) {
        //sql语句信息
        XNode context = (XNode)var3.next();
        //创建XMLStatementBuilder对象
        XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);

        try {
            //构建MappedStatement执行对象,用于存储sql语句的各种信息
            statementParser.parseStatementNode();
        } catch (IncompleteElementException var7) {
            this.configuration.addIncompleteStatement(statementParser);
        }
    }
}

parseStatementNode是sql语句信息解析方法,将每一个sql中的信息都解析出来,生成一个MappedStatement对象,并保存到configuration中,源码如下:
 

//解析sql语句信息,生成MappedStatement对象
public void parseStatementNode() {
    String id = this.context.getStringAttribute("id");
    String databaseId = this.context.getStringAttribute("databaseId");
    if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        String nodeName = this.context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
        boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
        includeParser.applyIncludes(this.context.getNode());
        String parameterType = this.context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = this.resolveClass(parameterType);
        String lang = this.context.getStringAttribute("lang");
        LanguageDriver langDriver = this.getLanguageDriver(lang);
        this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
        String keyStatementId = id + "!selectKey";
        keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
        Object keyGenerator;
        if (this.configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
        } else {
            keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }

        SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
        StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        Integer fetchSize = this.context.getIntAttribute("fetchSize");
        Integer timeout = this.context.getIntAttribute("timeout");
        String parameterMap = this.context.getStringAttribute("parameterMap");
        String resultType = this.context.getStringAttribute("resultType");
        Class<?> resultTypeClass = this.resolveClass(resultType);
        String resultMap = this.context.getStringAttribute("resultMap");
        String resultSetType = this.context.getStringAttribute("resultSetType");
        ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
        if (resultSetTypeEnum == null) {
            resultSetTypeEnum = this.configuration.getDefaultResultSetType();
        }

        String keyProperty = this.context.getStringAttribute("keyProperty");
        String keyColumn = this.context.getStringAttribute("keyColumn");
        String resultSets = this.context.getStringAttribute("resultSets");
        //生成MappedStatement对象,并保存在configuration中
        this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}

将MappedStatement对象保存到configuration中,过程如下:
 

public MappedStatement addMappedStatement(String id, 
                                            SqlSource sqlSource, 
                                            StatementType statementType, 
                                            SqlCommandType sqlCommandType, 
                                            Integer fetchSize, 
                                            Integer timeout, 
                                            String parameterMap, 
                                            Class<?> parameterType, 
                                            String resultMap, 
                                            Class<?> resultType, 
                                            ResultSetType resultSetType, 
                                            boolean flushCache, 
                                            boolean useCache, 
                                            boolean resultOrdered, 
                                            KeyGenerator keyGenerator, 
                                            String keyProperty, 
                                            String keyColumn, 
                                            String databaseId, 
                                            LanguageDriver lang, 
                                            String resultSets) {
    if (this.unresolvedCacheRef) {
        throw new IncompleteElementException("Cache-ref not yet resolved");
    } else {
        id = this.applyCurrentNamespace(id, false);
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = (new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType)).resource(this.resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(this.getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired((Boolean)this.valueOrDefault(flushCache, !isSelect)).useCache((Boolean)this.valueOrDefault(useCache, isSelect)).cache(this.currentCache);
        ParameterMap statementParameterMap = this.getStatementParameterMap(parameterMap, parameterType, id);
        if (statementParameterMap != null) {
            statementBuilder.parameterMap(statementParameterMap);
        }
        //创建sql信息的MappedStatement对象
        MappedStatement statement = statementBuilder.build();
        //保存configuration中
        this.configuration.addMappedStatement(statement);
        return statement;
    }
}

到此,配置文件和Mapper.xml文件的加载解析,并保存到configuration的流程已经结束,这个过程中,主要进行的工作是: 

创建上下文对象configuration,并解析配置文件,Mapper.xml文件,并将解析出来的数据源信息,sql语句信息等保存与configuration对象中,以便后续使用

最后就是根据解析出来的消息创建SqlSessionFactory对象,调用的是build方法,源码如下:

public SqlSessionFactory build(Configuration config) {  
    return new DefaultSqlSessionFactory(config);  
}

3、创建SqlSession

        创建完SqlSessionFactory之后,就根据SqlSessionFactory进行SqlSession的创建过程了,SqlSession的创建过程比较简单,就是创建SqlSession对象并对一些属性赋值的过程,源码如下:

//第一步:使用SqlSessionFactory的openSession方法进行创建
SqlSession sqlSession = sqlSessionFactory.openSession();
//第二步:
public SqlSession openSession() {
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}

核心方法是openSessionFromDataSource中的处理过程,需要看一下这个方法的源码,源码如下:
 

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        //在上下文对象configuration中获取数据源信息
        Environment environment = this.configuration.getEnvironment();
        //创建事务管理TransactionFactory工厂
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        //创建事务对象
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //创建sql执行器
        Executor executor = this.configuration.newExecutor(tx, execType);
        //根据上下文对象,执行器创建SqlSession对象,并返回
        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;
}

创建SqlSession的过程就结束了,这里面只要是为了后面执行sql做准备。接下来我们来看看这些准备工作做好之后,怎么执行一条sql的过程。


4、执行sql并返回结果

        sql的执行分为两步走,一步是执行sql,一步是将执行后的数据进行映射为Java中的Object进行返回。我们直接开始看源码,源码中,我才用sqlSession.selectList的调用方式来进行源码跟踪,源码如下:
 

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        //获取构建MappedStatement对象,这个MappedStatement在创建SqlSessionFactory的时候创建的
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        //执行query查询方法
        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;
}

继续跟踪executor.query方法的逻辑,这个方法就是进行sql执行逻辑了。


4.1 执行sql语句

        我们可以从executor.query()方法的源码中得到执行的是CachingExecutor中query()方法,源码如下:
 

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //从MappedStatement中获取sql语句信息
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        //创建缓存
        CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
        //执行sql语句
        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

跟踪一下创建缓存的源码createCacheKey,详情如下:
 

public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        //创建一个CacheKey对象,记录sql的各种信息,并将CacheKey返回
        CacheKey cacheKey = new CacheKey();
        //记录sql的id
        cacheKey.update(ms.getId());
        //记录偏移量
        cacheKey.update(rowBounds.getOffset());
        //记录分页信息
        cacheKey.update(rowBounds.getLimit());
        //记录sql信息
        cacheKey.update(boundSql.getSql());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
        Iterator var8 = parameterMappings.iterator();

        while(var8.hasNext()) {
            ParameterMapping parameterMapping = (ParameterMapping)var8.next();
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                String propertyName = parameterMapping.getProperty();
                Object value;
                if (boundSql.hasAdditionalParameter(propertyName)) {
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                //记录sql参数信息
                cacheKey.update(value);
            }
        }
        if (this.configuration.getEnvironment() != null) {
            cacheKey.update(this.configuration.getEnvironment().getId());
        }
        return cacheKey;
    }
}

创建完缓存信息后,执行query()方法,源码如下:
 

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    //获取xml中配置的信息,没有配置的话,这里始终为null
    Cache cache = ms.getCache();
    if (cache != null) {
        this.flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
            this.ensureNoOutParams(ms, boundSql);
            List<E> list = (List)this.tcm.getObject(cache, key);
            if (list == null) {
                list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                this.tcm.putObject(cache, key, list);
            }

            return list;
        }
    }
    //执行查询
    return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

继续跟踪源码,详情如下:
 

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());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            //先查询一级缓存中看下是否存在查询结果,如果不存在,则查询数据库,如果存在则直接返回缓存数据
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                //针对StatementType.CALLABLE的类型进行处理,其他类型不进行任何处理
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                //查询数据库
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator var8 = this.deferredLoads.iterator();

            while(var8.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }
        return list;
    }
}

继续阅读查询数据库时的操作逻辑,方法this.queryFromDatabase的源码如下:

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    //先将查询sql缓存起来,value是一个EXECUTION_PLACEHOLDER枚举值
    this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

    List list;
    try {
        //处理查询的逻辑
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
	    //如果存在异常,删除缓存
        this.localCache.removeObject(key);
    }
    //更新缓存信息
    this.localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        this.localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

继续跟踪SimpleExecutor中的this.doQuery()方法的源码,如下:
 

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();
        //创建StatementHandler对象
        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;
}
//创建Statement对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    //获取数据库连接对象Connection
    Connection connection = this.getConnection(statementLog);
    //创建Statement对象
    Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}
//执行sql
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = this.boundSql.getSql();
    //执行sql
    statement.execute(sql);
    //处理返回结果
    return this.resultSetHandler.handleResultSets(statement);
}

到这里就执行完sql了,然后在this.resultSetHandler.handleResultSets(statement)中将查询的结果进行映射后返回。
 

4.2 执行结果返回
 

        结果处理返回的源码在DefaultResultSetHandler的handleResultSets方法中,其中核心点的注释如下:
 

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
        if (parentMapping != null) {
            this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
        } else if (this.resultHandler == null) {
            //处理逻辑入口
            DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
            this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
            multipleResults.add(defaultResultHandler.getResultList());
        } else {
            this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
        }
    } finally {
        this.closeResultSet(rsw.getResultSet());
    }
}

debug中可以看到rsw的具体数据信息如下:

继续跟踪handleRowValues方法的源码,如下:

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
        this.ensureNoRowBounds();
        this.checkResultHandler();
        this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        //处理逻辑的方法
        this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

跟踪handleRowValuesForSimpleResultMap方法源码,如下:

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext();
    ResultSet resultSet = rsw.getResultSet();
    this.skipRows(resultSet, rowBounds);

    while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
        ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
        //处理后的结果
        Object rowValue = this.getRowValue(rsw, discriminatedResultMap, (String)null);
        this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
}

 结果数据映射结果,结果返回,效果如下:

由结果可以得到,在调用this.getRowValue()方法后,就得到了我们需要的Java对象并且已经赋值了,所以需要跟踪一下this.getRowValue()方法的处理逻辑,源码如下:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //根据resultMap中的yupe属性创建对象,该对象仅仅是创建完成,还未进行属性赋值
    Object rowValue = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    if (rowValue != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        MetaObject metaObject = this.configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        if (this.shouldApplyAutomaticMappings(resultMap, false)) {
            //赋值处理
            foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = !foundValues && !this.configuration.isReturnInstanceForEmptyRow() ? null : rowValue;
    }
    return rowValue;
}
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    //创建还未映射的字段
    List<DefaultResultSetHandler.UnMappedColumnAutoMapping> autoMapping = this.createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {
        Iterator var7 = autoMapping.iterator();

        while(true) {
            DefaultResultSetHandler.UnMappedColumnAutoMapping mapping;
            Object value;
            do {
                if (!var7.hasNext()) {
                    return foundValues;
                }

                mapping = (DefaultResultSetHandler.UnMappedColumnAutoMapping)var7.next();
                //得到数据库字段的值
                value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
                if (value != null) {
                    foundValues = true;
                }
            } while(value == null && (!this.configuration.isCallSettersOnNulls() || mapping.primitive));
            //将数据库字段的值赋值给metaObject的对应的属性值,metaObject是有返回对象创建而来,可以理解为是Java对象
            //这里就实现了数据库数据和Java对象的映射
            metaObject.setValue(mapping.property, value);
        }
    } else {
        return foundValues;
    }
}

 以上就是mybatis的执行全过程源码的解读,当然,这并不是一个很详细的源码分析,主要在于对mybatis的主流程逻辑的处理分析。

相关推荐

  1. MyBatis-PageHelper 解说

    2024-06-12 08:46:07       32 阅读
  2. MyBatis-Plus 解说

    2024-06-12 08:46:07       20 阅读
  3. Mybatis总结

    2024-06-12 08:46:07       57 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-12 08:46:07       91 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-12 08:46:07       97 阅读
  3. 在Django里面运行非项目文件

    2024-06-12 08:46:07       78 阅读
  4. Python语言-面向对象

    2024-06-12 08:46:07       88 阅读

热门阅读

  1. Hive的存储格式和压缩算法的特点和选择

    2024-06-12 08:46:07       32 阅读
  2. React和Vue有什么区别

    2024-06-12 08:46:07       32 阅读
  3. ubuntu22.04禁止自动休眠的几种方式

    2024-06-12 08:46:07       29 阅读
  4. 算法训练营day53

    2024-06-12 08:46:07       33 阅读
  5. 代码随想录算法训练营day44

    2024-06-12 08:46:07       33 阅读
  6. 【环境搭建】3.阿里云ECS服务器 安装Redis

    2024-06-12 08:46:07       25 阅读
  7. CDN、CNAME、DNS

    2024-06-12 08:46:07       24 阅读