@Test
void testMybatisFirstCache() {
List<code.entity.Test> select = testMapper.select(10L);
}
MapperProxy.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);
}
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
判断是不是Object里面的方法,然后走对应的逻辑
MapperProxy.PlainMethodInvoker.invoke方法
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}
-
sqlSession是SqlSessionTemplate。executoryType是SIMPLE,枚举类型有三种,分别是SIMPLE(简单,默认),BATCH(批量)和REUSE(重用)
-
args是10L
MapperMethod.execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
// 简化部分代码,由于本次只是select查询所以只看case SELECT部分代码
Object result;
switch (command.getType()) {
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 {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
}
return result;
}
- 判断sql执行类型,sql语句使用了select标签,就会被识别为select
<select id="select" resultType="code.entity.Test">
select * from test
</select>
- 该查询返回值是List,所以命中method.returnsMany()判断
MapperMethod.executeForMany方法
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);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
}
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
return result;
}
-
convertArgsToSqlCommandParam将args转换为map
-
判断是否有RowBounds(分页相关参数),可以用以下写法来触发hasRowBounds判断
List<Test> select(@Param("id")Long id,RowBounds rowBounds);
- 由于demo中没有传RowBounds所以不会触发,会走selectList全量查询
- 在执行完查询后,对result进行处理并返回
SqlSessionTemplate.SqlSessionInterceptor.invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
- 先获取sqlSession,如果有事务会保存在ThreadLocal里面重复使用,来保证事务的统一提交和回滚,如果没有则每次都会创建一个新的sqlSession
- 进入到try里面,执行完之后会判断当前是否处于事务中,如果没有则强制commit
- catch和finally里面也会有对应的会话判断操作,以及相应日志记录
执行method.invoke方法进入到DefaultSqlSession.selectList
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
dirty |= ms.isDirtySelect();
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
- 在这里会给上RowBounds.DEFAULT,对应的是0-Integer.MAX_VALUE
- 继续往下给上Executor.NO_RESULT_HANDLER,是个null
- executor.query代表从sqlSession进入到executor了
- 此处的executor是CachingExecutor mybatis二级缓存相关处理都在这里
CachingExecutor.query()
@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, 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);
}
- createCacheKey生成缓存的key
- 在query方法里面首先判断二级缓存是否生效,如果没生效就走delegate.query方法
- delegate是SimpleExecutor,但query方法是BaseExecutor里面的方法,两者是父子关系
SimpleExecutor(BaseExecutor).query
@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());
// 简化后代码,主要是为了关注查询时候的流程
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--;
}
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;
}
- 首先从localCache里面加载,如果能找到就处理下参数输出,然后返回
- 如果找不到就执行queryFromDatabase从数据库里面查询
- 实际查询是doQuery方法,是个抽象方法由SimpleExecutor实现
- 查询成功后将数据put到缓存里面,缓存底层是个HashMap
SimpleExecutor.doQuery
@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.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, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
-
newStatementHandler会去创建StatementHandler,RoutingStatementHandler里面有个delegate,对应的是实际StatemenHandler,在默认配置下是PreparedStatementHandler
-
进入prepareStatement方法第一步获取connection,第二步执行prepare方法,setStatementTimeout
-
parameterize对参数进行处理,例如下面这个sql#{id}就是会被识别并处理
select * from test where id = #{id}
- parameterize核发就是下面这段代码
typeHandler.setParameter(ps, i + 1, value, jdbcType);
- handler.query(stmt, resultHandler)进入实际数据库查询
PreparedStatementHandler.query
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
- PreparedStatement.execute就是jdbc层面执行数据库查询的操作
-
- resultSetHandler对结果集进行处理,有且仅有一个实现DefaultResultSetHandler
- 对于这块简而言之就是将结果集处理按ResultType或ResultMap处理成方法里面写的返回值,然后返回,暂不细看