上篇我感觉逻辑有点乱,开篇先理理主要逻辑:
1.加载配置,构造Configuration对象
2.创建DefaultSqlSessionFactory工厂
3.通过DefaultSqlSessionFactory.openSession()创建DefaultSqlSession对象,同时会创建Executor,设置到SqlSession属性中。
4.通过sqlSession.getMapper()方法,返回一个mapper代理对象
5.通过mapper代理对象,执行具体查询
mybatis主要有四大神器:Executor,StatmentHandler,ParameterHandler,ResultSetHandler
上篇已经分析了Executor,StatmentHandler,ParameterHandler的源码,但是感觉不是连贯,下面先把逻辑连一下:
上篇说了Executor是在创建SqlSession的时候就会创建Executor(执行器)。
那其他几个对象是在什么时候调用的呢?具体是在什么去查的数据库呢?
具体开端就在对mapper的代理里面:
public class MapperProxy<T> implements InvocationHandler
这个类就实现了对mapper的增强逻辑
@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);
//在代理mapper具体调用某个方法的时候
return mapperMethod.execute(sqlSession, args);
}
////缓存 Map<Method, MapperMethod> methodCache
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
下面就是主要的mapperMethod.execute(SqlSession,args)的源码
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//判断是什么数据库操作
switch (command.getType()) {
case INSERT: {
// 处理方法的参数名 一个的时候不用匹配 当有多个的时候需要用@param申明的name匹配
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());
}
return result;
}
到这,流程就是代理对象在具体执行的时候,是调用的sqlSession提供的方法
下面接着看,看看sqlSession.selectOne();
@Override
public <T> T selectOne(String statement, Object parameter) {
// 也是调用的selectList方法,判断结果为1才返回,大于1就报错
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;
}
}
//主要看selectList方法
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//方法名和数据库操作对象关系以及sql关联的sql标签信息(比如resultmap,sql,statmentType等熟悉)
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
}
到这 ,可以看到sqlSession在执行具体逻辑的时候调用的是执行器executor
接着看executor.query()
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
if (queryStack == 0 && ms.isFlushCacheRequired()) {
//清本地缓存
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {//callable
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {//主要的逻辑看这个方法
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
deferredLoads.clear();
//如果本地缓存的作用域是statment,每次都会请缓存
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 必须你的sql sql参数 statmentid都一样 生成的key才会一样
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//下面主要看这个方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
//mybatis的一级缓存,缓存查询结果 ,默认开启了的
localCache.putObject(key, list);
return list;
}
这个doQuery方法 具体就是executor中定义的方法,接着就是上篇的 “3.创建StatmentHandler对象…”
后面的大致流程就是:executor调用的是StatmentHandler对象(会创建数据库对象PrepareStatment),设置参数调用parameterHandler,结果映射调用ResultSetHandler对象,主要逻辑就完了。