} 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."); } } } } }
}
<?xml version="1.0" encoding="UTF-8"?> select * from `user` where userId = #{userId}## 1.4. 2: mapperParser.parse(); analysis **my UserMapper.xml The contents of the document are:**
**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>**
/**