MyBatis 查询SQl流程
MyBatis myBatis = mapper.getOne(1L);
-
因为mapper对象是被MapperProxy类代理的实体类所以所有方法调用都会走到
MapperProxy::invoke(此方法是MapperProxy继承InvocationHandler接口并重写的) -
MapperProxy类中有个对象methodCache,是专门用来缓存方法的,每次请求
MapperProxy对象都会尝试从缓存里面获取此方法,如果获取不到,就实例化此方法,然后缓存,再返回此方法的实例化
private final Map<Method, MapperMethod> methodCache; -
每一个方法在MyBatis中就对应一个MapperMethod类,最后的SQl请求就是由此类的对象发起的,MapperMethod中有两个
属性,分别是 SqlCommand command;和MethodSignature method;
其中,method属性主要是方法的信息,比如方法返回值,方法传参等数据都是存放在这个对象里面
command对象主要存放方法名,和SQL类型
invoke();方法
@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);
// 执行SQL
return mapperMethod.execute(sqlSession, args);
}
MapperMethod{};类
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
}
- 执行SQL mapperMethod.execute(sqlSession, args);mapperMethod就是MapperMethod类的实体类
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
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()) {
// 返回值是Map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 返回值是指针(java的一个类Cursor.class)
result = executeForCursor(sqlSession, args);
} else {
// 单对象返回
// 参数映射(其他类型的请求,他们的参数映射都隐式得写到的对应的方法里) 经过映射后的 id=1 为:
// {
// "id":1,
// "param1":1
// }
Object param = method.convertArgsToSqlCommandParam(args);
// 执行SQL获取返回值
result = sqlSession.selectOne(command.getName(), param);
}
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;
}
- 参数映射 method.convertArgsToSqlCommandParam(args);
参数映射(其他类型的请求,他们的参数映射都隐式得写到了对应的方法里)
经过映射后的 id=1 为:
{
"id":1,
"param1":1
}
- 执行SQL sqlSession.selectOne(command.getName(), param);
默认的SQL执行类为DefaultSqlSession,此类继承了SqlSession接口,具有默认的请求方式
@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 {
// 这里可以看到,mybatis查不到数据时,真的是返回null的
return null;
}
}
- 查询列表SQL List list = this.selectList(statement, parameter);
@Override
public<E> List<E> selectList(String statement,Object parameter,RowBounds rowBounds){
try{
// 从全局Configuration中获取解析xxMapper.xml得到的SQL对象,里面保存的是SQL模板,也就是请求要用到的SQL
MappedStatement ms=configuration.getMappedStatement(statement);
// 通过SQL执行器去执行SQL
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();
}
}
- MyBatis 三种执行器类型 ExecutorType
- SIMPLE
> SimpleExecutor是一种常规执行器,每次执行都会创建一个statement,用完后关闭。- REUSE
> ReuseExecutor是可重用执行器,将statement存入map中,操作map中的statement而不会重复创建statement。- BATCH
> BatchExecutor是批处理型执行器,doUpdate预处理存储过程或批处理操作,doQuery提交并执行过程。
- 查询SQL executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
默认会进入CachingExecutor(缓存执行器)类中,此类也继承了执行器接口
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取绑定SQL 即:select XXX from XXX where XXX
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 根据请求的SQL和请求数据等信息,获取一个唯一的缓存key键
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
// 执行查询
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
- 执行SQL 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);
// 如果SQL类中设置的是使用缓存,并且返回值处理器为null
if(ms.isUseCache()&&resultHandler==null){
// 检查是否有带有OUT参数的储存过程(这里不是很理解)
ensureNoOutParams(ms,parameterObject,boundSql);
@SuppressWarnings("unchecked")
// 从缓存中获取缓存数据
List<E> list=(List<E>)tcm.getObject(cache,key);
if(list==null){
// 缓存数据为空,执行SQL查询
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);
}
- 执行SQL delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
@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{
// 从数据库中查询,再往后走就需要用到对JDBC支持的类了,alibaba的druid连接池就有此类接口
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;
}
- 返回值解析