Mybatis源码解读

news2024/11/19 13:29:36

        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的主流程逻辑的处理分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1811597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

过孔开窗、过孔盖油、过孔塞油、过孔塞树脂的比较

一、过孔开窗、过孔盖油、过孔塞油、过孔塞树脂的比较: 过孔开窗是指过孔的焊环上面裸露,不盖油墨。如果做表面处理喷锡的话,焊环这里就类似于贴片焊盘上锡,可以用来焊接作用。 过孔盖油指的是via过孔的焊环上面不裸露,覆盖油墨,为避免板子使用时有短路等情况。

霍尼韦尔落地灯好用吗?书客、霍尼韦尔、柏曼护眼大路灯对比较量!

我们都知道光线无处不在&#xff0c;想要减少近视隐患&#xff0c;就不得不提一下护眼灯了&#xff0c;特别是经常坐在电脑前码字的上班族以及深夜还在学习的学生党这一类人群&#xff0c;经常用眼光线不好不仅影响视力健康&#xff0c;还会影响效率。而一款护眼灯能够提供柔和…

人工智能术语

1、人工智能的概念 人工智能概念&#xff0c;在1956年召开的达特茅斯会议上正式被提出。该会议是由信息学鼻祖克劳德.艾尔伍德.香农(ClaudeElwoodShannon)以及马文.明斯基(Marvin Minsky)、约翰.麦卡锡(JohnMcCarthy)等十位信息学、数学、计算机学的科学先驱&#xff0c;在美国…

红黑树的基本原理

目录 一.概念与性质 二.基本操作 1.建树 2.插入 情况一 情况二 3.查找 4.验证 三.红黑树与AVL树的比较 一.概念与性质 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或 Black。 通过对任何一条从根…

构建大语言模型友好型网站

以大语言模型为代表的AI 技术迅速发展&#xff0c;将会影响原有信息网络的方式。其中一个明显的趋势是通过chatGPT 对话代替搜索引擎和浏览器来获取信息。 互联网时代&#xff0c;主要是通过网站&#xff08;website&#xff09;提供信息。网站主要为人类阅读的方式构建的。主要…

✊构建浏览器工作原理知识体系(网络协议篇)

🌻 前言 书接上回~ 系列文章目录: # ✊构建浏览器工作原理知识体系(开篇)# ✊构建浏览器工作原理知识体系(浏览器内核篇)# ✊构建浏览器工作原理知识体系(网络协议篇)✊构建浏览器工作原理知识体系(网页加载超详细全过程篇)为什么你觉得偶尔看浏览器的工作原理,…

【稳定检索/投稿优惠】2024年艺术、语言与文化交流国际会议(ALCE 2024)

2024 International Conference on Art, Language, and Cultural Exchange 2024年艺术、语言与文化交流国际会议 【会议信息】 会议简称&#xff1a;ALCE 2024 截稿时间&#xff1a;点击查看 大会地点&#xff1a;中国桂林 会议官网&#xff1a;www.icalce.com 会议邮箱&#…

重生奇迹mu套装掉的地点一览

1、目前只有三个地方掉套装&#xff1a;赤色要塞&#xff0c;不是100%掉&#xff0c;靠运气。卡利玛7&#xff0c;杀困顿能掉。魔炼之地&#xff0c;只有城主盟成员可以进入。 2、只有攻城城主盟可以进入的地图“魔炼之地”掉套装&#xff0c;暴率几乎为0。如果你是敏法的话&am…

深圳宝安餐饮行业揭秘:可燃气体报警器校准方法与周期的重要性

在日益注重餐饮安全的今天&#xff0c;深圳宝安区的餐饮行业也在不断探索和实践更加有效的安全管理措施。其中&#xff0c;可燃气体报警器的使用与校准成为了保障餐饮场所安全的重要一环。 在这篇文章中&#xff0c;佰德将详细解析可燃气体报警器的重要性、深圳宝安餐饮现状、…

LLM定制的四个层次

LLM(Large Language Models)代表了一种提高生产力的创新方法。他们能够简化各种任务&#xff0c;显著提高整体效率。从提示工程到Agents可以分为四个层次。 Level-1: Prompt engineering Prompt是简明的输入文本&#xff0c;用作查询或指令&#xff0c;引导语言模型产生所需输…

借助Historian Connector + TDengine,打造工业创新底座

在工业自动化的领域中&#xff0c;数据的采集、存储和分析是实现高效决策和操作的基石。AVEVA Historian (原 Wonderware Historian) 作为领先的工业实时数据库&#xff0c;专注于收集和存储高保真度的历史工艺数据。与此同时&#xff0c;TDengine 作为一款专为时序数据打造的高…

关于会议论文/CPCI/ISTP会议论文

关于会议论文 会议论文是公开发表的学术论文&#xff0c;一般正式的国际学术会议都会公开征稿&#xff0c;并要求录用的论文在会议上进行宣读、交流&#xff0c;然后集结出版&#xff0c;这就是我们常说的会议论文集&#xff0c;而这些发表的论文也可用于硕博毕业、项目结题、…

视频直播点播EasyDSS平台授权时,出现授权时间即将到期的提示是什么原因?

视频直播点播EasyDSS平台具备灵活的视频能力&#xff0c;包括直播、点播、转码、管理、录像、检索、时移回看等&#xff0c;平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等能力服务&#xff0c;可应用在无人机推流、在线直播、虚拟直播、远程培训等场景中。…

跃入AI新纪元:亚马逊云科技LLM全景培训,解锁AI构建者之路

亲爱的技术爱好者们&#xff0c;你是否也对大语言模型&#xff08;LLM&#xff09;的神奇魅力所吸引&#xff0c;渴望深入探索其背后的技术奥秘&#xff1f;今天&#xff0c;我要为大家推荐一份超级硬核的学习资源——亚马逊云科技 对话AI 构建者&#xff1a;从基础到应用的LLM…

Linux安装Docker | 使用国内镜像

环境 CentOS7 先确认能够上网 curl www.baidu.com返回该输出说明网络OK 步骤一&#xff1a;安装gcc 和 gcc-c yum -y install gccyum -y install gcc-c步骤二&#xff1a;安装Docker仓库 yum install -y yum-utils接下来配置yum的国内镜像 yum-config-manager --add-re…

基于变分自动编码器VAE的电池剩余使用寿命RUL估计

加载模块 import math import itertools import numpy as np import pandas as pd import seaborn as sns import tensorflow as tf from keras import layers from sklearn.svm import SVR from tensorflow import keras from keras import backend as K import matplotlib.p…

Unity | Shader基础知识(番外:了解内置Shader-Standard-含specular模式<二>)

目录 前言 一、Standard参数详解 1.NormalMap法线贴图 2.HeightMap高度贴图 3.Occlusion遮挡贴图 4.DetailMask细节遮挡 5.Emission自发光 6.Tiling铺地砖和Offset偏移度 二、Standard-Specular setup模式 三、作者的碎碎念 前言 Unity | Shader基础知识(番外&#xf…

【ATU Book-i.MX8系列 - TFLite 进阶】 NXP i.MX8M Plus 实现高效 Mobilenet SSD 物体检测

NXP i.MX8M Plus 实现高效 Mobilenet SSD 物体检测 一、概述 在 边缘运算(Edge Computing) 领域中&#xff0c;轻量级的模型扮演着举足轻重的角色。因此&#xff0c;如何在有限硬体资源下实现电脑视觉&#xff08;Computer vision&#xff09; 应用是个极具挑战性的课题。特别…

Flow Launcher:Windows高效启动与搜索工具

目录 一、软件简介 二、软件安装 三、软件功能介绍 3.1 快速启动应用 3.2 文件快速搜索 3.3 多功能操作中心 3.4 支持插件扩展 一、软件简介 Flow Launcher 是基于C#编程语言开发一款专为Windows设计的高效启动与搜索工具&#xff0c;它以创新简洁的界面重新定义了用户…

基于SWIFT和Qwen1.5-14B-Chat进行大模型LoRA微调测试

基于SWIFT和Qwen1.5-14B-Chat进行大模型LoRA微调测试 环境准备 基础环境 操作系统&#xff1a;Ubuntu 18.04.5 LTS (GNU/Linux 3.10.0-1127.el7.x86_64 x86_64)Anaconda3&#xff1a;Anaconda3-2023.03-1-Linux-x86_64根据服务器网络情况配置好conda源和pip源&#xff0c;此…