MyBatis - underlying source code analysis - (detailed), Java development for three years, the monthly salary is only 12K

                } else {
                    /**
                     * The following code shows that only one of the three attributes of url resource class can be selected, otherwise an error will be reported
                     */
                    throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                }
            }
        }
    }
}

}

## 1.4. 2: mapperParser.parse(); analysis

**my UserMapper.xml The contents of the document are:**

<?xml version="1.0" encoding="UTF-8"?> select * from `user` where userId = #{userId}
**UserMapper The interface contents are:**

package com.mapper;

import com.entity.User;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {
@Select("select * from user where userId = 2")
User findById(int userId);
}

**doubt? UserMapper.xml have<select id="findById",And in the interface findById I added another method@Select Notes; Which one will be selected for execution Sql Execution or error reporting?**

**with UserMapper.xml As an example, you can see resource = mapper/UserMapper.xml**
![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-04c09c0e358b0694?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
/**
*Parse mapper XML file content
/
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
/*
*Parse mapper XML
*Annotation information of {UserMapper} interface specified by namespace
*/
bindMapperForNamespace();
}

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

}

## 1.4.3: configurationElement(parser.evalNode("/mapper"));

**analysis mapper.xml File content**

public class XMLMapperBuilder extends BaseBuilder {
private void configurationElement(XNode context) {
try {
/**
*namespace property
/
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
/*
*If you do not specify a namespace, an error will be reported. Therefore, you know that the namespace attribute must be specified
/
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
/*
*Parsing cache ref
/
cacheRefElement(context.evalNode("cache-ref"));
/*
*Parse cache
/
cacheElement(context.evalNode("cache"));
/*
*Resolve parameterMap
/
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
/*
*Parse resultMap
/
resultMapElements(context.evalNodes("/mapper/resultMap"));
/*
*Parsing sql
/
sqlElement(context.evalNodes("/mapper/sql"));
/*
*Parsing sql statement select Insert update delete
*Focus on the analysis here. The analysis here will be associated with the execution method sql statement mapping of the mapper interface
*/
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);
}
}
}

## 1.4.4: buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

**analysis sql sentence select|insert|update|delete;list The parameter content is select|insert|update|delete of Sql sentence**
**XMLStatementBuilder Sql Statement parser**
![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-e9b8bf020064dd10?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 1.4. 5: statementParser.parseStatementNode(); Parsing Sql statements

builderAssistant.addMappedStatement,Not adding one mapper.xml An instance of file insinuation,<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">But for everyone Sql Statement to create an instance</mark>

public class XMLStatementBuilder extends BaseBuilder {
private final MapperBuilderAssistant builderAssistant;
public void parseStatementNode() {
/**
*A big push code is omitted here
/
/*
*
* select * from user where userId = #{userId}
*
*Parameter analysis
*
*id: id specified by label = findbyid
*sqlSource: Sql statement, Sql parameter placeholder
*statementType: sql execution type reference {@ link StatementType}
*STATEMENT: directly operate sql without precompiling ${}
*PREPARED: preprocessing, parameter, precompiling #{}
*CALLABLE: execute stored procedure
*sqlCommandType: sql statement type reference {@ link SqlCommandType}
*UNKNOWN: UNKNOWN, INSERT: new, UPDATE: modify, DELETE: DELETE, SELECT: query, FLUSH: refresh
*
*Other parameters can be viewed on the official website: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
*/
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}

## 1.4.6: builderAssistant.addMappedStatement();

**Create a MappedStatement Add instance to Configuration.mappedStatements of Map In collection**

public class XMLStatementBuilder extends BaseBuilder {
public MappedStatement addMappedStatement() {
/**
*A big push code is omitted here
/
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
/*
*Builder mode
*Sets the properties of the MappedStatement
*A big push code is omitted here
*/

    /**
     * Set the parameterType property of the parameter input type
     * <select id="findById" parameterType="int" resultType="com.entity.User">
     *    select * from `user` where userId = #{userId}
     * </select>
     */
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
        statementBuilder.parameterMap(statementParameterMap);
    }

    /**
     * Create an instance of {@ link MappedStatement}
     */
    MappedStatement statement = statementBuilder.build();

    /**
     * MappedStatement The instance is added to the {@ link #configuration.mappedStatements} Map collection
     * MappedStatement Is an instance object corresponding to a Sql statement
     *
     * configuration.mappedStatements Store all MappedStatement instances, which will be described in detail later
     */
    configuration.addMappedStatement(statement);
    return statement;
}

}

## After the above process is completed, return to < mark style = "box sizing: border box; Outline: 0px; background color: rgb (248, 248, 64); color: rgb (0, 0, 0); overflow Wrap: break word;" > 1.4. 2: mapperParser.parse(); Parse < / mark >, now start parsing the annotation information of the interface specified by namespace, and create the proxy factory object of the interface, UserMapper interface.

![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-380c8b0d4e8e7797?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 1.4.7: bindMapperForNamespace();

**Start parsing the interface annotation and add one<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">MapperProxyFactory</mark>Agent factory object to<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">configuration.mapperRegistry.knownMappers</mark>;key yes<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">Mapper Interface</mark>**

public class XMLMapperBuilder extends BaseBuilder {
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
/**
*java reflection class classForName
/
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
/*
*There is no exception thrown here, indicating that namespace can specify an interface that does not exist
/
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
/*
*Add a proxy factory object of Mapper interface to configuration mapperRegistry. In the knownmappers collection
*Refer to {@ link Configuration#mapperRegistry},
* {@link MapperRegistry#knownMappers}
*/
configuration.addMapper(boundType);
}
}
}
}
}

## 1.4.8: configuration.addMapper(boundType);

public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public void addMapper(Class type) {
/**
*mapperRegistry = {@link MapperRegistry} mapper interface registrar, which stores all mapper interface information
*/
mapperRegistry.addMapper(type);
}
}

**mapperRegistry.addMapper(type);
by Mapper Interface to create a proxy factory for later use Mapper Interface to create a proxy class**
**analysis mapper Annotation information of interface**

public class MapperRegistry {
public void addMapper(Class type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
/**
*{@ link MapperProxyFactory} proxy interface factory
/
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
/*
*{@ link MapperAnnotationBuilder} mapper interface annotation parser
/
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
/Start parsing*/
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}

## 1.4.9 parser.parse(); MapperAnnotationBuilder.parse()

**analysis mapper Annotation information of the interface, parseStatement(method)Mainly in this method**

public class MapperAnnotationBuilder {
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
/**
*Resolve @ CacheNamespace annotation
/
parseCache();
/*
*Parsing CacheNamespaceRef annotation
/
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
/*
*Parse Sql related annotation columns, such as @ Select|@Update
*
*Focus on
/
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
/*
*Analytical undetermined method
*/
parsePendingMethods();
}
}

## 1.5.0: parseStatement(method);

**Create a MappedStatement Add instance to Configuration.mappedStatements of Map In collection**

public class MapperAnnotationBuilder {
private final MapperBuilderAssistant assistant;
void parseStatement(Method method) {
/**
*A big push code is omitted here
*/
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}

## 1.5.1: assistant.addMappedStatement();

**<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">Here you can refer to 1.4.6: builderAssistant.addMappedStatement();As like as two peas</mark>**
<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">**All called configuration.addMappedStatement(statement);**</mark>
![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-7a172e142e5239a8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

**Here is the key analysis Configuration.addMappedStatement(statement);What are you doing and solving <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">1.4.2 Remaining doubts; UserMapper.xml and UserMapper All interfaces findById of Sql Statement definition</mark>**

public class Configuration {
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
}

**mappedStatements.put(ms.getId(), ms); Actual call Configuration.StrictMap.put()method**
**<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">Configuration.StrictMap Is a rewritten HashMap,put Method will check first key Does it exist</mark>**

public class Configuration {
/**
*Mappedstatements object of SQL statement
*
* Configuration.StrictMap implements HashMap
*/
protected final Map<String, MappedStatement> mappedStatements = new Configuration.StrictMap("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());

protected static class StrictMap<V> extends HashMap<String, V> {
    @Override
    @SuppressWarnings("unchecked")
    public V put(String key, V value) {
        /**
         * key Throw an exception if it exists
         *
         * Therefore, only one method mapping Sql statement can be defined, either in mapper Defined in XML or annotated
         */
        if (containsKey(key)) {
            throw new IllegalArgumentException(name + " already contains value for " + key
                    + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
        }
        if (key.contains(".")) {
            final String shortKey = getShortName(key);
            if (super.get(shortKey) == null) {
                super.put(shortKey, value);
            } else {
                super.put(shortKey, (V) new org.apache.ibatis.session.Configuration.StrictMap.Ambiguity(shortKey));
            }
        }
        return super.put(key, value);
    }
}

}

**debug Commissioning, key If it already exists, an error will be reported. Thus, the method mapping Sql Only one statement can be defined, or mapper.xml Defined in or annotated**
![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-a866e4089e7aa556?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## At this point, the start-up process of MyBatis is over.

## <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;"> 2: Next, let's look at how to execute Sql queries</ mark>

public class Main {
public static void main(String[] args) throws IOException {
String configName = "mybatis_config.xml";
Reader reader = Resources.getResourceAsReader(configName);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

    /**
     * Get a session connection
     */
    SqlSession sqlSession = sqlSessionFactory.openSession();

    /**
     * Get the proxy class of UserMapper
     */
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    /**
     * Execute Sql query
     */
    User user = userMapper.findById(1);
    System.out.println(user);
}

}

> **Output results: User{userId=1, username='Zhang San', sex='male', age=12}**

**<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">One line of code query, since the bottom layer has gone through so many processes;</mark>**

## flow chart:

![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-459d4ed3e065a0d5?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 2.1: sqlSessionFactory.openSession(); Open session connection

**call DefaultSqlSessionFactory.openSessionFromDataSource();**

public class DefaultSqlSessionFactory implements SqlSessionFactory {
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
/**
* mybatis_config.xml configured
*Data source
*/
final Environment environment = configuration.getEnvironment();

        /**
         * transactionManager Configured transaction manager factory type = "JDBC" {@ link jdbctransactionfactory}
         *<environments default="developmentss">
         *    <environment id="developmentss">
         *        <transactionManager type="JDBC"></transactionManager>
         *    </environment>
         *  </environments>
         */
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

        /**
         * Create a transaction manager because the transaction manager factory specified above is {@ link JdbcTransactionFactory}
         * So the transaction manager created is {@ link JdbcTransaction}
         *
         * @param level Transaction isolation level
         * @param autoCommit Automatically commit transactions
         */
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

        /**
         * Create Sql executor
         *
         * @param execType Create an actuator type defaultExecutorType. If not specified, the default is SIMPLE
         *                 <settings>
         *                     <setting name="defaultExecutorType" value="SIMPLE"/>
         *                 </settings>
         */
        final Executor executor = configuration.newExecutor(tx, execType);

        /**
         * Create a default SqlSession instance
         */
        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();
    }
}

}

## 2.2: configuration.newExecutor(tx, execType); Create actuator

public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
/**
*The executor not only reuses statements, but also performs batch updates
/
executor = new BatchExecutor(this, transaction);
/*
*The executor reuses the PreparedStatement
/
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
/*
*The normal actuator is also the default actuator
/
executor = new SimpleExecutor(this, transaction);
}
/*
*If L2 cache cacheEnabled is enabled, create a caching executor cache executor
*cacheEnabled defaults to true
*
*
*
*/
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}

## 2.3: sqlSession.getMapper(UserMapper.class);

**obtain Mapper Interface proxy class instance**

public class DefaultSqlSession implements SqlSession {
@Override
public T getMapper(Class type) {
/**
*Using getMapper method of Configuration class
*/
return configuration.getMapper(type, this);
}
}

public class Configuration {
public T getMapper(Class type, SqlSession sqlSession) {
/**
*Calling MapperRegistry Mapper interface registrar
*/
return mapperRegistry.getMapper(type, sqlSession);
}
}

public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    /**
     * knownMappers Get MapperProxyFactory Mapper interface proxy factory from cache
     * If it is not found, an exception will be thrown,
     * Note: when obtaining the Mapper interface proxy instance, you need to define -- > the package equivalent to Spring in advance
     */
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        /**
         * Create proxy instance
         */
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

}

## <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;"> 2.3. 1: Create Mapper interface proxy using JDK proxy < / mark >

**InvocationHandler yes MapperProxy**

public class MapperProxyFactory {

private final Class<T> mapperInterface;
/**
 * MapperMethod cache
 */
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
}

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
    /**
     * JDK Production agent class
     */
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    /**
     * Proxy class callback interface
     */
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

}

## 2.4: userMapper.findById(1);  Call Mapper interface method Sql query

**Can walk proxy class MapperProxy.invoke**

public class MapperProxy implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;

public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else if (method.isDefault()) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    /**
     * Obtain a MapperMethod instance according to the method fully qualified name and cache it
     *
     * Note: the methodCache here is only a reference, and all cached objects are in {@ link MapperProxyFactory#methodCache}
     */
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    /**
     * Start execution
     */
    return mapperMethod.execute(sqlSession, args);
}

/**
 * Add to cache methodCache {@link MapperProxyFactory#methodCache}
 * computeIfAbsent   HashMap  Get if it exists, and add if it does not exist
 * @param method
 * @return
 */
private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}

}

## 2.5: mapperMethod.execute(sqlSession, args);

**implement Sql Statement query, because my return result is a User Object, so it will go to
result = sqlSession.selectOne(command.getName(), param);In this business,<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">Query a record</mark>**
**<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">Actually go to DefaultSqlSession.selectOne()</mark>**

public class MapperMethod {

private final org.apache.ibatis.binding.MapperMethod.SqlCommand command;
private final org.apache.ibatis.binding.MapperMethod.MethodSignature method;

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new org.apache.ibatis.binding.MapperMethod.SqlCommand(config, mapperInterface, method);
    this.method = new org.apache.ibatis.binding.MapperMethod.MethodSignature(config, mapperInterface, method);
}
/**
* Start Sql query execution
*/
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
        case INSERT: {
            // Omit code Execute insert statement < Insert > / @ insert
            break;
        }
        case UPDATE: {
            // Omit code Execute insert statement < update > / @ update
            break;
        }
        case DELETE: {
            // Omit code Execute delete statement < delete > / @ delete
            break;
        }
        case SELECT:      // Execute the select statement < Select > / @ select
            if (method.returnsVoid() && method.hasResultHandler()) {
                // Whether the return type is empty. Generally, Sql operations must return results
                executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (method.returnsMany()) {
                /**
                 * Whether to return more than one result set {@ link Collection} collection / array
                 */
                result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
                /**
                 * Whether the return type is a Map collection
                 */
                result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {
                /**
                 * Whether the return type is cursor {@ link org.apache.ibatis.cursor.Cursor}
                 */
                result = executeForCursor(sqlSession, args);
            } else {
                /**
                 * Convert parameters to Sql command parameters
                 */
                Object param = method.convertArgsToSqlCommandParam(args);
                /**
                 * The method in sqlSession initiates the query call
                 */
                result = sqlSession.selectOne(command.getName(), param);
                if (method.returnsOptional()
                        && (result == null || !method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:  // Refresh
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
        throw new BindingException("Mapper method '" + command.getName()
                + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
}

}

**DefaultSqlSession.selectOne()You can see that it is actually a call<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">selectList()</mark>,And if multiple result sets are returned, an error will be reported. The error message is as follows**
**Expected one result (or null) to be returned by selectOne(), but found: 2**
![Insert picture description here](https://upload-images.jianshu.io/upload_images/25222111-51fde0f4a08cf0a4?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 2.6: DefaultSqlSession.selectList()

**Query multiple result sets**

public class DefaultSqlSession implements SqlSession {
/**
*
*The fully qualified name of @ param statement method, for example: com mapper. UserMapper. findById
*@ parameter parameter parameter
*@ param rowBounds page
* @param
* @return
/
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/*
*According to the method, the fully qualified name is in configuration Get the Sql Statement object instance corresponding to the method from the mappedstatements cache collection
/
MappedStatement ms = configuration.getMappedStatement(statement);
/*
*Use actuator execution is performed by the currently set actuator
*
*
*
*Note: cacheEnabled uses {@ link cacheingexecution} cache executor to query because L2 cache is enabled and the default value is true
*/
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();
}
}
}

## 2.7: executor.query executor query

**Creating and viewing actuators <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">2.2: configuration.newExecutor(tx, execType);Create actuator</mark>**

/**

Keywords: Java Database SQL Back-end Programmer

Added by JoCitizen on Wed, 15 Dec 2021 02:39:38 +0200