jdbc执行流程图
执行sql的过程就是发起请求的过程, 前文中已经学习了如何建立并获取数据库连接, 本文主要就是学习一下在service调用mapper获取结果的过程, 也就是上图的3~6的过程
方法调用链
首先通过上文, 我们知道了Mybatis执行sql必须要先获取数据库的连接, 所以我们在SimpleExecutor这个类中的prepareStatement方法中获取了connection, 我们找到调用prepareStatement的方法, 这个方法应该就是查询方法doQuery
@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();
// 这里是获取一个statement的handler, handler主要是处理生成statement的
// 在newStatementHandler中还过了一次拦截器链
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 使用handler获取一个statement
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
至于statement是如何生成的, 我们以后再看
点击进入query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// ps中的内容截图在下方
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
我们可以看见, 这里存放的是我们的入参, 以及前面处理好的sql语句, 即实际在mysql中使用的sql语句,
PreparedStatement 的execute方法就是将sql语句送往mysql执行
接下来这个statement通过getResultSet()方法获取结果集, 这里用到了代理
这是最后执行的方法
public ResultSet getResultSet() throws SQLException {
// 传入的statement中有delegate属性, delegate可以getResultSet
final ResultSet resultSet = delegate.getResultSet();
if (resultSet != null) {
if (proxyResultSet == null || ((ProxyResultSet) proxyResultSet).delegate != resultSet) {
proxyResultSet = ProxyFactory.getProxyResultSet(connection, this, resultSet);
}
}
else {
proxyResultSet = null;
}
return proxyResultSet;
}
这一步的return中会处理上面获得的结果集
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
封装结果集
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
// 获取元数据
final ResultSetMetaData metaData = rs.getMetaData();
// 获取查询出来的字段个数
final int columnCount = metaData.getColumnCount();
// 循环设置字段名以及mysql的变量类型和java的类型映射
for (int i = 1; i <= columnCount; i++) {
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
classNames.add(metaData.getColumnClassName(i));
}
}
结果集返回给resultSetHandler
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
// 这里接收上面的结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
// 处理指定个数的结果集
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 进入DefaultResultSetHandler中的结果集处理
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
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中的结果集处理, 具体操作不在本文描述, 以后再讲
通过这个方法处理结果集, 会返回我们需要的java类型, 并且将字段的值注入
handleRowValues这个方法中会处理字段和值的映射
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)
// 对应于jdbc中的关闭结果集操作
closeResultSet(rsw.getResultSet());
}
}
结果集关闭后就层层返回到doQuery关闭statement, 由于我们的结果设置的不是list也不是map, 所以使用的是DefaultSqlSession中的selectOne方法
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 可以看见无论结果设置是否多个, 都是调用selectList去数据库中查询
List<T> list = this.selectList(statement, parameter);
// 由于是selectOne, 所以这里返回的list必须是只有一个元素
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;
}
}
最后的结果返回SqlSessionTemplate类, 这个拦截器是template的内部类,
是在MapperMethod类中的execute方法的selectOne进入的方法,
大家去看看Template类可以发现, selectOne返回的是this.sqlSessionProxy.selectOne,
而这个sqlSessionProxy属性是构造器中new出来的内部类SqlSessionInterceptor
也就是最后进入的其实是下面这个invoke方法
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// 这里这个invoke进入的是DefaultSqlSession中的selectOne方法
// 真正被执行的selectOne
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
// 最后关闭会话
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
我们调用自己写的mapper中的方法之后, 会通过代理进入MapperProxy中, 可以看见这里调用了上面的execute, 这个invoke返回之后, 就会在我们的service得到最终的java对象
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 (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
后记
本篇主要是讲了讲Mybatis在执行sql的时候, 得到数据库连接后, 何时创建statement, 何时创建resultSet
不难发现, mybatis的很多实现都是依赖代理, 我们调用mapper方法后就会进入MapperProxy, 这里就是一个代理
如果除去所有代理的话, 其实流程就是先判断返回的结果集个数从而决定调用selectOne还是selectList方法, 然后判断一级缓存, 如果不存在就去数据库查找, 查找过程就是先通过Hikari中获取的connection创建statement, 然后提交sql到数据库, 获得结果后通过resultSetHandler处理结果返回到我们调用mapper方法的service中