MyBatis核心源码深度剖析SQL执行过程
Posted 赵广陆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis核心源码深度剖析SQL执行过程相关的知识,希望对你有一定的参考价值。
目录
1 SQL语句的执行过程介绍
MyBatis核心执行组件:
2 SQL执行的入口分析
2.1 为Mapper接口创建代理对象
// 方式1:
User user = session.selectOne("com.oldlu.dao.UserMapper.findUserById", 101);
// 方式2:
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> userList = mapper.findAll();
2.2 执行代理逻辑
方式1入口分析:
session是DefaultSqlSession类型的,因为sqlSessionFactory默认生成的SqlSession是
DefaultSqlSession类型。
selectOne()会调用selectList()。
// DefaultSqlSession类
public <E> List<E> selectList(String statement, Object parameter, RowBounds
rowBounds)
try
MappedStatement ms = configuration.getMappedStatement(statement);
// CURD操作是交给Excetor去处理的
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();
方式2入口分析:
获取代理对象:
//DefaultSqlSession类 ====================>
@Override
public <T> T getMapper(Class<T> type)
return configuration.getMapper(type, this);
// Configuration类 ====================>
public <T> T getMapper(Class<T> type, SqlSession sqlSession)
return mapperRegistry.getMapper(type, sqlSession);
//MapperRegistry ----> apperProxyFactory.newInstance ====================>
public <T> T getMapper(Class<T> type, SqlSession sqlSession)
//从缓存中获取该Mapper接口的代理工厂对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>)
knownMappers.get(type);
//如果该Mapper接口没有注册过,则抛异常
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the
MapperRegistry.");
try
//【使用代理工厂创建Mapper接口的代理对象】
return mapperProxyFactory.newInstance(sqlSession);
catch (Exception e)
throw new BindingException("Error getting mapper instance. Cause: " + e,
e);
//MapperProxyFactory --->此时生成代理对象 ====================>
protected T newInstance(MapperProxy<T> mapperProxy)
//Mybatis底层是调用JDK的Proxy类来创建代理实例
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new
Class[] mapperInterface , mapperProxy);
public T newInstance(SqlSession sqlSession)
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession,
mapperInterface, methodCache);
return newInstance(mapperProxy);
代理对象执行逻辑:
//MapperProxy ====================>
/**代理对象执行的方法,代理以后,所有Mapper的方法调用时,都会调用这个invoke方法*/
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable
try
if (Object.class.equals(method.getDeclaringClass()))
//如果是Object方法,则调用方法本身
return method.invoke(this, args);
else
//调用接口方法:根据被调用接口的Method对象,从缓存中获取MapperMethodInvoker对象
//apper接口中的每一个方法都对应一个MapperMethodInvoker对象,而MapperMethodInvoker
对象里面的MapperMethod保存着对应的SQL信息和返回类型以完成SQL调用 ...
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
catch (Throwable t)
throw ExceptionUtil.unwrapThrowable(t);
/**
获取缓存中MapperMethodInvoker,如果没有则创建一个,而MapperMethodInvoker内部封装这一
个MethodHandler
*/
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable
try
return methodCache.computeIfAbsent(method, m ->
if (m.isDefault())
//如果调用接口的是默认方法(default方法)
try
if (privateLookupInMethod == null)
return new
DefaultMethodInvoker(getMethodHandleJava8(method));
else
return new
DefaultMethodInvoker(getMethodHandleJava9(method));
catch (IllegalAccessException | InstantiationException |
InvocationTargetException
| NoSuchMethodException e)
throw new RuntimeException(e);
else
//如果调用的普通方法(非default方法),则创建一个PlainMethodInvoker并放
入缓存,其中MapperMethod保存对应接口方法的SQL以及入参和出参的数据类型等信息
return new PlainMethodInvoker(new MapperMethod(mapperInterface,
method, sqlSession.getConfiguration()));
);
catch (RuntimeException re)
Throwable cause = re.getCause();
throw cause == null ? re : cause;
// MapperProxy内部类: PainMethodInvoker ====================>
// 当cacheInvoker返回了PalinMethodInvoker实例之后,紧接着调用了这个实例的
PlainMethodInvoker:invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession
sqlSession) throws Throwable
//Mybatis实现接口方法的核心: MapperMethod::execute方法:
return mapperMethod.execute(sqlSession, args);
// MapperMethod ====================>
public Object execute(SqlSession sqlSession, Object[] args)
Object result;
switch (command.getType())
case INSERT:
// 将args进行解析,如果是多个参数则,则根据@Param注解指定名称将参数转换为Map,
如果是封装实体则不转换
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())
result = executeForMany(sqlSession, args);
else if (method.returnsMap())
result = executeForMap(sqlSession, args);
else if (method.returnsCursor())
result = executeForCursor(sqlSession, args);
else
//解析参数,因为SqlSession::selectOne方法参数只能传入一个,但是我们
Mapper中可能传入多个参数,
//有可能是通过@Param注解指定参数名,所以这里需要将Mapper接口方法中的多个参
数转化为一个ParamMap,
//也就是说如果是传入的单个封装实体,那么直接返回出来;如果传入的是多个参数,
实际上都转换成了Map
Object param = method.convertArgsToSqlCommandParam(args);
//可以看到动态代理最后还是使用SqlSession操作数据库的
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null ||
!method.getReturnType().equals(result.getClass())))
result = Optional.ofNullable(result);
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;
// 此时我们发现: 回到了sqlsession中
private <E> Object executeForMany(SqlSession sqlSession, Object[] args)
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds())
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
else
result = sqlSession.selectList(command.getName(), param);
// ...
return result;
3 查询语句的执行过程分析
3.1 selectOne方法分析
// DefaultSqlSession类 ===============>
// selectOne
@Override
public <T> T selectOne(String statement, Object parameter)
// //selectOne()会调用selectList()。
List<T> list = this.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;
// selectList
public <E> List<E> selectList(String statement, Object parameter, RowBounds
rowBounds)
try
MappedStatement ms = configuration.getMappedStatement(statement);
// CURD操作是交给Excetor去处理的
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();
3.2 sql获取
// CachingExecutor ===============>
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds
rowBounds, ResultHandler resultHandler) throws SQLException
// 获取绑定的sql命令,比如"SELECT * FROM xxx"
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, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null)
list = delegate.query(ms, parameterObject, rowBounds,
resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
return list;
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key,
boundSql);
//真正执行query操作的是SimplyExecutor代理来完成的,SimplyExecutor的父类BaseExecutor的
query方法中:
// BaseExecutor类:SimplyExecutor的父类 =================>
@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以上是关于MyBatis核心源码深度剖析SQL执行过程的主要内容,如果未能解决你的问题,请参考以下文章