2、Mybatis源码-Mybatis SQL执行流程
一,准备工作
mapper.xml
<mapper namespace="com.DemoMapper">
<select id="selectAll" parameterType="Map" resultType="Map">
select * from test
<if test="id != null and id != ''">
WHERE id = ${id}
</if>
</select>
</mapper>main方法
public static void main(String[] args) throws Exception {
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从调用者角度来讲,利用SqlSession对象与数据库打交道
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理去帮我们执行SQL
DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id","1");
System.out.println(mapper.selectAll(map));
sqlSession.close();
}1.1、流程图
可以看到MapperProxy调用invoke方法,进而调用MapperMethod的execute(),这些MapperMethod就是和你要执行的命令相关,比如执行select语句,则会通过SqlSession的select()方法(会根据类型不同,路由到不同的SqlSession中的方法执行),最终调用到Executor的query方法。Executor会再协调另外核心组件xxxHandler等进行处理。
二、sqlSession.getMapper()
通过代码sqlSession.getMapper(DemoMapper.class);获取到代理对象。
// DefaultSqlSession.getMapper();
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
// Configuration.getMapper();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry.getMapper();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//获取接口对应的泛型工厂类,前面注册mapper的时候调用MapperRegistry.addmapper()存入的
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//通过JDK动态代理获取代理类。
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}MapperProxyFactory代理工厂类,用于生成MapperProxy。由于MapperProxy是实现了InvocationHandler接口。执行代理操作,会调用mapperProxy.invoke()方法。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//执行代理操作,将调用mapperProxy.invoke()方法。
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);
}
}//MapperMethod.execute()方法
@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 (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//拿到MapperMethod,这个类是整个代理机制的核心类,对Sqlsession当中的操作进行了封装
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}三,MapperMethod
MapperMethod里面有2个类,通过构造函数传入。
//一个封装了SQL标签的类型的对象(insert update delete select)
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);
}execute方法,里面根据不同command.getType(),路由到恰当的SqlSession方法上做处理。
//根据不同Type,路由到恰当的SqlSession方法上做处理
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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:
//如果返回void并且参数有resultHandler(该对象可以对对返回的结果进行处理,然后再返回给用户)
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//如果返回多行结果则调用executeForMany(),里面调用的是sqlSession.selectList();
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//如果返回类型是map则调用executeForMap方法,里面调用的是sqlSession.selectMap();
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
//查询可以返回Cusror<T>类型的数据,类似于JDBC里的ResultSet类。
//当查询百万级的数据的时候,使用游标可以节省内存的消耗,不需要一次性取出所有数据,可以进行逐条处理或逐条取出部分批量处理。
//里面调用的sqlSession.selectCursor()
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;
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;
}这里我们查询的是单个对象,进入result = sqlSession.selectOne(command.getName(), param);默认使用的DefaultSqlSession类
四,DefaultSqlSession
SqlSession接口的默认实现,内部5个属性,通过构造方法传入。
private final Configuration configuration;
private final Executor executor;
//是否自动提交事务,默认false
private final boolean autoCommit;
//标记是否为脏数据,需要roolback的。
private boolean dirty;
//Cusror<T>类型的数据,类似于JDBC里的ResultSet类。
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
@Override
public <T> T selectOne(String statement) {
return this.selectOne(statement, null);
}
@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.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;
}
}
@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,里面包含了需要执行的SQL语句
MappedStatement ms = configuration.getMappedStatement(statement);
//装饰者模式
//如果开启二级缓存(前提是没在setting指定那个执行器),先使用CachingExecutor,再使用BaseExecutor
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();
}
}五,xxxExecutor
mybatis中的Executor执行器,Executor为接口,BaseExecutor是Executor默认实现(抽象类),通过装饰者模式,加强了BaseExecutor的功能。
| BaseExecutor | BaseExecutor是Executor默认实现(抽象类),实现了执行器的基本功能。 具体使用哪一个Executor则是可以在 mybatis 的 config.xml 中进行配置的。默认为SimpleExecutor <setting name="defaultExecutorType" value="SIMPLE"/> |
| BatchExecutor | 批量操作的执行器,批量执行后会返回一 个 |
| CachingExecutor | 启用二级缓存的执行器,使用的是TransactionalCacheManager。 |
| ReuseExecutor | 可重用的执行器,其定义了一个Map<String, Statement>,将执行的sql作为key,将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了。 |
| SimpleExecutor | 最简单的一个执行器。 |
5.1、CachingExecutor
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
//创建缓存的key,返回二级缓存的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 {
Cache cache = ms.getCache();
if (cache != null) {
//判断要不要清空缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
//使用TransactionalCacheManager来管理二级缓存
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
//如果二级缓存没有拿到,则去一级缓存获取,这里会走到BaseExecutor.query()
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);
}5.2、BaseExecutor
@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 {
//缓存为null,查询数据库
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;
//提前放一个占位符
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;
}5.3、SimpleExecutor
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//获取连接对象configuration
Configuration configuration = ms.getConfiguration();
//创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//处理参数。构成JDBC的Statement
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}六,xxxStatementHandler
负责操作 Statement 对象与数据库进行交流,结构如下:
6.1、PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行查询
ps.execute();
//处理结果集
return resultSetHandler.handleResultSets(ps);
}七、Mybatis执行SQL时序图
八、Mybatis四大对象
StatementHandler:处理sql语句预编译,设置参数等相关工作。
ParameterHandler:设置预编译参数。
ResultHandler:处理结果集。
Executor:它是一个执行器,真正进行java与数据库交互的对象。
九、代码流程
版权声明
非特殊说明,本文由Zender原创或收集发布,欢迎转载。
上一篇:1、Mybatis简单入门 下一篇:3、Mybatis源码-Mybatis配置解析
ZENDER






发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。