MyBatis source code analysis
A, use,
MyBatis = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis = MyBatis
@Test
public void test(a) throws Exception {
// Read the configuration file
InputStream resourceAsStream = Resources.getResourceAsStream("resources/sqlMapConfig.xml");
// Create SqlSessionFactory with SqlSessionFactoryBuilder
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// Obtain the SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
/ / call the specified Mapper method com. Wyh. Apply Mapper. UserMapper. QueryAll statementId
List<User> userList = sqlSession.selectList("com.wyh.mapper.UserMapper.queryAll");
System.out.println(userList);
}
Copy the code
Read the configuration file and convert it to an InputStream
/** * Returns a resource on the classpath as a Stream object@param loader The classloader used to fetch the resource
* @param resource The resource to find
* @return The resource
* @throws java.io.IOException If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
Copy the code
Create SqlSessionFactory
Create SqlSessionFactory from SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream) {
// Call overloaded methods
return build(inputStream, null.null);
}
/** * How to parse MyBatis config file **@paramInputStream Configuration file stream *@paramEnvironment Environment name *@paramProperties Configuration properties *@return SqlSessionFactory
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// Create XMLConfigBuilder to parse sqlmapconfig.xml configuration
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
The // parser.parse() method parses the configuration in sqlmapconfig.xml
// The build() method builds DefaultSqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.}}}Copy the code
3.1 Parsing configuration Files
XMLConfigBuilder is used to parse MyBatis sqlmapconfig.xml. Similar examples include XMLMapperBuilder, XMLStatementBuilder…. Their parent classes are baseBuilders, which are used to parse various configurations
Continuing with the code, the xmlConfigBuilder.parse method is called
public Configuration parse(a) {
// Check whether parsing is done
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
// Mark the resolved state as resolved
parsed = true;
// Parse the configuration under configuration
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
Copy the code
The parse method calls parseConfiguration() to parse the tag. This method parses properties, Settings, typeAliases, mapper…. These labels
private void parseConfiguration(XNode root) {
try {
// Parse the properties tag
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
// Parse the Settings tag
Properties settings = settingsAsProperties(root.evalNode("settings"));
// Load the custom vfsImpl
loadCustomVfs(settings);
// Load custom logImpl
loadCustomLogImpl(settings);
// Parse the typeAliases tag
typeAliasesElement(root.evalNode("typeAliases"));
// Parse the plugins tag
pluginElement(root.evalNode("plugins"));
// Parse the objectFactory tag
objectFactoryElement(root.evalNode("objectFactory"));
// Parse the objectWrapperFactory tag
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// Resolve the reflectorFactory tag
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// Resolve the environments tag
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
// Parse the databaseIdProvider tag
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// Parse the typeHandlers tag
typeHandlerElement(root.evalNode("typeHandlers"));
// Parse the mapper tag
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: "+ e, e); }}Copy the code
3.2 analytical properties
private void propertiesElement(XNode context) throws Exception {
if(context ! =null) {
Properties defaults = context.getChildrenAsProperties();
/ / get the resource
String resource = context.getStringAttribute("resource");
// Get the URL attribute
String url = context.getStringAttribute("url");
// Resource and URL cannot exist together
if(resource ! =null&& url ! =null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if(resource ! =null) {
// Put all resources in the defaults class
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if(url ! =null) {
// Place the properties obtained from the URL into the defaults class
defaults.putAll(Resources.getUrlAsProperties(url));
}
// Get the configuration in configuration
Properties vars = configuration.getVariables();
if(vars ! =null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
// Put the parsed properties back into configurationconfiguration.setVariables(defaults); }}Copy the code
3.3 analytical typeAliases
TypeAliases are used to configure aliases for parameterType and resultType in mapperXml configuration without fully qualified naming
TypeAliases configuration:
<typeAliases>
<! -- Define a path for a single alias. Type: type alias: alias -->
<! -- <typeAlias type="cn.wyh.entity.User" alias="user"/> -->
<! Mybatis automatically scans the Po class in the package, automatically defines the alias, the alias is the class name (the first letter can be capital or lowercase).
<package name="com.wyh.entity"/>
</typeAliases>
Copy the code
The typeAliasesElement method is used to parse the contents of the typeAliases tag
private void typeAliasesElement(XNode parent) {
if(parent ! =null) {
// Loop through all child nodes
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// Get the package name
String typeAliasPackage = child.getStringAttribute("name");
/ / class under scanning all packages, then register to the Configuration. The typeAliasRegistry
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// Get the alias
String alias = child.getStringAttribute("alias");
// Get the type
String type = child.getStringAttribute("type");
try{ Class<? > clazz = Resources.classForName(type);if (alias == null) {
/ / registered under the name of the class to the Configuration. The typeAliasRegistry
typeAliasRegistry.registerAlias(clazz);
} else {
/ / registered under configured alias alias to the Configuration. The typeAliasRegistrytypeAliasRegistry.registerAlias(alias, clazz); }}catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
Copy the code
After completion of parsing the alias information is stored in the Configuration. The typeAliasRegistry, MyBatis also comes with an alias Configuration, as follows:
public TypeAliasRegistry(a) {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
/ /... , there are many below, you can see the source code
}
Copy the code
3.4 analytical plugins
Plugins configuration:
<plugins>
<! -- Paging plugin -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
<! -- MyBatisPlus configuration -->
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<! -- Specify which generic mapper interface is currently used -->
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>
</plugins>
Copy the code
PluginElement is used to resolve the plugins Configuration, this method will all plugins Configuration register to the Configuration. The interceptorChain
private void pluginElement(XNode parent) throws Exception {
if(parent ! =null) {
for (XNode child : parent.getChildren()) {
// Parse the class name
String interceptor = child.getStringAttribute("interceptor");
// Parse the properties property
Properties properties = child.getChildrenAsProperties();
// Get an instance of the class
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
/ / Interceptor to registration to the Configuration. The interceptorChainconfiguration.addInterceptor(interceptorInstance); }}}Copy the code
3.5 analytical typeHandlers
TypeHandlers configuration information:
<typeHandlers>
<typeHandler handler="com.wyh.util.ExampleTypeHandler" javaType="java.util.Date" jdbcType="VARCHAR"/></typeHandlers>
Copy the code
TypeHandlerElement this method according to the Configuration of the javaType and corresponding jdbcType do then register to the Configuration. The typeHandlerRegistry
/** * Parse the typeHandlers configuration *@param parent
*/
private void typeHandlerElement(XNode parent) {
if(parent ! =null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// Get the configured package name
String typeHandlerPackage = child.getStringAttribute("name");
// Look for all classes in the package, and then scan the annotations in the class for javaTypeClass and jdbcType
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// Get the Java type
String javaTypeName = child.getStringAttribute("javaType");
// Get the JDBC type corresponding to the Java type
String jdbcTypeName = child.getStringAttribute("jdbcType");
/ / get handlerTypeName
String handlerTypeName = child.getStringAttribute("handler"); Class<? > javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<? > typeHandlerClass = resolveClass(handlerTypeName);if(javaTypeClass ! =null) {
if (jdbcType == null) {
// Register with typeHandlerRegistry
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
// Register with typeHandlerRegistrytypeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); }}else {
// Register with typeHandlerRegistry
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
Copy the code
3.6 analytical mapper
The mapperElement method is used to parse a mapper mapping file, and the code is as follows:
private void mapperElement(XNode parent) throws Exception {
if(parent ! =null) {
for (XNode child : parent.getChildren()) {
// Configuration is all classes in a package
if ("package".equals(child.getName())) {
// Get the package name
String mapperPackage = child.getStringAttribute("name");
/ / will package the next all classes to register to the Configuration. The mapperRegistry
configuration.addMappers(mapperPackage);
}
// A single resource file is configured
else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// Parse the resource configuration
if(resource ! =null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// Get the resource file stream
InputStream inputStream = Resources.getResourceAsStream(resource);
// Parse the file with XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
// Parse url resources
else if (resource == null&& url ! =null && mapperClass == null) {
ErrorContext.instance().resource(url);
// Get the resource file stream
InputStream inputStream = Resources.getUrlAsStream(url);
// Parse the file with XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
}
// Parse the mapperClass resource
else if (resource == null && url == null&& mapperClass ! =null) { Class<? > mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); }else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
Copy the code
3.6.1 XMLMapperBuilder. Parse
The XMLMapperBuilder object is used to parse mapper. XML. Its parent class is BaseBuilder.
public void parse(a) {
// Check whether the resource has been loaded
if(! configuration.isResourceLoaded(resource)) {// Load the mapper mapping file
configurationElement(parser.evalNode("/mapper"));
// Add the resource to the loaded list
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
// Parse the contents under the mapper tag
private void configurationElement(XNode context) {
try {
// Get the namespace attribute
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
/ / parsing parameterMap
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
/ / resultMap
resultMapElements(context.evalNodes("/mapper/resultMap"));
// Parse the SQL fragment
sqlElement(context.evalNodes("/mapper/sql"));
/ / parse the select | insert | update | delete tags Will eventually label content is parsed into MappedStatement and add to the Configuration. The mappedStatements
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: "+ e, e); }}Copy the code
3.6.1.1 buildStatementFromContext method
BuildStatementFromContext method is used to resolve the mapper. The XML SQL configuration:
private void buildStatementFromContext(List<XNode> list) {
if(configuration.getDatabaseId() ! =null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
/ / create XMLStatementBuilder
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch(IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); }}}Copy the code
Call statementParser. ParseStatementNode parse () method;
public void parseStatementNode(a) {
// The method id
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if(! databaseIdMatchesCurrent(id, databaseId,this.requiredDatabaseId)) {
return;
}
/ / the name of the tag < select | insert | update | delete >
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
// Check whether it is a query statement
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered".false);
// SQL fragment in XML
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
/ / get parameterType
String parameterType = context.getStringAttribute("parameterType");
// Look it up in the Alias configuration typeAliasRegistry based on the parameterType name. If not, use reflection to get the classClass<? > parameterTypeClass = resolveClass(parameterType); String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
// Get statementType for Sql execution
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
// Return type
String resultType = context.getStringAttribute("resultType"); Class<? > resultTypeClass = resolveClass(resultType);// Returns the Map type
String resultMap = context.getStringAttribute("resultMap");
// When retrieving the resultSetType mapping SQL results are returned
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
Copy the code
StatementParser again. ParseStatementNode () method calls the builderAssistant. AddMappedStatement method, Parses the attributes are encapsulated into stamentMap then stored to the Configuration. The mappedStatements
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 (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = newMappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, ! isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if(statementParameterMap ! =null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
}
Copy the code
3.7 Parsing Complete
After these Configuration files are parsed, the Configuration information is stored in the Configuration object, which is the data center of myBatis.
We come back to see the SqlSessionFactoryBuilder is. The build () method
/** * How to parse MyBatis config file **@paramInputStream Configuration file stream *@paramEnvironment Environment name *@paramProperties Configuration properties *@return SqlSessionFactory
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// Create XMLConfigBuilder to parse sqlmapconfig.xml configuration
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
The // parser.parse() method parses the configuration in sqlmapconfig.xml
// The build() method builds DefaultSqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.}}}public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
Copy the code
Builder method according to the Configuration object to construct a DefaultSqlSessionFactory, thus building a SqlSessionFactory building is completed
4. Obtain SqlSession
Since SqlSessionFactoryBuilder returns DefaultSqlSession, the openSession method is also in DefaultSqlSession
@Override
public SqlSession openSession(a) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null.false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// Get the database environment configured in the configuration file
final Environment environment = configuration.getEnvironment();
// Build TransactionFactory based on the database environment
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
/ / build the Transaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
ExecType =null; SimpleExecutor is built by default
final Executor executor = configuration.newExecutor(tx, execType);
/ / return DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally{ ErrorContext.instance().reset(); }}Copy the code
Execute the specified Sql
To call the SQL specified in mapper. XML, we call sqlSession.selectOne or selectList, passing in mapper.xml.namespace + Tag ID, because it is stored in this format when parsing tags above, example code:
List<User> userList = sqlSession.selectList("com.wyh.mapper.UserMapper.queryAll");
Copy the code
DefaultSqlSession.selectList
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// Obtain the MappedStatement according to statementId
MappedStatement ms = configuration.getMappedStatement(statement);
// Call the query method of BaseExecutor, the parent of SimpleExecutor
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally{ ErrorContext.instance().reset(); }}Copy the code
BaseExecutor.query
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
// Check if there is any in the cache
if(list ! =null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
}
// Do not exist in cache, query database
else{ list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }}finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482clearLocalCache(); }}return list;
}
Copy the code
BaseExecutor.queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// Execute the simpleExecutor.doQuery method
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
Copy the code
SimpleExecutor.doQuery
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
PreparedStatementHandler is returned
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// Assign values to parameters in SQL
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally{ closeStatement(stmt); }}Copy the code
preparedStatementHandler.query
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// Execute the SQL statement
ps.execute();
// Encapsulate the return result
return resultSetHandler.handleResultSets(ps);
}
Copy the code
So far a set of MyBatis execution process is resolved.
conclusion
The execution process of MyBatis
SqlSessionFactoryBuilder -> DefaultSqlSessionFactory -> DefaultSqlSession -> SimpleExecutor -> PreparedStatementHandler -> ResultSetHandler