目录
mybatis 执行流程深入分析
按照2个主要步骤来分析执行查询的流程
1. jdbc查询的流程
2. 处理返回结果
分析的时候涉及到的概念性名词在附注之后说明
阅读本文时,假定以下条件
1. 理解反射相关知识
2. 理解代理模式
本文忽略的部分:
1. 查询时的参数赋值
2. 延迟加载
3. 元数据对象
4. 结果集处理器
笔者认为,忽略的部分都可以单独拿出来详细说明.本文对于忽略的部分进行了简单的概念介绍.不影响阅读
例子
mybatis 查询所有
@Test
public void testRead() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
selectAll
List<Blog> blog = sqlSession.selectList("com.aya.mapper.BlogMapper.selectAll");
System.out.println(blog);
} finally {
sqlSession.close();
}
}
selectAll 的sql
<select id="selectAll" resultType="com.aya.mapper.Blog" >
select * from blog
</select>
分析-执行查询流程
准备查询
@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 {
// 获取映射声明
MappedStatement ms = configuration.getMappedStatement(statement);
//wrapCollection 包装集合对象
//executor.query 执行查询
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();
}
}
com.aya.mapper.BlogMapper.selectAll
获取对象 MappedStatement- wrapCollection 包装集合
- executor.query 执行器执行查询
执行查询-绑定key与缓存
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取绑定的sql
BoundSql boundSql = ms.getBoundSql(parameter);
// 获取缓存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 执行查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
执行查询-缓存与延迟加载
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 记录执行上下文信息
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//queryStack 处理延迟加载
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 从本地缓存获取数据
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 处理输出参数
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 从数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
// 处理延迟加载
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
这里做的相当于一个统筹的工作
- ErrorContext是用来记录当前步骤了,发生异常的时候回打印,详情:错误上下文
- 使用queryStack 处理延迟加载.当 ResultMap 嵌套association|collection 查询时
queryStack>0
.
queryStack == 0 时,处理延迟加载的操作- localCache 从本地缓存中获取数据,获取到之后处理输出数据
- queryFromDatabase 从数据库查询
接下来分析从数据库查询.
不对缓存,延迟加载,异常上下文详细说明
执行数据库查询-占位符
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 本地缓存存入占位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//执行数据库查询
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 本地缓存移除占位符
localCache.removeObject(key);
}
// 本地缓存存入真实结果
localCache.putObject(key, list);
// 如果是存储过程,本地输出参数缓存
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
这里任然是做的一个统筹的工作
1. 本地缓存存入占位符
2. 本地缓存存入真实结果
存入占位符的目的是什么? 当ResultMap嵌套的属性是延迟加载的时候,判断是不是占位符.
执行数据库查询-执行
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// // 创建 StatementHandler, 用工厂模式获得对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 获得连接,预处理查询
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查询,并返回处理返回结果
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
- StatementHandler 是mybatis定义的一个,对于 Statement,PrepareStatement,CallableStatement 三种JDBC 查询的封装
- prepareStatement 执行对应的 createStatement,prepareStatement,prepareCall
- 执行查询获得ResultSet,返回处理结果
- 关闭Statement (
stat.close()
)
到这里JDBC的查询就已经执行完成了。
查询流程总结
整体来看做了以下处理
1. 缓存处理
2. 延迟加载处理
3. 异常信息记录
分析-处理返回结果
执行查询-准备处理返回结果
RoutingStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
- RoutingStatementHandler实际上是一个代理模式,内部执行是用PreparedStatementHandler|StatementHandler|CallableHandler
- PreparedStatementHandler 执行查询
- DefaultResultSetHandler 执行处理结果
处理返回结果-统筹结果集
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
// 记录信息
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
// 查询可以返回多个结果集,ResultSet 可以有多个, 这里获得第一个
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获得 Xml 对应的 ResultMap 映射对象,记录了类实例字段和数据库表字段对应关系的列表
// 但是这里笔者只使用过 在select标签定义一个resultMap,并不会定义多个。
// 不理解这里为什么要定义为一个集合
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 验证是不是有结果集返回,如果没有结果集返回,抛出异常
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
// 获得select标签定义的 ResultMap 映射对象
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集将结果存入multipleResults
handleResultSet(rsw, resultMap, multipleResults, null);
// 遍历结果集
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 处理select 标签的resultSet 定义的结果集信息
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++;
}
}
// 展开并返回List<List<T>> 的第一个结果集
return collapseSingleResultList(multipleResults);
}
处理返回结果-统筹结果集处理器
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
// 父映射不为空
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
// 默认结果集处理器
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
// 自定义结果集处理器
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
这里对多种情况进行判断,接下来进行常用的默认结果集处理器分析
处理返回结果-统筹嵌套映射
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
// 是否有嵌套的 ResultMap
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 顶层ResultMap 处理
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
处理返回结果-遍历与存储
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
// 内存忽略读取n行
skipRows(rsw.getResultSet(), rowBounds);
遍历ResultSet 查询到的结果,依次添加到resultHandler
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
// 根据ResultMap 的 discriminated 标签进行选择具体的ResultMap 对象
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
//创建对象, 将第 n 行的数据给对象
Object rowValue = getRowValue(rsw, discriminatedResultMap);
// 将对象用 resultHandler 进行处理。
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
- 内存忽略读取N行
- 获得返回结果
- 存储到 resultHandler 的 list
处理返回结果-对象的创建于赋值
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 创建对象的元数据对象
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 应用自动映射, 类字段和表字段一致时赋值
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
// 应用手动映射, result 定义的 column 与 property
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
// 如果找到了值或者 mybatis.xml configuration>settings>returnInstanceForEmptyRow=true 时,返回对象(有可能并未填充值)
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
- 根据resultMap 的定义,创建对象
- 创建元数据对象, (赋值时使用元数据赋值)
- 自动映射赋值
- 手动映射赋值
- 实际找到的结果和 returnInstanceForEmptyRow 综合, 返回空对象或者null
结论
对于mybatis结果为空时:
返回一定不为null 的只有一种顶层集合
返回null 的情况有三种
1. 嵌套对象
2. 嵌套集合
3. 顶层对象
但是返回为null也可以通过xml配置 configuration>settings>returnInstanceForEmptyRow=true
, 设置结果集一定不为 null
附注
映射声明
MappedStatement 是mybatis的一个映射声明
在加载xml的时候添加到Configuration实例的字段Map<String, MappedStatement> mappedStatements
中
错误上下文
ErrorContext 是用来记录当前操作线程正在执行的信息
在准备查询时,捕获所有异常,并打印ErrorContext记录的的信息
ExceptionFactory的wrapException
public static RuntimeException wrapException(String message, Exception e) {
return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
}
ErrorContext的toString
@Override
public String toString() {
StringBuilder description = new StringBuilder();
// message
if (this.message != null) {
description.append(LINE_SEPARATOR);
description.append("### ");
description.append(this.message);
}
// 版面原因,省略其他打印的信息
return description.toString();
}
jdbc查询
选自 https://www.cnblogs.com/wuyuegb2312/p/3872607.html 的一部分
private static Connection getConn() {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/samp_db";
String username = "root";
String password = "";
Connection conn = null;
try {
Class.forName(driver); //classLoader,加载对应驱动
conn = (Connection) DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
private static Integer getAll() {
// 创建jdbc连接
Connection conn = getConn();
String sql = "select * from students";
PreparedStatement pstmt;
try {
// 创建Statement
pstmt = (PreparedStatement)conn.prepareStatement(sql);
// 执行查询
ResultSet rs = pstmt.executeQuery();
int col = rs.getMetaData().getColumnCount();
System.out.println("============================");
// 处理返回结果
while (rs.next()) {
for (int i = 1; i <= col; i++) {
System.out.print(rs.getString(i) + "\t");
if ((i == 2) && (rs.getString(i).length() < 8)) {
System.out.print("\t");
}
}
System.out.println("");
}
System.out.println("============================");
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
结果集
结果集可以在sqlSession.selectList
自定义,
不传入结果集时使用默认结果集处理器 DefaultResultHandler
元数据对象
MetaObject 内部有真实的对象, 内部对 Bean,Collection,Map 的赋值有自己的处理
主要可以通过 setValue 方法,进行反射赋值