以前一直想看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模块:
#orgmybatismybatis-spring1.3.1mybatis-spring-1.3.1.jar!orgmybatisspringSqlSessionTemplate.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; } }
#orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatissessiondefaultsDefaultSqlSession.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();
}
}
#orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatispluginPlugin.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的方法,进行了自己的方法的一些内容的添加(装饰器模式,保留核心功能,同时又新加了一些功能)。
#orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorBaseExecutor.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;
}
#orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorSimpleExecutor.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执行
#orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorstatementRoutingStatementHandler.java
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); }
orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutorstatementPreparedStatementHandler.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()方法
#mysqlmysql-connector-java5.1.46mysql-connector-java-5.1.46.jar!commysqljdbcPreparedStatement.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(); } } } }
下面是结果映射的代码:
#orgmybatismybatis3.4.5mybatis-3.4.5-sources.jar!orgapacheibatisexecutor esultsetDefaultResultSetHandler.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 相关类