从之前的文章,我们知道了其实mapper真正执行的方法就下面的最后两行。(以下所有的分析都基于一次mybatis的一次select查询。
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);
}
//获取MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行查询并且返回结果
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
//methodCache其实就是一个缓存,将方法与mapperMethod作为一组键值对进行缓存
MapperMethod mapperMethod = methodCache.get(method);
//若缓存中没找到则生成一个,在把生成的mapperMethod加入缓存
if (mapperMethod == null) {
//生成mapperMethod的方法
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
这里解释一下mapperMethod有什么作用,首先看一下MapperMethod类的具体参数
public class MapperMethod {
//记录方法是什么类型的方法(UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;)
private final SqlCommand command;
//记录方法的具体情况(比如说返回一个还是多个,方法返回类型,方法的参数是啥等信息)
private final MethodSignature method;
//MapperMethod的构造函数
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
}
//执行方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//通过command的类型去找实际需要执行的方法,不一个个分析了,只以select的executeForMany为例子
//其他都是差不了多少的
switch (command.getType()) {
//若是插入类型走这里
case INSERT: {
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 {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
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;
}
//查询方法
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
//
Object param = method.convertArgsToSqlCommandParam(args);
//是否需要分页
if (method.hasRowBounds()) {
//拿到rowBounds
RowBounds rowBounds = method.extractRowBounds(args);
//查询
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
//查询,进入这条
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
//实际还是走的有RowBounds 的语句,只是给了默认值,默认值是拿到Integer的最大值
@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) {
try {
//从configuration获取MappedStatement (在初始化的时候就已经放到缓存中了,这里只是获取一下)
MappedStatement ms = configuration.getMappedStatement(statement);
//通过executor来查询
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默认开始Cache,所以我们的Executor是CachingExecutor。但是我们没在mapper.xml中配置Cache的属性,所以最终是没缓存功能的。通过装饰器模式来增加了Executor的功能
//这两个方法是CachingExecutor里面的
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//生成BoundSql,里面存放着sql语句
BoundSql boundSql = ms.getBoundSql(parameterObject);
//生成缓存用的key
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 {
//因为mapper中没有配置cache,所以这里的cahce是没有的
Cache cache = ms.getCache();
//cache为空,所以不会走这里面的逻辑
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//到来这里,通过delegate来执行query(因为CachingExecutor是一个装饰类,delegate是原始类(在这里是SimpleExecutor))
return delegate.<E> 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++;
//这里的localCache是一级缓存,是在BaseExecutor中的
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--;
}
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;
//先从key中放个占位的值
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;
}
@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,对Statement进行处理
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//生成statement
stmt = prepareStatement(handler, ms.getStatementLog());
//进行访问
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
//这里是prepareStatement函数,用来生成访问数据库需要的Statement对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//如果是debug状态则对sqlSession进行代理,因为要打印log
Connection connection = getConnection(statementLog);
//
stmt = handler.prepare(connection, transaction.getTimeout());
//对参数进行处理
handler.parameterize(stmt);
return stmt;
}
我们进handler的prepare方法看看
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//初始化statement
statement = instantiateStatement(connection);
//设置超时
setStatementTimeout(statement, transactionTimeout);
//设置fetchSize(mysql不起作用,不支持这个)
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);
}
}
之后得到了Statement对象后,要去访问数据库了。(上面的return handler.query(stmt, resultHandler);)调用的是下面的query方法
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}1234
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//将Statement强转成PreparedStatement
PreparedStatement ps = (PreparedStatement) statement;
//对数据库进行查询(对数据库进行查询是jdbc做的事情,mybatis也只是对jdbc进行了包装)
ps.execute();
//对返回结果进行处理
return resultSetHandler.<E> handleResultSets(ps);
}
到这里,我们就进行了一次对数据库的访问,并拿到了数据。数据此时在Statement的ResultSet里面,
如果你熟悉jdbc的话,一定不陌生下面的代码,我们拿到resultSet后的处理方式如下。
ResultSet resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
String string = resultSet.getString(1);
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_43842093/article/details/122441746
内容来源于网络,如有侵权,请联系作者删除!