MyBatis 源码分析 - SqlSession 会话与 SQL 执行入口
参考 知识星球 中 芋道源码 星球的源码解析,一个活跃度非常高的 Java 技术社群,感兴趣的小伙伴可以加入 芋道源码 星球,一起学习😄
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读
MyBatis 版本:3.5.2
MyBatis-Spring 版本:2.0.3
MyBatis-Spring-Boot-Starter 版本:2.1.4
该系列其他文档请查看:《精尽 MyBatis 源码分析 - 文章导读》
SqlSession会话与SQL执行入口
在前面一系列的文档中,已经详细的介绍了 MyBatis 的初始化和执行 SQL 的过程,在执行 SQL 的过程中,还存在着一些疑问,例如其中一直提到的 SqlSession 会话在 MyBatis 中是如何被创建的?如何调用 Executor 执行器执行 SQL 的?
那么接下来就关于上面两个的两个问题,一起来探讨一下 MyBatis 中 SqlSession 会话的相关内容
先回顾一下《基础支持层》的Binding模块,每个Mapper Interface
会有对应的MapperProxyFactory
动态代理对象工厂,用于创建MapperProxy
动态代理对象,Mapper 接口中的每个方法都有对应的MapperMethod
对象,该对象会通过 SqlSession 会话执行数据操作
再来看到这张MyBatis整体图:
在单独使用 MyBatis 进行数据库操作时,需要通过SqlSessionFactoryBuilder
构建一个SessionFactory
对象,然后通过它创建一个SqlSession
会话进行数据库操作
我们通常都会先调用SqlSession
会话的getMapper(Class<T> mapper)
方法,为 Mapper 接口生成一个“实现类”(JDK动态代理对象),然后就可以通过它进行数据库操作
示例
// <1> 构建 SqlSessionFactory 对象
Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
// <2> 默认 DefaultSqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// <3> 获得 SqlSession 对象,默认 DefaultSqlSession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// <4> 获得 Mapper 对象
final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
// <5> 执行查询
final Object subject = mapper.getSubject(1);
SqlSessionFactoryBuilder
org.apache.ibatis.session.SqlSessionFactoryBuilder
:构建SqlSessionFactory工厂类,里面定义了许多build重载方法,主要分为处理Reader和InputStream两种文件资源对象,代码如下:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
/**
* 构造 SqlSessionFactory 对象
*
* @param reader Reader 对象
* @param environment 环境
* @param properties Properties 变量
* @return SqlSessionFactory 对象
*/
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
/*
* <1> 创建 XMLConfigBuilder 对象
* 会生成一个 XPathParser,包含 Document 对象
* 会创建一个 Configuration 全局配置对象
*/
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
/*
* <2> 解析 XML 文件并配置到 Configuration 全局配置对象中
* <3> 创建 DefaultSqlSessionFactory 对象
*/
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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);
}
}
在《MyBatis初始化(一)之加载mybatis-config.xml》中已经分析了,这里就不再赘述,就是根据文件资源创建Configuration全局配置对象,然后构建一个DefaultSqlSessionFactory对象
DefaultSqlSessionFactory
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
:实现 SqlSessionFactory 接口,默认的 SqlSessionFactory 实现类
openSession方法
openSession
方法,创建一个DefaultSqlSession
对象,如下:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return openSessionFromDataSource(execType, level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}
@Override
public SqlSession openSession(Connection connection) {
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}
@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
return openSessionFromConnection(execType, connection);
}
}
openSession
有很多重载的方法,主要是提供以下几种入参的支持:
类型 | 参数名称 | 默认值 | 描述 |
---|---|---|---|
boolean | autoCommit | false | 事务是否自动提交 |
ExecutorType | execType | ExecutorType.SIMPLE | Executor执行器类型 |
TransactionIsolationLevel | level | 无 | 事务隔离级别 |
java.sql.Connection | connection | 无 | 数据库连接 |
内部直接调用openSessionFromDataSource
私有方法,内部也需要调用openSessionFromConnection
私有方法,如果存在connection
入参,内部则直接调用openSessionFromConnection
私有方法
openSessionFromDataSource方法
openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
方法,用于创建一个DefaultSqlSession
对象,方法如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
// 获得 Environment 对象
final Environment environment = configuration.getEnvironment();
// 创建 Transaction 对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建 Executor 对象
final Executor executor = configuration.newExecutor(tx, execType);
// 创建 DefaultSqlSession 对象
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 如果发生异常,则关闭 Transaction 对象
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
- 获得
Environment
当前环境对象 - 通过
getTransactionFactoryFromEnvironment
方法,从 Environment 环境对象中TransactionFactory
对象,用于创建一个Transaction
事务 - 然后再创建一个
Executor
执行器对象 - 根据全局配置对象、执行器和事务是否自动提交创建一个
DefaultSqlSession
对象
openSessionFromConnection方法
openSessionFromConnection(ExecutorType execType, Connection connection)
方法,用于创建一个DefaultSqlSession
对象
和上面的方法逻辑相同,只不过它的入参直接传入了一个Connection数据库连接,方法如下:
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
// 获得是否可以自动提交
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
// 获得 Environment 对象
final Environment environment = configuration.getEnvironment();
// 创建 Transaction 对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
// 创建 Executor 对象
final Executor executor = configuration.newExecutor(tx, execType);
// 创建 DefaultSqlSession 对象
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
和上面的方法不同的是,创建Transaction
事务对象时,传入的参数直接是Connection
数据库连接对象
getTransactionFactoryFromEnvironment方法
getTransactionFactoryFromEnvironment(Environment environment)
方法,用于创建一个TransactionFactory
对象,在创建 SqlSessionFactory 时,就可以通过设置 Environment 的 DataSource 数据源和 TransactionFactory 事务工厂来集成第三方数据源和事务管理器,代码如下:
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
// 情况一,创建 ManagedTransactionFactory 对象
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
// 情况二,使用 `environment` 中的
return environment.getTransactionFactory();
}
closeTransaction方法
closeTransaction(Transaction tx)
方法,关闭事务,方法如下:
private void closeTransaction(Transaction tx) {
if (tx != null) {
try {
tx.close();
} catch (SQLException ignore) {
// Intentionally ignore. Prefer previous error.
}
}
}
DefaultSqlSession
org.apache.ibatis.session.defaults.DefaultSqlSession
:实现 SqlSession 接口,默认的 SqlSession 实现类,调用 Executor 执行器,执行数据库操作
构造方法
public class DefaultSqlSession implements SqlSession {
/**
* 全局配置
*/
private final Configuration configuration;
/**
* 执行器对象
*/
private final Executor executor;
/**
* 是否自动提交事务
*/
private final boolean autoCommit;
/**
* 是否发生数据变更
*/
private boolean dirty;
/**
* Cursor 数组
*/
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
}
select方法
执行数据库查询操作,提供了许多重载方法
@Override
public void select(String statement, Object parameter, ResultHandler handler) {
select(statement, parameter, RowBounds.DEFAULT, handler);
}
@Override
public void select(String statement, ResultHandler handler) {
select(statement, null, RowBounds.DEFAULT, handler);
}
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public <T> T selectOne(String statement) {
return this.selectOne(statement, null);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException(
"Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
@Override
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
}
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
// <1> 执行查询
final List<? extends V> list = selectList(statement, parameter, rowBounds);
// <2> 创建 DefaultMapResultHandler 对象
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(),
configuration.getReflectorFactory());
// <3> 创建 DefaultResultContext 对象
final DefaultResultContext<V> context = new DefaultResultContext<>();
// <4> 遍历查询结果
for (V o : list) {
// 设置 DefaultResultContext 中
context.nextResultObject(o);
// 使用 DefaultMapResultHandler 处理结果的当前元素
mapResultHandler.handleResult(context);
}
// <5> 返回结果
return mapResultHandler.getMappedResults();
}
@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 {
// <1> 获得 MappedStatement 对象
MappedStatement ms = configuration.getMappedStatement(statement);
// <2> 执行查询
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();
}
}
上面有很多的数据库查询方法,主要分为以下几种:
select
:执行数据库查询操作,通过入参中的ResultHandler
处理结果集,无返回结果selectList
:执行数据库查询操作,返回List集合selectOne
:调用selectList
方法,执行数据库查询操作,最多只能返回一条数据selectMap
:调用selectList
方法,执行数据库查询操作,通过DefaultMapResultHandler
进行处理,将返回结果转换成Map集合
可以看到通过Executor执行器的query
方法执行查询操作,可以回顾《SQL执行过程(一)之Executor》中的内容
这里会先调用wrapCollection
方法对入参进行包装(如果是集合类型)
update方法
执行数据库更新操作
@Override
public int insert(String statement) {
return insert(statement, null);
}
@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int delete(String statement) {
return update(statement, null);
}
@Override
public int delete(String statement, Object parameter) {
return update(statement, parameter);
}
@Override
public int update(String statement) {
return update(statement, null);
}
@Override
public int update(String statement, Object parameter) {
try {
// <1> 标记 dirty ,表示执行过写操作
dirty = true;
// <2> 获得 MappedStatement 对象
MappedStatement ms = configuration.getMappedStatement(statement);
// <3> 执行更新操作
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
insert
和delete
方法最终都是调用update
方法,通过Executor执行器的update
方法执行数据库更新操作
这里会先调用wrapCollection
方法对入参进行包装(如果是集合类型)
wrapCollection方法
wrapCollection(final Object object)
方法,将集合类型的参数包装成StrictMap
对象,方法如下:
public static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -5741767162221585340L;
@Override
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException(
"Parameter '" + key + "' not found. Available parameters are " + this.keySet());
}
return super.get(key);
}
}
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
// 如果是集合,则添加到 collection 中
StrictMap<Object> map = new StrictMap<>();
map.put("collection", object);
// 如果是 List ,则添加到 list 中
if (object instanceof List) {
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
// 如果是 Array ,则添加到 array 中
StrictMap<Object> map = new StrictMap<>();
map.put("array", object);
return map;
}
return object;
}
getMapper方法
getMapper(Class<T> type)
方法,获取Mapper接口的代理对象
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
通过Configuration
全局配置对象获取一个动态代理对象
实际通过MapperRegistry
注册表获取到该Mapper接口对应的MapperProxyFactory
动态代理工厂,然后创建一个MapperProxy
动态代理的实例对象
其他方法
flushStatements()
:提交批处理commit()
:提交事务rollback()
:回滚事务close()
:关闭当前会话getConnection()
:获取当前事务的数据库连接clearCache()
:清理一级缓存
MapperMethod
org.apache.ibatis.binding.MapperMethod
:Mapper接口中定义的方法对应的Mapper方法,通过它来执行SQL
先来看看执行一个SQL的完整流程图:
在《基础支持层》的Binding模块已经讲过了相关的内容,例如看到前面示例的第4
步,通过DefaultSqlSession
的getMapper
方法会执行以下操作:
- 创建Mapper接口对应的
MapperProxyFactory
动态代理对象工厂 - 通过这个工厂的
newInstance
方法会创建一个MapperProxy
接口代理类,然后返回该Mapper接口的动态代理对象
当你调用这个接口的某个方法时,会进入这个MapperProxy
代理类,我们来看到它的invoke
方法是如何实现的,方法如下:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// <1> 如果是 Object 定义的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) { // 是否有 default 修饰的方法
// 针对Java7以上版本对动态类型语言的支持
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// <2.1> 获得 MapperMethod 对象
final MapperMethod mapperMethod = cachedMapperMethod(method);
// <2.2> 执行 MapperMethod 方法
return mapperMethod.execute(sqlSession, args);
}
首先获取到方法对应的MapperMethod
对象,然后通过该对象去执行数据库的操作
execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
// 根据 SqlCommand 的 Type 判断应该如何执行 SQL 语句
Object result;
switch (command.getType()) {
case INSERT: {
// <1> 获取参数值与参数名的映射
Object param = method.convertArgsToSqlCommandParam(args);
// <2> 然后通过 SqlSession 进行数据库更新操作,并将受影响行数转换为结果
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) { // 无返回,且入参中有 ResultHandler 结果处理器
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 执行查询,返回列表
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 执行查询,返回 Map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 执行查询,返回 Cursor
result = executeForCursor(sqlSession, args);
} else { // 执行查询,返回单个对象
// 获取参数名称与入参的映射
Object param = method.convertArgsToSqlCommandParam(args);
// 执行查询,返回单条数据
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());
}
// 返回结果为 null ,并且返回类型为原始类型(基本类型),则抛出 BindingException 异常
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;
}
根据 SqlCommand 的 Type 判断应该如何执行 SQL 语句,类型分为INSERT
、UPDATE
、DELETE
、SELECT
和FLUSH
(进行批处理)五种
前三种都属于数据库的更新操作,调用的是DefaultSqlSession
的update
方法,通过rowCountResult(int rowCount)
将受影响行数转换为返回对象
查询语句的话就分为以下几种情况:
- 无返回,且入参中有
ResultHandler
结果处理器,则调用executeWithResultHandler
方法,执行查询,返回结果设置为null - 返回对象为多条数据,则调用
executeForMany
方法,执行查询,返回列表 - 返回对象为Map类型,则调用
executeForMap
方法,执行查询,返回Map - 返回对象为Cursor类型,则调用
executeForCursor
方法,执行查询,返回Cursor - 返回对象为单个对象,则调用
DefaultSqlSession
的selectOne
方法,执行查询,返回单个对象
上面执行数据库操作前会先调用convertArgsToSqlCommandParam
方法,获取参数值与参数名的映射
convertArgsToSqlCommandParam方法
convertArgsToSqlCommandParam(Object[] args)
方法,根据入参返回参数名称与入参的映射,方法如下:
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
在《基础支持层》的反射模块的ParamNameResolver小节已经分析过该方法,可以跳过去看看
rowCountResult方法
rowCountResult(int rowCount)
方法,将受影响行数转换为结果,方法如下:
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long) rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = rowCount > 0;
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: "
+ method.getReturnType());
}
return result;
}
executeWithResultHandler方法
executeWithResultHandler(SqlSession sqlSession, Object[] args)
方法,通过入参中定义的ResultHandler结果处理器执行查询,方法如下:
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
// <1> 获得 MappedStatement 对象
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
/*
* <2> 配置校验
* 因为入参定义了 ResultHandler 结果处理器,所以如果不是存储过程,且没有配置返回结果的 Java Type,则会抛出异常
*/
if (!StatementType.CALLABLE.equals(ms.getStatementType())
&& void.class.equals(ms.getResultMaps().get(0).getType())) {
throw new BindingException(
"method " + command.getName() + " needs either a @ResultMap annotation, a @ResultType annotation,"
+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
}
// <3> 获取参数名称与入参的映射
Object param = method.convertArgsToSqlCommandParam(args);
// <4> 执行数据库查询操作
if (method.hasRowBounds()) { // <4.1> 入参定义了 RowBounds 分页对象
// <4.1.1> 获取入参定义了 RowBounds 分页对象
RowBounds rowBounds = method.extractRowBounds(args);
// <4.1.2> 执行查询
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
// <4.2> 执行查询
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
- 获得
MappedStatement
对象 - 配置校验,因为入参定义了
ResultHandler
结果处理器,所以如果不是存储过程,且没有配置返回结果的 Java Type,则会抛出异常 - 获取参数名称与入参的映射
- 执行数据库查询操作
- 入参定义了
RowBounds
分页对象,则获取该对象,然后执行查询,传入分析对象 - 没有定义则执行执行查询
- 入参定义了
executeForMany方法
executeForMany(SqlSession sqlSession, Object[] args)
方法,执行查询,返回列表,方法如下:
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 获取参数名称与入参的映射
Object param = method.convertArgsToSqlCommandParam(args);
// 执行数据库查询操作
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
// 执行查询,返回 List 集合
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 执行查询,返回 List 集合
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
// 封装 Array 或 Collection 结果
if (!method.getReturnType().isAssignableFrom(result.getClass())) { // 如果不是 List 集合类型
if (method.getReturnType().isArray()) {
// 将 List 转换成 Array 数组类型的结果
return convertToArray(result);
} else {
// 转换成其他 Collection 集合类型的结果
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
// 直接返回 List 集合类型的结果
return result;
}
- 获取参数名称与入参的映射
- 执行数据库查询操作,获取到 List 集合结果
- 根据返回结果的类型进行转换
- 如果是 Array 数组类型,则将 List 转换成 Array 数组类型的结果
- 如果是其他集合类型,则将 List 转换成其他 Collection 集合类型的结果
- 否则直接返回 List 集合类型的结果
executeForMap方法
executeForMap(SqlSession sqlSession, Object[] args)
方法,执行查询,返回Map,方法如下:
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Map<K, V> result;
// 获取参数名称与入参的映射
Object param = method.convertArgsToSqlCommandParam(args);
// 执行 SELECT 操作
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
// 执行查询,返回 Map 集合
result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
// 执行查询,返回 Map 集合
result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
- 获取参数名称与入参的映射
- 执行查询,返回 Map 集合
总结
本分分析了 SqlSession 会话在 MyBatis 中是如何被创建,如何获取到 Mapper 接口的动态代理对象,通过该动态代理对象是如何执行 SQL 的
-
SqlSessionFactoryBuilder
构造器提供build
方法,根据mybatis-config.xml
配置文件对 MyBatis 进行初始化,生成Configuration
对象,用于构建一个DefaultSqlSessionFactory
对象 -
通过
1
生成的 SqlSession 工厂对象,我们可以构建一个DefaultSqlSession
会话对象,通过这个会话对象的getMapper(Class<T> mapper)
方法,可以为 Mapper 接口创建一个动态代理对象
(JDK动态代理),其动态代理类为MapperProxy
对象 -
调用 Mapper 接口的某个方法时,会进入相应的
MapperProxy
代理类,根据方法对应的MapperMethod
对象,执行数据库操作 -
执行数据库相关操作,调用的就是上面的
DefaultSqlSession
会话对象的相关方法,该会话内部则会通过Executor
执行器去执行数据库的相关操作,并返回执行结果
好了,对于 MyBatis 整体上所有的内容已经全部分析完了,相信大家对 MyBatis 有了一个全面认识,其中肯定有不对或者迷惑的地方,欢迎指正!!!感谢大家的阅读!!!😄😄😄
参考文章:芋道源码《精尽 MyBatis 源码分析》