MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单、优雅。本文主要讲述MyBatis的架构设计思路,然后探究MyBatis的是如何实现查询的。
MyBatis执行流程
利用MyBatis实现一次查询
InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//创建SqlSessionFactory
SqlSessionFactory factory = builder.build(inputStream);
//从SqlSession工厂 SqlSessionFactory中创建一个SqlSession,进行数据库操作
SqlSession sqlSession = factory.openSession();
//使用SqlSession查询
Map<String,Object> params = new HashMap<String,Object>();
params.put("xxx",xxx);
//执行查询
List<Object> result = sqlSession.selectList("mapper.sqlId",params);
- SqlSession sqlSession = factory.openSession():开启一个sqlSession,它表示和数据库交互的会话,完成必要数据库增删改查功能。
- 执行sqlSession.selectList:返回查询结果List,Mybatis和数据库进行交互的流程都在此方法中。
那么探究MyBtis就由此进入吧!
selectList(String statement, Object parameter)方法源码:
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据Statement Id,在mybatis 配置对象Configuration中查找和配置文件相对应的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//查找好以后交给executor.query方法执行下面的流程
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
<select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" >
select
*
from table
</select>
说明:根据Statement获得StatementId(如上面配置文件的Id为selectByMinSalary),通过StatementId找出配置文件mapper.xml中对应的MappedStatement,并且将其交给executor的query方法来执行。
Executor的实现类BaseExecutor.query方法源码
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//动态地生成需要执行的SQL语句,用BoundSql对象表示
BoundSql boundSql = ms.getBoundSql(parameter);
//设置一级缓存的key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
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.");
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();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}
//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;
}
//SimpleExecutor(BaseExecutor子类)类的doQuery()
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//获得配置对象configuration
Configuration configuration = ms.getConfiguration();
//创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//StatementHandler根据configuration创建Statement
stmt = prepareStatement(handler, ms.getStatementLog());
//交给StatementHandler.query执行
//成对数据库的查询,最终返回List结果集。
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
说明:Executor的功能和作用是
1.创建BoundSql;
2.创建缓存
3.将BoundSql传递给StatementHandler,由StatementHandler来完成对数据库的查询,以及返回List。
StatementHandler
接着我们进入prepareStatement方法设置参数:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
//对statement设置参数,就是对?占位符赋值
handler.parameterize(stmt);
return stmt;
}
进入parameterize方法:
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
此时我们知道,是通过使用ParameterHandler对象来完成对Statement的设值 ,将一个一个参数填充到”?”中。
此时Statement对象构建完成了,是一个可用的SQL语句了。
StatementHandler.query()方法:
//PreParedStatementHandler类来实现query
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//将statement转为PreparedStatement
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
//通过使用ResultHandler来处理ResultSet来返回List
return resultSetHandler.<E> handleResultSets(ps);
}
说明:StatementHandler通过ResultHandler的handleResultSets方法,实现将ResultSet结果集转换成List 结果集。
ResultHandler
ResultHandler.handleResultSets用来将ResultSet结果集转换成List
源码如下
//
// HANDLE RESULT SETS
//DefaultResultSetHandler的handleResultSets方法
public List<Object> handleResultSets(Statement stmt) throws SQLException {
//这里是List
final List<Object> multipleResults = new ArrayList<Object>();
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);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResulSets();
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);
}
总结
1.开启一个sqlSession
2.sqlSession根据StatementId得到一个MapperStatement,并交给Executor来执行查询
3.Executor得到MapperStatement后,会生成一个boundsql,并且创建一个代表缓存的cacheKey,如果对于缓存中有数据,则直接返回;如果缓存中没有,就需要boundsql传递且借助StatementHandler去数据库中获得数据,并返回结果集List,之后在加入缓存。
4.StatementHandle**r获得boundsql以后,借助**ParameterHandler 来讲参数设置进Statement并返回一个PreparedStatement对象,此时执行查找返回结果集ResultSet。StatementHandler此时再借助ResultHandler.handleResultSets用来将ResultSet结果集转换成List 。
借用大神的一张图来概括上述流程: