Mybatis学习之工作流程代码详解数据库

一、整体流程

  Mybatis是一种ORM对象关系映射架构,实现Java Object和数据库字段映射。

        Mybatis学习之工作流程代码详解数据库

   如上图所示,Mybatis就是根据Java配置的数据源(driver、url、username、password)以及Mapper配置SQL(DQL查询、DML修改、DDL create)语句,基于JDBC底层的实现原理实现对数据库的操作。所以整体来说Mybatis工作大致分为三步:获取数据源、拿到需要执行的SQL、执行SQL语句并得到结果。我们以下面的程序入口来查看源码流程:

public class AppTest { 
    @Test 
    public void test() throws IOException { 
        String resource = "mybatis-config.xml"; 
        InputStream inputStream = Resources.getResourceAsStream(resource); 
        SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream); 
        SqlSession session = sqlSessionFactory.openSession(); 
        try { 
            User user = session.selectOne("org.mybatis.example.UserMapper.selectUser", 1); 
            System.out.println("user:{}"+user); 
        } finally { 
            session.close(); 
        } 
    } 
}

二、获取数据源

String resource = "mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);

  如上代码所示,首先我们先来看看数据源的获取,查看如下代码,并进入到build方法:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { 
    SqlSessionFactory var5; 
    try { 
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 
        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; 
}

  然后我们可以看到XMLConfigBuilder对XML进行parse:

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { 
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); 
} 
 
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { 
    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; 
} 
 
public Configuration parse() { 
    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; 
    } 
} 
 
private void parseConfiguration(XNode root) { 
    try { 
        this.propertiesElement(root.evalNode("properties")); 
        Properties settings = this.settingsAsProperties(root.evalNode("settings")); 
        this.loadCustomVfs(settings); 
        this.loadCustomLogImpl(settings); 
        this.typeAliasesElement(root.evalNode("typeAliases")); 
        this.pluginElement(root.evalNode("plugins")); 
        this.objectFactoryElement(root.evalNode("objectFactory")); 
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 
        this.reflectorFactoryElement(root.evalNode("reflectorFactory")); 
        this.settingsElement(settings); 
        this.environmentsElement(root.evalNode("environments")); 
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); 
        this.typeHandlerElement(root.evalNode("typeHandlers")); 
        this.mapperElement(root.evalNode("mappers")); 
    } catch (Exception var3) { 
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); 
    } 
}

  可以看到我们进行了typeAliases以及environments元素的加载,这实际就进行了数据源的XML解析以及获取数据源信息,如下配置所示:

<configuration> 
    <properties resource="config.properties"/> 
    <environments default="development"> 
        <environment id="development"> 
            <transactionManager type="JDBC"/> 
            <dataSource type="POOLED"> 
                <property name="driver" value="${driver}"/> 
                <property name="url" value="${url}"/> 
                <property name="username" value="${username}"/> 
                <property name="password" value="${password}"/> 
            </dataSource> 
        </environment> 
    </environments> 
    <mappers> 
        <mapper resource="mybatis/UserMapper.xml"/> 
    </mappers> 
</configuration>

  接下来我们看看environmentsElement方法内部如何解析数据源:

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")); 
                DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource")); 
                DataSource dataSource = dsFactory.getDataSource(); 
                Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource); 
                this.configuration.setEnvironment(environmentBuilder.build()); 
            } 
        } 
    } 
} 
private DataSourceFactory dataSourceElement(XNode context) throws Exception { 
    if (context != null) { 
        String type = context.getStringAttribute("type"); 
        Properties props = context.getChildrenAsProperties(); 
        DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).getDeclaredConstructor().newInstance(); 
        factory.setProperties(props); 
        return factory; 
    } else { 
        throw new BuilderException("Environment declaration requires a DataSourceFactory."); 
    } 
}

  如图所示,可以知道一共有三种DataSourceFactory实现:

        Mybatis学习之工作流程代码详解数据库

  我们看下我们获取了配置之后,会在Configuration中通过setEnvironment存储环境信息,首先我们来看看Configuration类:

public class Configuration { 
    protected Environment environment; 
} 
 
public final class Environment { 
    private final String id; 
    private final TransactionFactory transactionFactory; 
    private final DataSource dataSource; 
}

  因此可以知道数据源信息就存储在Configuration中的Environment属性中。如图所示:

          Mybatis学习之工作流程代码详解数据库

  因此整个流程代码就是:

>org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)  
 >org.apache.ibatis.builder.xml.XMLConfigBuilder 
  >org.apache.ibatis.builder.xml.XMLConfigBuilder.parse 
   >org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement 
     >org.apache.ibatis.datasource.DataSourceFactory.getDataSource 
       >org.apache.ibatis.session.Configuration.setEnvironment###### set数据源

  流程图如下:

    Mybatis学习之工作流程代码详解数据库

三、SQL获取

  在我们成功获取了所有数据源信息后,接下来就是获取SQL执行语句,在上面的配置加载过程中,有这么一句会进行Mapper的加载:

private void parseConfiguration(XNode root) { 
    try { 
        this.propertiesElement(root.evalNode("properties")); 
        Properties settings = this.settingsAsProperties(root.evalNode("settings")); 
        this.loadCustomVfs(settings); 
        this.loadCustomLogImpl(settings); 
        this.typeAliasesElement(root.evalNode("typeAliases")); 
        this.pluginElement(root.evalNode("plugins")); 
        this.objectFactoryElement(root.evalNode("objectFactory")); 
        this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 
        this.reflectorFactoryElement(root.evalNode("reflectorFactory")); 
        this.settingsElement(settings); 
        this.environmentsElement(root.evalNode("environments")); 
        this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); 
        this.typeHandlerElement(root.evalNode("typeHandlers")); 
        this.mapperElement(root.evalNode("mappers")); 
    } catch (Exception var3) { 
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); 
    } 
}

  我们进入到方法内部进行查看,如下代码所示:

private void mapperElement(XNode parent) throws Exception { 
    if (parent != null) { 
        Iterator var2 = parent.getChildren().iterator(); 
 
        while(true) { 
            while(var2.hasNext()) { 
                XNode child = (XNode)var2.next(); 
                String resource; 
                if ("package".equals(child.getName())) { 
                    resource = child.getStringAttribute("name"); 
                    this.configuration.addMappers(resource); 
                } else { 
                    resource = child.getStringAttribute("resource"); 
                    String url = child.getStringAttribute("url"); 
                    String mapperClass = child.getStringAttribute("class"); 
                    XMLMapperBuilder mapperParser; 
                    InputStream inputStream; 
                    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; 
        } 
    } 
}

  可以看到mapper的解析有四种方式(resource、url、class、package),其实对应配置方式就是:

    <mappers> 
        <mapper resource="mybatis/UserMapper.xml"/> 
        <mapper url="file:///var/mappers/AuthorMapper.xml"/> 
        <mapper class="com.generator.mapper.UserMapper"></mapper> 
        <package name="com.generator.mapper"></package> 
    </mappers>

  我们以XML为例在方法内部查看时如何进行解析的,进入到XMLMapperBuilder中的parse流程:

public void parse() { 
    if (!this.configuration.isResourceLoaded(this.resource)) { 
        this.configurationElement(this.parser.evalNode("/mapper")); 
        this.configuration.addLoadedResource(this.resource); 
        this.bindMapperForNamespace(); 
    } 
 
    this.parsePendingResultMaps(); 
    this.parsePendingCacheRefs(); 
    this.parsePendingStatements(); 
}private void configurationElement(XNode context) { 
    try { 
        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")); 
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap")); 
            this.resultMapElements(context.evalNodes("/mapper/resultMap")); 
            this.sqlElement(context.evalNodes("/mapper/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, String requiredDatabaseId) { 
    Iterator var3 = list.iterator(); 
 
    while(var3.hasNext()) { 
        XNode context = (XNode)var3.next(); 
        XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId); 
 
        try { 
            statementParser.parseStatementNode(); 
        } catch (IncompleteElementException var7) { 
            this.configuration.addIncompleteStatement(statementParser); 
        } 
    } 
}

  然后我们看到了statementParse的parseStatementNode方法,进入到方法,可以看到:

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"); 
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); 
} 
} 
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); 
} 
MappedStatement statement = statementBuilder.build(); 
this.configuration.addMappedStatement(statement); 
return statement; 
} 
}

  因此我们发现SQL语句也被加载到Configuration中了,如图所示:

          Mybatis学习之工作流程代码详解数据库

  因此我们可以知道流程是这样子的:

>org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)  
 >org.apache.ibatis.builder.xml.XMLConfigBuilder 
>org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement 
>org.apache.ibatis.builder.xml.XMLMapperBuilder 
>org.apache.ibatis.builder.xml.XMLMapperBuilder.parse 
>org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>) 
>org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode 
>org.apache.ibatis.session.Configuration.addMappedStatement ######set 这个sql

  流程图如下所示:

     Mybatis学习之工作流程代码详解数据库

四、SQL执行

  接下来我们看看怎么用存储的数据源信息以及SQL语句进行执行获取结果:

SqlSession session = sqlSessionFactory.openSession(); 
try { 
User user = session.selectOne("org.mybatis.example.UserMapper.selectUser", 1); 
System.out.println("user:{}"+user); 
} finally { 
session.close(); 
}

  我们先看看openSession,我们以SqlSessionFactory的默认实现DefaultSqlSessionFactory为例,代码如下:

public class DefaultSqlSessionFactory implements SqlSessionFactory { 
private final Configuration configuration; 
public SqlSession openSession() { 
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false); 
} 
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { 
Transaction tx = null; 
DefaultSqlSession var8; 
try { 
Environment environment = this.configuration.getEnvironment(); 
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); 
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); 
Executor executor = this.configuration.newExecutor(tx, execType); 
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); 
} catch (Exception var12) { 
this.closeTransaction(tx); 
throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12); 
} finally { 
ErrorContext.instance().reset(); 
} 
return var8; 
} 
}

  可以看见我们初始化了一个SQLSession,接下来我们看selectOne如何调用SQL:

public <T> T selectOne(String statement) { 
return this.selectOne(statement, (Object)null); 
} 
public <T> T selectOne(String statement, Object parameter) { 
List<T> list = this.selectList(statement, parameter); 
if (list.size() == 1) { 
return list.get(0); 
} else if (list.size() > 1) { 
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); 
} else { 
return null; 
} 
} 
public <E> List<E> selectList(String statement, Object parameter) { 
return this.selectList(statement, parameter, RowBounds.DEFAULT); 
} 
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 
List var5; 
try { 
MappedStatement ms = this.configuration.getMappedStatement(statement); 
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; 
}

  我们可以看到会获取MappedStatement,然后调用executor进行执行,我们以SimpleExecutor为例,代码如下:

public class SimpleExecutor extends BaseExecutor { 
public SimpleExecutor(Configuration configuration, Transaction transaction) { 
super(configuration, transaction); 
}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 handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); 
stmt = this.prepareStatement(handler, ms.getStatementLog()); 
var9 = handler.query(stmt, resultHandler); 
} finally { 
this.closeStatement(stmt); 
} 
return var9; 
} 
}

  我们接下来随便查看一个PreparedStatement的实现类PreparedStatementWrapper,代码如下:

public class PreparedStatementHandler extends BaseStatementHandler { 
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { 
PreparedStatement ps = (PreparedStatement)statement; 
ps.execute(); 
return this.resultSetHandler.handleResultSets(ps); 
} 
}

  继续看结果处理:

public class DefaultResultSetHandler implements ResultSetHandler { 
public List<Object> handleResultSets(Statement stmt) throws SQLException { 
ErrorContext.instance().activity("handling results").object(this.mappedStatement.getId()); 
List<Object> multipleResults = new ArrayList(); 
int resultSetCount = 0; 
ResultSetWrapper rsw = this.getFirstResultSet(stmt); 
List<ResultMap> resultMaps = this.mappedStatement.getResultMaps(); 
int resultMapCount = resultMaps.size(); 
this.validateResultMapsCount(rsw, resultMapCount); 
while(rsw != null && resultMapCount > resultSetCount) { 
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount); 
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null); 
rsw = this.getNextResultSet(stmt); 
this.cleanUpAfterHandlingResultSet(); 
++resultSetCount; 
} 
String[] resultSets = this.mappedStatement.getResultSets(); 
if (resultSets != null) { 
while(rsw != null && resultSetCount < resultSets.length) { 
ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]); 
if (parentMapping != null) { 
String nestedResultMapId = parentMapping.getNestedResultMapId(); 
ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId); 
this.handleResultSet(rsw, resultMap, (List)null, parentMapping); 
} 
rsw = this.getNextResultSet(stmt); 
this.cleanUpAfterHandlingResultSet(); 
++resultSetCount; 
} 
} 
return this.collapseSingleResultList(multipleResults); 
} 
}

  接下来看ResultSetWrapper:

    public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException { 
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 
this.resultSet = rs; 
ResultSetMetaData metaData = rs.getMetaData(); 
int columnCount = metaData.getColumnCount(); 
for(int i = 1; i <= columnCount; ++i) { 
this.columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i)); 
this.jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i))); 
this.classNames.add(metaData.getColumnClassName(i)); 
        } 
}

  因此可以看出为什么Mybatis是一个ORM模型架构,因为实现了数据库列和Java对象属性的一一映射,如下图所示:

  Mybatis学习之工作流程代码详解数据库

  综上所述,SQL执行代码流程如下:

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession() 
>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource 
>org.apache.ibatis.session.Configuration.newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType) 
>org.apache.ibatis.session.defaults.DefaultSqlSession.DefaultSqlSession(org.apache.ibatis.session.Configuration, org.apache.ibatis.executor.Executor, boolean) 
>org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(java.lang.String, java.lang.Object) 
>org.apache.ibatis.executor.SimpleExecutor.doQuery 
>org.apache.ibatis.executor.statement.PreparedStatementHandler.query 
>org.apache.ibatis.executor.resultset.ResultSetWrapper.ResultSetWrapper

  因此SQL执行的时序图如下图所示:

            Mybatis学习之工作流程代码详解数据库

  而且根据以上代码,可以看出Mybatis是基于JDBC实现的,也是使用Connection、Statement、PrepareStatement、ResultSet进行SQL运行。

五、Mybatis执行流程总结

  根据代码走读,可以看出Mybatis执行流程如下所示:

  • Mybatis根据配置项将数据源DataSource和Mapper加载到Configuration的environment和statement中;
  • SqlSessionFactoryBuilder根据Configuration配置的数据源信息build一个SQLSessionFactory;
  • SqlSessionFactory工厂通过OpenSession创建一个SqlSession;
  • SqlSession调用自身的Executor根据COnfiguration中的statement配置创建一个StatementHandler进行SQL执行;
  • StatementHandler执行的结果通过ResultSetWrapper实现数据库列和Java类属性列一一映射,得到Java结果。 

       Mybatis学习之工作流程代码详解数据库

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/3777.html

(0)
上一篇 2021年7月16日
下一篇 2021年7月16日

相关推荐

发表回复

登录后才能评论