MyBatis学习笔记
- 源码篇
-
- 接口和对象介绍
-
- SqlSessionFactoryBuilder
- XMLConfigBuilder
- XMLMapperBuilder
- Configuration
- SqlSource接口
- SqlSessionFactory接口
- SqlSession接口
- Executor接口
-
- BaseExecutor
- CachingExecutor
- StatementHandler接口
-
- 定义
- 继承结构
- prepare方法
- parameterize方法
- RoutingStatementHandler
- ParameterHandler接口
- ResultSetHandler接口
- 源码阅读
-
- 加载全局配置文件(创建Configuration对象)流程
- 加载映射文件流程
- SQL解析流程
- 获取Mapper代理对象流程
- SqlSession执行主流程-执行SQL查询流程
- 设置PreparedStatement的参数流程
- 结果集映射流程
- 待分析的问题
源码篇
- 源码阅读的方法:找主线,找入口,记笔记(类名#方法名(数据成员变量)),参考其他人的源码阅读经验。
- 源码阅读的目的:通过阅读源码,提升对设计模式的理解,提升编程能力;通过阅读源码,可以找到问题的根源,用来解决问题;应付面试。
接口和对象介绍
SqlSessionFactoryBuilder
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9EleNl3aXp1Mk1mYox2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLxADOyITNzUTM5ETMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
XMLConfigBuilder
- 专门用来解析全局配置文件的解析器
XMLMapperBuilder
- 专门用来解析映射文件的解析器
Configuration
- Mybatis框架支持开发人员通过配置文件与其进行交流,在配置文件中配置的信息,框架运行时,会被XMLConfigBuilder解析并存储在一个Configuration对象中。
- Configuration对象会被作为参数传给DefaultSqlSessionFactory,而DefaultSqlSessionFactory根据Configuration对象信息为Client创建对应特征的SqlSession对象。
SqlSource接口
- DynamicSqlSource:主要是封装动态SQL标签解析之后的SQL语句和带有${}的SQL语句
- RawSqlSource:主要封装带有#{}的SQL语句
- StaticSqlSource:是BoundSql中要存储SQL语句的一个载体,上面两个SqlSource的SQL语句,最终都会存储到该SqlSource实现类中。
SqlSessionFactory接口
- 默认实现类是DefaultSqlSessionFactory类
SqlSession接口
- 默认实现类是DefaultSqlSession类
Executor接口
- 定义:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
- Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口。
- 从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。
- Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建,代码如下:
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
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);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
- Executor分成两大类:
- BaseExecutor:抽像类,在创建时会根据传过来的ExecutorType创建不同的类:SimpleExecutor、ReuseExecutor、BatchExecutor。
- CachingExecutor:先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是 BaseExecutor 中的任一一种。
BaseExecutor
- SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)
- ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用(可以是Statement或PrepareStatement对象)。
- 代码如下:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
private Statement getStatement(String s) {
return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
//...
- BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果篮里装了很多苹果,番茄篮里装了很多番茄。最后,再统一倒进仓库。(可以是Statement或PrepareStatement对象)。
CachingExecutor
- 代码如下:
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
//...
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
...
StatementHandler接口
- 首先约定文中讲的四大对象是指:Executor、StatementHandler、ParameterHandler 和 ResultHandler 接口对象。而StatementHandler 毫无疑问是四大对象中最重要的一个,它的任务就是和数据库对话。在它这里会使用 ParameterHandler 和ResultHandler 对象为我们绑定SQL参数和组装最后的结果返回。
定义
public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
void parameterize(Statement statement)
throws SQLException;
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
- 这里有几个重要的方法:prepare、parameterize、query、update,他们的作用是不一样的。
继承结构
- StatementHandler:顶层接口。
- BaseStatementHandler : 实现顶层接口的抽象类,实现了部分接口,并定义了一个抽象方法。
- SimpleStatementHandler:对应JDBC中常用的Statement接口,用于简单SQL的处理。
- PreparedStatementHandler:对应JDBC中的PreparedStatement,预编译SQL的接口。
- CallableStatementHandler:对应JDBC中CallableStatement,用于执行存储过程相关的接口。
- RoutingStatementHandler:这个接口是以上三个接口的路由,没有实际操作,只是负责上面三个StatementHandler的创建及调用。
在MyBatis中,Configuration对象会采用new RoutingStatementHandler()来生成StatementHandler对象,换句话说我们真正使用的是RoutingStatementHandler对象,然后它会根据Executor的类型去创建对应具体的statementHandler对象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。然后利用具体statementHandler的方法完成所需要的功能。那么这个具体的statementHandler是保存在RoutingStatementHandler对象的delegate属性的,所以当我们拦截statementHandler的时候就要常常访问它了。
prepare方法
- BaseStatementHandler的三个子类都继承 prepare() 方法,并没有重写该方法。该方法中调用了一个抽象方法 instantiateStatement()。根据一个 Connection 返回一个Statement 对象;三个子类都实现了该方法,分别返回了 Statement,PrepareStaement 和 CallableStatement 对象。
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
- 显然我们通过源码更加关注抽象方法instantiateStatement是做了什么事情。它依旧是一个抽象方法,那么它就有其实现类。
- 那就是之前说的那几个具体的StatementHandler对象,让我们看看PreparedStatementHandler:
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
// 获取带有占位符的SQL语句
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
// 处理带有主键返回的SQL
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
这个方法非常简单,我们可以看到它主要是根据上下文来预编译SQL,这时我们还没有设置参数。设置参数的任务是交由statement接口的parameterize方法来实现的。
parameterize方法
- 进行参数设置:
@Override
public void parameterize(Statement statement) throws SQLException {
// 通过ParameterHandler处理参数
parameterHandler.setParameters((PreparedStatement) statement);
}
- 抽象类的三个子类都实现了 update()、batch()、query(),在对应的方法中将传入的 Statement 对象转型为 具体的 Statement 类型进行相应的操作。
- 以PreparedStatementHandler的实现为例:
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行PreparedStatement,也就是执行SQL语句
ps.execute();
// 处理结果集
return resultSetHandler.handleResultSets(ps);
}
- 我们可以看到如果是进行update的,它将会执行生成主键的操作(插入数据要自动生成主键的时候),然后就返回影响行数。
- 如果是进行query的就更加简单了,它就是执行SQL语句,然后将结果使用resultHandler的handleResultSets去完成我们的结果组装。
RoutingStatementHandler
- 该类采用了代理模式(静态代理),直接实现了 StatmentHandler 接口,实现了接口的全部方法。该类持有一个 StatmentHandler 的实例,并在创建该类时根据传入的 MappedStatement 的 type 创建不同的 Statement 实例,然后通过这个具体的实例去实现相应的操作。
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
@Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
}
// 其他方法同理
...
}
ParameterHandler接口
- 定义
public interface ParameterHandler {
Object getParameterObject(); // 获取参数对象
void setParameters(PreparedStatement ps)
throws SQLException; // 设置参数对象
}
- 继承结构:只有一个默认的实现类
JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇 - 默认实现类:DefaultParameterHandler
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 获取要设置的参数映射信息
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 只处理入参
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 获取属性名称
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取每个参数的类型处理器,去设置入参和获取返回值
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 获取每个参数的JdbcType
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 给PreparedStatement设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
- 它的 setParameters() 只在两处地方被调用,分别为 PreparedStatementHandler 和 CallableStatementHandler 的 parameterize()。
ResultSetHandler接口
- 定义:
public interface ResultSetHandler {
// 将Statement执行后产生的结果集(可能有多个结果集)映射为结果列表
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 处理存储过程执行后的输出参数
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
- 继承结构
JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇 - 默认实现类:ResultSetHandler的具体实现类是DefaultResultSetHandler,其实现的步骤就是将Statement执行后的结果集,按照Mapper文件中配置的ResultType或ResultMap来封装成对应的对象,最后将封装的对象返回 。
源码阅读
- Mybatis整体架构
JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇 - MyBatis源码包对应的架构图
JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(3)源码篇
加载全局配置文件(创建Configuration对象)流程
- 找入口:SqlSessionFactoryBuilder#build()
/**
* 主线一:加载全局配置文件流程
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// XMLConfigBuilder 用来解析XML配置文件
// 使用构建者模式
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parser.parse():使用XPath解析XML配置文件,将配置文件信息封装为Configuration对象
// 返回 DefaultSqlSessionFactory 对象,改对象拥有Configuration对象(封装了配置文件信息的)
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.
}
}
}
- 记笔记
SqlSessionFactoryBuilder#build(...):用于构建SqlSessionFactory对象
|--XMLConfigBuilder#构造函数(...):用来解析全局文件的解析器
|--XPathParser#构造函数(...):用来使用XPath语法解析XML的解析器
|--XPathParser#createDocument(...):解析全局配置文件,封装为Document对象(封装一些子节点,使用XPath语法解析获取)
|--Configuration#构造函数(...):创建Configuration对象,同时初始化内置类的别名
|--XMLConfigBuilder#parse(...):全局配置文件的解析器
|--XPathParser#parse(XPath语法):XPath解析器,专门用来通过XPath语法解析XML返回XNode节点
|--XMLConfigBuilder#parseConfiguration(XNode):从全局配置文件根节点开始解析,加载的信息设置到Configuration对象中
|--SqlSessionFactoryBuilder#build(...):创建SqlSessionFactory接口的默认实现类DefaultSqlSessionFactoryBuilder
- 总结:
- SqlSessionFactoryBuilder创建SqlSessionFactory时,需要传入一个Configuration对象
- XMLConfigBuilder对象会去实例化Configuration
- XMLConfigBuilder对象会去初始化Configuration对象
- 通过XPathParser去解析全局配置文件,形成Document对象
- 通过XPathParser去获取指定节点的XNode对象
- 解析XNode对象的信息,然后封装到Configuration对象中
- 主要涉及到的类:
SqlSessionFactoryBuilder
XMLConfigBuilder
XPathParser
Configuration
加载映射文件流程
- 找入口:XMLConfigBuilder#mapperElement()
/**
* 主线二:解析<mappers>标签
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 获取<mappers>标签的子标签
for (XNode child : parent.getChildren()) {
// <package>子标签
if ("package".equals(child.getName())) {
// 获取mapper接口和mapper映射文件对应的package包名
String mapperPackage = child.getStringAttribute("name");
// 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中
// key为mapper接口类型,value为代理对象工厂
configuration.addMappers(mapperPackage);
} else {// <mapper>子标签
// 获取<mapper>子标签的resource属性
String resource = child.getStringAttribute("resource");
// 获取<mapper>子标签的url属性
String url = child.getStringAttribute("url");
// 获取<mapper>子标签的class属性
String mapperClass = child.getStringAttribute("class");
// 按照 resource --> url --> class 的优先级去解析<mapper>子标签,他们是互斥的
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// XMLMapperBuilder专门用来解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// XMLMapperBuilder调用parse方法解析mapper映射文件
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// XMLMapperBuilder调用parse方法解析mapper映射文件
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 将指定mapper接口以及它的代理对象存储到一个Map集合中
// key为mapper接口类型,value为代理对象工厂
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
- 记笔记
XMLConfigBuilder#mapperElement(...):解析全局配置文件的<mappers>标签
|--XMLMapperBuilder#构造函数(...):专门用来解析映射文件
|--XPathParser#构造函数(...):
|--XPathParser#createDocument(...):解析映射文件,封装为Document对象
|--MapperBuilderAssistant#构造函数(...):用于构建MappedStatement对象
|--XMLMapperBuilder#parse(...):解析方法
|--XMLMapperBuilder#configurationElement(...):专门用来解析mapper映射文件
|--XMLMapperBuilder#buildStatementFromContext(...):用来创建MappedStatement对象的
|--|--XMLMapperBuilder#buildStatementFromContext(...):
|--XMLStatementBuilder#构造函数(...):专门用来解析MappedStatement
|--XMLStatementBuilder#parseStatementNode(...):
|--MapperBuilderAssistant#addMappedStatement(...):创建MappedStatement
|--MappedStatement.Builder#构造函数(...)
|--MappedStatement#build(...):创建MappedStatement对象,并存储到Configuration对象中
- 主要涉及到的类:
XMLConfigBuilder
XMLMapperBuilder
XPathParser
MapperBuilderAssistant
XMLStatementBuilder
MappedStatement.Builder
MappedStatement
SQL解析流程
- 找入口:XMLLanguageDriver#createSqlSource
/**
* 主线三:SQL解析
*/
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
// 初始化了动态SQL标签处理器
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
// 解析动态SQL
return builder.parseScriptNode();
}
- 记笔记
XMLLanguageDriver#createSqlSource(...):获取解析之后的SQL信息,以及参数信息
|--XMLScriptBuilder#构造函数(...):初始化动态SQL中的节点处理器集合
|--XMLScriptBuilder#parseScriptNode(...):
|--XMLScriptBuilder#parseDynamicTags(...):解析动态SQL标签
|--SqlSource#getBoundSql(...)
|--SqlSourceBuilder#parse(...):将带有#{}的SQL语句进行解析,然后封装到StaticSqlSource中
- 主要涉及到的类:
XMLLanguageDriver
XMLScriptBuilder
SqlSource
SqlSourceBuilder
获取Mapper代理对象流程
- 找入口:DefaultSqlSession#getMapper(…)
/**
* 主线四:获取Mapper代理对象
*/
@Override
public <T> T getMapper(Class<T> type) {
// 从Configuration对象中,根据Mapper接口,获取Mapper代理对象
return configuration.<T>getMapper(type, this);
}
- 记笔记
DefaultSqlSession#getMapper(...):获取Mapper代理对象
|--Configuration#getMapper(...):获取Mapper代理对象
|--MapperRegistry#getMapper(...):通过代理对象工厂,获取代理对象
|--MapperProxyFactory#newInstance(...):调用JDK的动态代理方式,创建Mapper代理
- 主要涉及到的类:
DefaultSqlSession
Configuration
MapperRegistry
MapperProxyFactory
SqlSession执行主流程-执行SQL查询流程
- 找入口:DefaultSqlSession#selectList(…)
/**
* 主线五:SqlSession执行
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据传入的statementId,获取MappedStatement对象
MappedStatement ms = configuration.getMappedStatement(statement);
// 调用执行器的查询方法
// RowBounds是用来逻辑分页(按照条件将数据从数据库查询到内存中,在内存中进行分页)
// wrapCollection(parameter)是用来装饰集合或者数组参数
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();
}
}
- 记笔记
DefaultSqlSession#selectList(...)
|--CachingExecutor#query(...)
|--BaseExecutor#query(...)
|--BaseExecutor#queryFromDatabase(...)
|--SimpleExecutor#doQuery(...)
|--Configuration#newStatementHandler(...):创建StatementHandler用来执行MappedStatement对象
|--RoutingStatementHandler#构造函数(...):根据路由规则,设置不同的StatementHandler
|--SimpleExecutor#prepareStatement(...):主要设置PreparedStatement的参数
|--SimpleExecutor#getConnection(...):获取数据库连接
|--PreparedStatementHandler#prepare(...):创建PreparedStatement对象
|--PreparedStatementHandler#parameterize:设置PreparedStatement的参数
|--PreparedStatementHandler#query(...):主要用来执行SQL语句,以及处理结果集
|--PreparedStatement#execute():调用JDBC的API执行Statement
|--DefaultResultSetHandler#handleResultSets:处理结果集
- 主要涉及到的类:
DefaultSqlSession
CachingExecutor
BaseExecutor
SimpleExecutor
Configuration
RoutingStatementHandler
PreparedStatementHandler
PreparedStatement
DefaultResultSetHandler
设置PreparedStatement的参数流程
- 找入口:PreparedStatementHandler#parameterize(…)
/**
* 主线六:PreparedStatement 进行参数设置
*/
@Override
public void parameterize(Statement statement) throws SQLException {
// 通过ParameterHandler处理参数
parameterHandler.setParameters((PreparedStatement) statement);
}
- 记笔记
PreparedStatementHandler#parameterize(...):设置PreparedStatement的参数
|--DefaultParameterHandler#setParameters(...):设置参数
|--BaseTypeHandler#setParameter(...):
|--XxxTypeHandler#setNonNullParameter(...):调用PreparedStatement的setXxx方法
- 主要涉及到的类:
PreparedStatementHandler
DefaultParameterHandler
BaseTypeHandler
XxxTypeHandler
结果集映射流程
- 找入口:DefaultResultSetHandler#handleResultSets(…)
/**
* 主线七:结果集映射
*/
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// <select>标签的resultMap属性,可以指定多个值,多个值之间用逗号(,)分割
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 这里是获取第一个结果集,将传统JDBC的ResultSet包装成一个包含结果列元信息的ResultSetWrapper对象
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 这里是获取所有要映射的ResultMap(按照逗号分割出来的)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 要映射的ResultMap的数量
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 循环处理每个ResultMap,从第一个开始处理
while (rsw != null && resultMapCount > resultSetCount) {
// 得到结果映射信息
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集
// 从rsw结果集参数中获取查询结果,再根据resultMap映射信息,将查询结果映射到multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 对应<select>标签的resultSets属性,一般不使用该属性
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// 如果只有一个结果集合,则直接从多结果集中取出第一个
return collapseSingleResultList(multipleResults);
}
- 记笔记
DefaultResultSetHandler#handleResultSets(...)
|--DefaultResultSetHandler#handleResultSet(...)
|--DefaultResultSetHandler#handleRowValues(...)
|--DefaultResultSetHandler#handleRowValuesForNestedResultMap(...)
|--DefaultResultSetHandler#getRowValue(...)
|--DefaultResultSetHandler#createResultObject(...):创建映射结果对象
|--DefaultResultSetHandler#applyAutomaticMappings(...)
|--DefaultResultSetHandler#applyPropertyMappings(...)
- 主要涉及到的类:
DefaultResultSetHandler
- resultType最终会封装成resultMap
待分析的问题
- 延迟加载的流程
- 嵌套结果集映射流程