以前一直想看mybatis的源代码,但是一直没找到入口(傻),最近看教程,有些感悟。

和起以前一样,关键代码我会用红色标记。

首先,先贴下我的dao和mapper,代码很简单,和平时写的hello world 差不多。代码如下:

  <select id="select" parameterType="INTEGER" resultMap="BaseResultMap">
SELECT * from t_user where id=#{id}
</select>
@Mapper
public interface UserMapper {
int insert(User record); int insertSelective(User record); int update(User user); User select(int id);
}

service层代码:

@Service
public class UserService { @Autowired
private UserMapper userMapper; public User getUser(int id){
return userMapper.select(id);
}
}

这个 userMapper.select(id) 就是入口了。以前一直没有在运行的时候调试过这个,还以为是直接就调用了 User select(int id),傻逼了。跟踪进入代码:

  @Override
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);
}

从代码可以看出,这里使用了MapperProxy 代理(启动程序的时候已经生成),这是一个jdk代理(实现了InvocationHandler 接口),因为这个调用的userMapper是一个接口,,接口的默认实现是jdk代理。继续跟进

public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
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()) { //这里检查是否会有结果集处理
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) { //针对集合list进行处理
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) { //针对ResultMap处理
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) { //针对游标处理(这是什么?)
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
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;
}

上面进的代码,主要是区分了sql的类型:insert、update、delete、select、flush。继续跟进代码,下面的代码进入SqlSession模块:

#org\mybatis\mybatis-spring\1.3.1\mybatis-spring-1.3.1.jar!\org\mybatis\spring\SqlSessionTemplate.class

private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
} unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
} throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
} } return unwrapped;
}
}
#org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\session\defaults\DefaultSqlSession.java  

@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.<T>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 <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
 #org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\plugin\Plugin.java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) { //注意这里,我们自定义插件的时候,就是在这里起作用的。如果有自定义插件,则会被拦截
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}

下面开始进入Executor模块,代码如下:

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

@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, parameterObject, 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);
}
 

这个类是执行器类。可以看到,这里是一个装饰器模式,通过重写Excutor的方法,进行了自己的方法的一些内容的添加(装饰器模式,保留核心功能,同时又新加了一些功能)。

#org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\executor\BaseExecutor.java
@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.");
}
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;
}

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;
}
 
#org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\executor\SimpleExecutor.java 

@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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

从SimpleExecutor.doQuery()方法可以看出,这个是流程是:1、获取链接Connection  2、调用StateMentHandler执行

#org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\executor\statement\RoutingStatementHandler.java

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\executor\statement\PreparedStatementHandler.java

  @Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}

从上面可以看出,mybatis最终调用的是jdbc的PrepareStatement 的execute方法执行sql,并且进行结果映射。再看下PrepareStatement

的execute()方法

#mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar!\com\mysql\jdbc\PreparedStatement.class
public boolean execute() throws SQLException {
synchronized(this.checkClosed().getConnectionMutex()) {
MySQLConnection locallyScopedConn = this.connection;
if (!this.doPingInstead && !this.checkReadOnlySafeStatement()) {
throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") + Messages.getString("PreparedStatement.21"), "S1009", this.getExceptionInterceptor());
} else {
ResultSetInternalMethods rs = null;
this.lastQueryIsOnDupKeyUpdate = false;
if (this.retrieveGeneratedKeys) {
this.lastQueryIsOnDupKeyUpdate = this.containsOnDuplicateKeyUpdateInSQL();
} this.batchedGeneratedKeys = null;
this.resetCancelledState();
this.implicitlyCloseAllOpenResults();
this.clearWarnings();
if (this.doPingInstead) {
this.doPingInstead();
return true;
} else {
this.setupStreamingTimeout(locallyScopedConn);
Buffer sendPacket = this.fillSendPacket();
String oldCatalog = null;
if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
oldCatalog = locallyScopedConn.getCatalog();
locallyScopedConn.setCatalog(this.currentCatalog);
} CachedResultSetMetaData cachedMetadata = null;
if (locallyScopedConn.getCacheResultSetMetadata()) {
cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
} Field[] metadataFromCache = null;
if (cachedMetadata != null) {
metadataFromCache = cachedMetadata.fields;
} boolean oldInfoMsgState = false;
if (this.retrieveGeneratedKeys) {
oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
locallyScopedConn.setReadInfoMsgEnabled(true);
} locallyScopedConn.setSessionMaxRows(this.firstCharOfStmt == 'S' ? this.maxRows : -1);
rs = this.executeInternal(this.maxRows, sendPacket, this.createStreamingResultSet(), this.firstCharOfStmt == 'S', metadataFromCache, false); //最终实际执行sql语句的地方
if (cachedMetadata != null) {
locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, cachedMetadata, rs);
} else if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) {
locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, (CachedResultSetMetaData)null, rs);
} if (this.retrieveGeneratedKeys) {
locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
rs.setFirstCharOfQuery(this.firstCharOfStmt);
} if (oldCatalog != null) {
locallyScopedConn.setCatalog(oldCatalog);
} if (rs != null) {
this.lastInsertId = rs.getUpdateID();
this.results = rs;
} return rs != null && rs.reallyResult();
}
}
}
}

下面是结果映射的代码:

#org\mybatis\mybatis\3.4.5\mybatis-3.4.5-sources.jar!\org\apache\ibatis\executor\resultset\DefaultResultSetHandler.java
@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;
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.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);
}

注意:一个有意思的地方是,调用insert()的时候,最终调用的还是update(),代码在DefaultSqlSession中展现,如下:

  @Override
public int insert(String statement) {
return insert(statement, null);
} @Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

总结:

mybatis的执行过程:

1、调用代理对象的execute方法执行

2、判断sql类型,分别处理增、删、改、查操作

3、转换参数成为sql参数。如果是单个参数,则直接返回,如果是多个参数,则用param1、param2 这样数组返回

4、如果一级缓存已经有缓存了这个方法,调用缓存的方法;如果没有则调用查询数据库的方法

5、获取connection

6、调用PrepareStateMent的execute方法

7、结果集映射

8、最后,调用sqlsession.commit()提交

select语句的类调用顺序:SqlSession 相关类->Executor 相关类 -> PrepareStatement 相关类

最新文章

  1. 简单明了区分escape、encodeURI和encodeURIComponent
  2. 图形化查看maven的dependency依赖
  3. 2017微软秋招A题
  4. hdu 4883 思维题
  5. algorithm: heap sort in python 算法导论 堆排序
  6. mysql 索引相关知识
  7. SNMP配置和常用命令OID(转)
  8. OpenStack使用Bosh部署CloudFoundry(一)—准备OpenStack环境
  9. libevent库简单使用
  10. WebApi系列(从.Net FrameWork 到 .Net Core)
  11. UOJ#75. 【UR #6】智商锁 随机化算法 矩阵树定理
  12. DRF 视图和路由
  13. plsql的汉化问题
  14. UVa 11134 - Fabled Rooks 优先队列,贪心 难度: 0
  15. [转帖]从1G到5G
  16. Python基础3--Python复杂数据类型
  17. xadmin系列之django的url分发的方式
  18. 机器学习之路: 深度学习 tensorflow 神经网络优化算法 学习率的设置
  19. 【源码学习之spark core 1.6.1 各种部署模式所使用的的TaskSceduler及SchedulerBackend】
  20. Thread之七:Object里的wait、notify、notifyAll的使用方法

热门文章

  1. Dashboard究竟是什么,它在数据展示上的优势何在?
  2. 关于python的turtle详解(转载)
  3. 【基础知识】cache 管线(Pipeline)的建立便可以提升cpu的性能,为什么还要去发展多核的cpu?
  4. Invoke and BeginInvoke
  5. Map&lt;String,String&gt;转Json转Base64
  6. 微服务从代码到k8s部署应有尽有系列(九、事务精讲)
  7. Qt:QUrl
  8. visual studio 快捷键重置及设置
  9. Windows用户如何安装cpolar内网穿透
  10. WPF优秀组件推荐之Stylet(二)