MyBatis工作原理源码分析(四)——Sql的执行过程

一、引言

手动使用Mybatis的四个步骤:

获取SqlSessionFactory对象
获取sqlSession对象
获取接口的代理对象(MapperProxy)
执行增删改查方法

MyBatis工作原理源码分析(一)——SqlSessionFactory的初始化
MyBatis工作原理源码分析(二)——获取SqlSession对象
MyBatis工作原理源码分析(三)——获取接口的代理对象(MapperProxy)

前三篇详细分析了第一步、第二步和第三步,下面在此基础上,继续来分析代理对象是如何执行增删改查Sql的。

二、源码分析

// 4、执行增删改查方法
Department dept = mapper.getDeptById(1);

接下来,要真正去看sql的执行过程了。

在第三步,我们拿到了MapperProxy,每个MapperProxy对应一个Dao接口(DepartmentMapper.class), 那么我们在使用接口中方法的时候,MapperProxy是怎么做的呢?

在第三步的源码分析中,我们知道第三步获取到的其实是接口(DepartmentMapper.class)的代理对象MapperProxy(该对象中有SqlSession和Configuration)。因为MapperProxy实现了InvocationHandler接口,所以在执行接口中的目标方法之前,要执行invoke()方法,在invoke()方法中去执行目标方法。

1、执行代理对象MapperProxy的invoke()方法

//MapperProxy的invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
  	//判断是不是Object方法
    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);
  }
  
  //不是Object的方法,将Method封装为MapperMethod
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  //传入sqlSession,args为sql需要的参数
  return mapperMethod.execute(sqlSession, args);
}

2、将Method封装为MapperMethod后,调用execute()

MapperMethod中的execute()方法,先判断CRUD类型,然后根据类型去选择到底执行SqlSession中的哪个方法,绕了一圈,又转回SqlSession了。

public Object execute(SqlSession sqlSession, Object[] args) {
   Object result;
   //判断增删改查类型
   switch (command.getType()) {
     case INSERT: {//insert
   	Object param = method.convertArgsToSqlCommandParam(args);
       result = rowCountResult(sqlSession.insert(command.getName(), param));
       break;
     }
     case UPDATE: {//update
       Object param = method.convertArgsToSqlCommandParam(args);
       result = rowCountResult(sqlSession.update(command.getName(), param));
       break;
     }
     case DELETE: {//delete
       Object param = method.convertArgsToSqlCommandParam(args);
       result = rowCountResult(sqlSession.delete(command.getName(), param));
       break;
     }
     case SELECT://select
       if (method.returnsVoid() && method.hasResultHandler()) {
         executeWithResultHandler(sqlSession, args);
         result = null;
       } else if (method.returnsMany()) {//返回结果多个
         result = executeForMany(sqlSession, args);
       } else if (method.returnsMap()) {//返回结果为Map
         result = executeForMap(sqlSession, args);
       } else if (method.returnsCursor()) {//返回结果为Cursor
         result = executeForCursor(sqlSession, args);
       } else {//返回结果不是上面的情况,此处查询的1条数据,所以进入属于这种情况
       	 //将参数转换一下
         Object param = method.convertArgsToSqlCommandParam(args);
         //调用SqlSession的selectOne()方法
         result = sqlSession.selectOne(command.getName(), param);
       }
       break;
     case FLUSH:
       result = sqlSession.flushStatements();
       break;
     default:
       throw new BindingException("Unknown execution method for: " + command.getName());
   }
   
   return result;
 }

//调用SqlSession中的selectOne()方法
@Override
public <T> T selectOne(String statement, Object parameter) {
  //最终还是调用selectList()方法
  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;
  }
}

3、SqlSession的selectList()方法

既然又回到SqlSession了, 那么下面就看看SqlSession的CRUD方法了,这里为了省事,我们选择了selectList()方法。

//selectList()方法
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    // 从Configuration中获取得到MappedStatement,
    // 我们知道,一个MappedStatement对象,就代表一个增删改查标签的详细信息。
    MappedStatement ms = configuration.getMappedStatement(statement);
    
    // CRUD实际上是交给Excetor去处理, Excutor其实也只是穿了个马甲而已
    // wrapCollection(parameter)方法,用于包装传入参数
    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();
  }
}

// 包装参数的wrapCollection()方法
private Object wrapCollection(final Object object) {
  if (object instanceof Collection) {//参数为Collection类型
    StrictMap<Object> map = new StrictMap<Object>();
    map.put("collection", object);//将参数放入Map中,键为collection,值为参数
    if (object instanceof List) {//参数为List类型
      map.put("list", object);//将参数放入Map中,键为list,值为参数
    }
    return map;
  } else if (object != null && object.getClass().isArray()) {//参数类型为数组
    StrictMap<Object> map = new StrictMap<Object>();
    map.put("array", object);//将参数放入Map中,键为array,值为参数
    return map;
  }
  return object;
}

4、调用Executor的query()方法

//BaseExecutor类中的query()方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  //BoundSql代表当前SQL的详细信息
  BoundSql boundSql = ms.getBoundSql(parameter);
  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

在这里插入图片描述

//BaseExecutor类中的query()方法
@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 {
      //从数据库中查询数据,ms代表一条sql的详细信息,parameter是参数
      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 {
  	//执行查询操作!!!最终执行sql的地方
    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;
}

5、调用doQuery()方法

然后,通过上面一层一层的调用,最终会来到doQuery方法, 这儿就随便找个Excutor看看doQuery()方法的实现,这儿选择了SimpleExecutor:

@Override
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);
    //得到Statement 的前置工作
    stmt = prepareStatement(handler, ms.getStatementLog());
    
    //StatementHandler封装了Statement, 让 StatementHandler 去处理
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  //得到与数据库的连接
  Connection connection = getConnection(statementLog);
  //得到Statement 
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}

接下来,咱们看看StatementHandler 的一个实现类 PreparedStatementHandler(这也是我们最常用的,封装的是PreparedStatement), 看看它使怎么去处理的:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  //到此,原形毕露, PreparedStatement
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  //结果交给了ResultSetHandler 去处理
  return resultSetHandler.<E> handleResultSets(ps);
}

6、查询流程总结

在这里插入图片描述
在这里插入图片描述
作用说明:
(1)、StatementHandler:处理sql语句预编译,设置参数等相关工作;
(2)、ParameterHandler:设置预编译参数用的
(4)、ResultHandler:处理结果集
(5)、TypeHandler:在整个过程中,进行数据库类型和javaBean类型的映射

三、小结

在下面的时序图中,需要注意StatementHandler、ParameterHandler、ResultSetHandler的创建
在这里插入图片描述

四、总结

终于把四个流程分析完啦,下面来总结一下:

1、根据配置文件(全局,sql映射)初始化出Configuration对象;
2、创建一个DefaultSqlSession对象;
	SqlSession里面包含Configuration以及Executor,
	根据全局配置文件中的defaultExecutorType创建出对应的Executor
3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
	MapperProxy里面有(DefaultSqlSession);
4、执行增删改查方法:
	1)、调用DefaultSqlSession的增删改查(Executor);
	2)、会创建一个StatementHandler对象。(同时也会创建出ParameterHandler和ResultSetHandler)
	3)、调用StatementHandler预编译参数以及设置参数值;
		使用ParameterHandler来给sql设置参数
	4)、调用StatementHandler的增删改查方法;
	5)、ResultSetHandler封装结果
4、注意:
四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)
每个创建的时候都有用interceptorChain.pluginAll()进行封装后,才得到对应的对象。
对Mybatis插件开发会有作用哦。
//创建Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

//创建StatementHandler 
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

//ParameterHandler 
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}

//StatementHandler 
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}
发布了123 篇原创文章 · 获赞 230 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/zxd1435513775/article/details/87544040