} else { /** * 以下代码可以看出 url resource class 三个属性只能选择一个,否则就会报错 */ throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
}
## 1.4.2:mapperParser.parse();解析 **我的UserMapper.xml文件内容为:**<?xml version="1.0" encoding="UTF-8"?> select * from `user` where userId = #{userId}
**UserMapper接口内容为:**
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);
}
**疑问?UserMapper.xml有<select id=“findById”,而在接口中的findById方法我又加了一个@Select注解;那么执行会选择哪一条Sql执行还是报错呢?** **以UserMapper.xml为例子解析,可以看到 resource = mapper/UserMapper.xml** ![在这里插入图片描述](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)) {
/**
* 解析 mapper.xml文件内容
/
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
/*
* 解析 mapper.xml的
* namespace指定的{UserMapper}接口的注解信息
*/
bindMapperForNamespace();
}
parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
}
## 1.4.3:configurationElement(parser.evalNode("/mapper")); **解析 mapper.xml文件内容**
public class XMLMapperBuilder extends BaseBuilder {
private void configurationElement(XNode context) {
try {
/**
* namespace属性
/
String namespace = context.getStringAttribute(“namespace”);
if (namespace == null || namespace.equals("")) {
/*
* 不指定 namespace会报错哦 由此得知 namespace属性是必须指定的
/
throw new BuilderException(“Mapper’s namespace cannot be empty”);
}
builderAssistant.setCurrentNamespace(namespace);
/*
* 解析 cache-ref
/
cacheRefElement(context.evalNode(“cache-ref”));
/*
* 解析 cache
/
cacheElement(context.evalNode(“cache”));
/*
* 解析 parameterMap
/
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
/*
* 解析 resultMap
/
resultMapElements(context.evalNodes("/mapper/resultMap"));
/*
* 解析 sql
/
sqlElement(context.evalNodes("/mapper/sql"));
/*
* 解析 sql语句 select|insert|update|delete
* 重点分析这里,这里的解析会关联到 mapper接口的执行方法 sql语句映射
*/
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”)); **解析 sql语句 select|insert|update|delete;list参数内容是select|insert|update|delete的Sql语句** **XMLStatementBuilder Sql语句的解析器** ![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/25222111-e9b8bf020064dd10?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 1.4.5:statementParser.parseStatementNode();解析Sql语句 builderAssistant.addMappedStatement,并不是添加一个mapper.xml文件隐射的实例,<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">而是为每一个Sql语句创建一个实例</mark>
public class XMLStatementBuilder extends BaseBuilder {
private final MapperBuilderAssistant builderAssistant;
public void parseStatementNode() {
/**
* 此处省略一大推代码…
/
/*
*
* select * from user
where userId = #{userId}
*
* 参数解析
*
* id:标签指定的id = findById
* sqlSource:Sql语句,Sql参数占位
* statementType:sql执行类型 参考{@link StatementType}
* STATEMENT: 直接操作sql,不进行预编译 ${}
* PREPARED: 预处理,参数,进行预编译 #{}
* CALLABLE: 执行存储过程
* sqlCommandType:sql语句类型 参考{@link SqlCommandType}
* UNKNOWN:未知,INSERT:新增,UPDATE:修改,DELETE:删除,SELECT:查询,FLUSH:刷新
*
* 其他参数可查看官网: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(); **创建一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中**
public class XMLStatementBuilder extends BaseBuilder {
public MappedStatement addMappedStatement() {
/**
* 此处省略一大推代码…
/
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
/*
* 建造者模式
* 用于设置 MappedStatement的属性
* 此处省略一大推代码…
*/
/** * 设置参数入参类型 parameterType属性 * <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); } /** * 创建一个 {@link MappedStatement} 实例 */ MappedStatement statement = statementBuilder.build(); /** * MappedStatement实例添加到 {@link #configuration.mappedStatements} Map集合中 * MappedStatement 是对应一个Sql语句的实例对象 * * configuration.mappedStatements 存放所有的MappedStatement实例,后面会详细介绍 */ configuration.addMappedStatement(statement); return statement; }
}
## 以上流程执行完后,又回到<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();解析</mark>,现在开始解析 namespace指定的接口的注解信息,并创建该接口的代理工厂对象,UserMapper接口。 ![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/25222111-380c8b0d4e8e7797?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 1.4.7:bindMapperForNamespace(); **开始解析接口注解,并添加一个<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>代理工厂的对象到<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是<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">Mapper接口</mark>**
public class XMLMapperBuilder extends BaseBuilder {
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
/**
* java 反射 Class.classForName
/
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
/*
* 这里并没有抛出异常,说明 namespace 可以指定一个不存在的接口
/
//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);
/*
* 添加一个Mapper接口的代理工厂对象到configuration.mapperRegistry.knownMappers集合中
* 参考 {@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接口注册器,存放所有的mapper接口信息
*/
mapperRegistry.addMapper(type);
}
}
**mapperRegistry.addMapper(type); 为Mapper接口创建一个代理工厂,方便后期使用Mapper接口时创建代理类** **解析mapper接口的注解信息**
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} 代理接口工厂
/
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接口注解解析器
/
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
/ 开始解析 */
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
## 1.4.9 parser.parse(); MapperAnnotationBuilder.parse() **解析mapper接口的注解信息,parseStatement(method)主要在这个方法中完成**
public class MapperAnnotationBuilder {
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
/**
* 解析 @CacheNamespace 注解
/
parseCache();
/*
* 解析 CacheNamespaceRef 注解
/
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
/*
* 解析Sql相关注解 列如 @Select|@Update 之类的注解
*
* 重点关注
/
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
/*
* 解析待定方法
*/
parsePendingMethods();
}
}
## 1.5.0:parseStatement(method); **创建一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中**
public class MapperAnnotationBuilder {
private final MapperBuilderAssistant assistant;
void parseStatement(Method method) {
/**
* 此处省略一大推代码…
*/
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;">这里可以参考1.4.6:builderAssistant.addMappedStatement();一模一样的操作</mark>** <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">**都调用了configuration.addMappedStatement(statement);**</mark> ![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/25222111-7a172e142e5239a8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) **这里重点分析Configuration.addMappedStatement(statement);在做什么操作,并且解决 <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留下的疑点;UserMapper.xml和UserMapper接口都有findById的Sql语句定义</mark>**
public class Configuration {
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
}
**mappedStatements.put(ms.getId(), ms); 实际调用 Configuration.StrictMap.put()方法** **<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是一个重写的HashMap,put方法会先校验key是否存在</mark>**
public class Configuration {
/**
* mappedStatements Sql语句的对象
*
* Configuration.StrictMap 实现了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 是否存在 存在就抛出异常 * * 由此得知,方法映射Sql语句只能定义一个,要么在 mapper.xml中定义,要么就注解定义 */ 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调试,key已经存在,就会报错。由此得知,方法映射Sql语句只能定义一个,要么在 mapper.xml中定义,要么就注解定义** ![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/25222111-a866e4089e7aa556?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 到此,MyBatis的启动流程就走完了。 ## <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">2:接下来就来看下是如何执行Sql查询的。</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);
/** * 获取一个会话连接 */ SqlSession sqlSession = sqlSessionFactory.openSession(); /** * 拿到 UserMapper 的代理类 */ UserMapper userMapper = sqlSession.getMapper(UserMapper.class); /** * 执行Sql查询 */ User user = userMapper.findById(1); System.out.println(user); }
}
> **输出结果:User{userId=1, username=‘张三’, sex=‘男’, 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;">一行代码的查询,底层既然走了那么多流程;</mark>** ## 流程图: ![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/25222111-459d4ed3e065a0d5?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 2.1:sqlSessionFactory.openSession();打开会话连接 **调用DefaultSqlSessionFactory.openSessionFromDataSource();**
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
/**
* mybatis_config.xml配置的
* 数据源
*/
final Environment environment = configuration.getEnvironment();
/** * transactionManager 配置的事务管理器工厂 type="JDBC" {@link JdbcTransactionFactory} *<environments default="developmentss"> * <environment id="developmentss"> * <transactionManager type="JDBC"></transactionManager> * </environment> * </environments> */ final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); /** * 创建事务管理器,由于上面指定的事务管理器工厂是 {@link JdbcTransactionFactory} * 所以创建的事务管理器是 {@link JdbcTransaction} * * @param level 事务隔离级别 * @param autoCommit 是否自动提交事务 */ tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); /** * 创建Sql执行器 * * @param execType 创建执行器类型 defaultExecutorType如果不指定 默认就是 SIMPLE * <settings> * <setting name="defaultExecutorType" value="SIMPLE"/> * </settings> */ final Executor executor = configuration.newExecutor(tx, execType); /** * 创建一个默认的 SqlSession实例 */ 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);创建执行器
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) {
/**
* 执行器不仅重用语句还会执行批量更新
/
executor = new BatchExecutor(this, transaction);
/*
* 执行器会重用预处理语句(PreparedStatement)
/
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
/*
* 普通的执行器 也是默认的执行器
/
executor = new SimpleExecutor(this, transaction);
}
/*
* 如果开启了二级缓存 cacheEnabled,创建一个CachingExecutor缓存执行器
* cacheEnabled 默认为true
*
*
*
*/
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
## 2.3:sqlSession.getMapper(UserMapper.class); **获取Mapper接口代理类实例**
public class DefaultSqlSession implements SqlSession {
@Override
public T getMapper(Class type) {
/**
* 用 Configuration 类的 getMapper方法
*/
return configuration.getMapper(type, this);
}
}
public class Configuration {
public T getMapper(Class type, SqlSession sqlSession) {
/**
* 调用 MapperRegistry Mapper接口注册器
*/
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 从缓存中获取 MapperProxyFactory Mapper接口代理工厂 * 如果没有找到就会抛出异常, * 说明获取Mapper接口代理实例时,需要事先定义好 --> 相当于Spring的扫包 */ final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { /** * 创建代理实例 */ 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:使用JDK代理创建Mapper接口代理</mark> **InvocationHandler是MapperProxy**
public class MapperProxyFactory {
private final Class<T> mapperInterface; /** * MapperMethod 缓存 */ 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 生产代理类 */ return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { /** * 代理类回调接口 */ final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
}
## 2.4:userMapper.findById(1); 调用Mapper接口方法Sql查询 **会走代理类 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); } /** * 根据方法全限名 获取一个MapperMethod实例,并且缓存 * * 注意:这里的 methodCache 只是一个引用,缓存的所有对象都在 {@link MapperProxyFactory#methodCache}中 */ final MapperMethod mapperMethod = cachedMapperMethod(method); /** * 开始执行 */ return mapperMethod.execute(sqlSession, args); } /** * 添加到缓存 methodCache {@link MapperProxyFactory#methodCache}中 * computeIfAbsent HashMap 存在就获取,不存在就新增 * @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); **执行Sql语句查询,由于我的返回结果是一个 User对象,所以会走到 result = sqlSession.selectOne(command.getName(), param);这一行,<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">查询一条记录</mark>** **<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">实际走到 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); } /** * 开始执行Sql查询 */ public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { // 省略代码... 执行 insert语句 <insert>/@Insert break; } case UPDATE: { // 省略代码... 执行 insert语句 <update>/@Update break; } case DELETE: { // 省略代码... 执行 delete语句 <delete>/@Delete break; } case SELECT: // 执行 select语句 <select>/@Select if (method.returnsVoid() && method.hasResultHandler()) { // 返回类型是否为空 一般情况做Sql操作都要有返回结果的 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { /** * 是否返回多个结果集 {@link Collection}集合/数组 */ result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { /** * 返回类型是否为 Map 集合 */ result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { /** * 返回类型是否是 游标 {@link org.apache.ibatis.cursor.Cursor} */ result = executeForCursor(sqlSession, args); } else { /** * 将参数转换为Sql命令参数 */ Object param = method.convertArgsToSqlCommandParam(args); /** * 发起查询 调用的是 sqlSession中的方法 */ result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: // 刷新 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()可以看出实际是调用<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>,而且如果返回了多个结果集就会报错。错误信息如下** **Expected one result (or null) to be returned by selectOne(), but found: 2** ![在这里插入图片描述](https://upload-images.jianshu.io/upload_images/25222111-51fde0f4a08cf0a4?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 2.6:DefaultSqlSession.selectList() **查询多结果集**
public class DefaultSqlSession implements SqlSession {
/**
*
* @param statement 方法全限名 比如:com.mapper.UserMapper.findById
* @param parameter 参数
* @param rowBounds 分页
* @param
* @return
/
@Override
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/*
* 根据方法全限名在 configuration.mappedStatements缓存集合中拿到方法对应的Sql Statement对象实例
/
MappedStatement ms = configuration.getMappedStatement(statement);
/*
* 使用执行器执行 由当前设置的执行器执行
*
*
*
* 注意:cacheEnabled由于开启二级缓存默认为true,会先使用 {@link CachingExecutor} 缓存执行器查询
*/
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执行器查询 **执行器的创建查看 <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);创建执行器</mark>**
/**