# mybatis 源码分析(二) sql执行路径分析
- mybatis 源码分析(一) Xml解析,容器初始化
- mybatis 源码分析(二) sql执行路径分析
- mybatis 源码分析(三) 插件原理
- mybatis 源码分析(四) 自带连接池
上一章我们分析了mybatis的初始化过程,今天我们来了解下
mybatis sql的执行原理
MapperProxy
上一章也分析到mybaits生成的mapper bean 实际上是由MapperProxy 这个类生成的动态代理类.所以今天从这个类下手
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
/** 代理mapper接口中的所有方法 */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
/** 执行方法 */
return mapperMethod.execute(sqlSession, args);
}
/** 缓存mapperMethod */
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
MapperMethod
public class MapperMethod {
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
/** 方法对应的sql信息 */
this.command = new SqlCommand(config, mapperInterface, method);
/** 方法的签名 */
this.method = new MethodSignature(config, method);
}
/** 所有的sql执行方法都是从SqlSession为入口的
* 从第一章知道 SqlSession的默认实例对象是 DefaultSqlSession
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
/** command.getName 返回的是 类的全限定名+方法 */
result = sqlSession.selectOne(command.getName(), param);
}
} else {
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;
}
}
DefaultSqlSession
public class DefaultSqlSession implements SqlSession
{
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
/** 这里遇到了一个很重要的类 MappedStatement 这个类包含了SQL执行的参数
* 也包括了 SQL 构建类 SqlSource,
* 该类有个参数 id 是以 类的全限定名+方法 构建的.
* ex: com.sun.mapper.EntityMapper.selectEntityById
* mybatis 生成ms的地方 解析mapper xml中的sql语句的时候(上一章有提到)
* 还有就是 使用了注解的接口 比如 使用了
* tk.mybatis.mapper.common.Mapper
* 我们来看下这2个地方的实现
*/
MappedStatement ms = this.configuration.getMappedStatement(statement);
this.executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
mapper Xml 解析 添加MS
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
/** 配置XML元素 */
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
/** 绑定注解类mapper */
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
/** 构建MS 继续跟踪下去会到MapperBuilderAssistant的 addMappedStatement 方法*/
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
}
MapperBuilderAssistant
public class MapperBuilderAssistant extends BaseBuilder {
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) throw new IncompleteElementException("Cache-ref not yet resolved");
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
statementBuilder.resource(resource);
statementBuilder.fetchSize(fetchSize);
statementBuilder.statementType(statementType);
statementBuilder.keyGenerator(keyGenerator);
statementBuilder.keyProperty(keyProperty);
statementBuilder.keyColumn(keyColumn);
statementBuilder.databaseId(databaseId);
statementBuilder.lang(lang);
statementBuilder.resultOrdered(resultOrdered);
statementBuilder.resulSets(resultSets);
setStatementTimeout(timeout, statementBuilder);
setStatementParameterMap(parameterMap, parameterType, statementBuilder);
setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
MappedStatement statement = statementBuilder.build();
/** 最终通过mybatis的配置类完成了添加操作 */
configuration.addMappedStatement(statement);
return statement;
}
}
注解方法 添加MS
public class XMLMapperBuilder extends BaseBuilder {
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
/** 从这里跟踪下去 会到MapperRegistry的addMapper 方法 */
configuration.addMapper(boundType);
}
}
}
}
}
MapperRegistry
public class MapperRegistry {
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
/** MapperAnnotationBuilder 看名字就知道是通过注解来构建mapper的 */
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
/** 继续往下跟 */
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}
MapperAnnotationBuilder
public class MapperAnnotationBuilder {
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
/** 构造函数中添加需要解析的注解 */
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
/** 判断是否是 桥接方法 */
if (!method.isBridge()) { // issue #237
/** 生成ms */
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
void parseStatement(Method method) {
/** 方法太长了 省略 重点 来了
* assistant是MapperBuilderAssistant的实例的引用
* 所以这里跟xml的解析最终走到一起了
*/
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
null, // ParameterMapID
parameterTypeClass,
resultMapId, // ResultMapID
getReturnType(method),
resultSetType,
flushCache,
useCache,
false, // TODO issue #577
keyGenerator,
keyProperty,
keyColumn,
null,
languageDriver,
null);
}
}
}
现在 我们都知道了MappedStatement 的来源了 以及作用了。接下来就从头看DefaultSqlSession.select 方法
DefaultSqlSession
public class DefaultSqlSession implements SqlSession {
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
/** statement 入参就是 类的全限定名字 + 方法名 */
MappedStatement ms = configuration.getMappedStatement(statement);
/** 在第一章说到 executor 默认实现是 SimpleExecutor */
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
SimpleExecutor
public class SimpleExecutor extends BaseExecutor {
/** 该方法在父类申明的 */
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
/** 获取sql 注解方法就是由DynamicSqlSource 动态生成的
* 其他方法就是从xml中读到的sql
*/
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
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();
/** prepareStatement组装一个Statement */
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
/** 动态获取一个连接 */
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
}
之后就是拿到connection 执行sql ~~~~ over