Mybatis提供了如下特性
- 使用连接池对连接进行管理。
- SQL和代码分离,集中管理。
- 参数映射和动态SQL。
- 结果集映射。
- 缓存管理。
- 重复SQL提取
<sql>
- 插件机制。
执行过程
执行过程图解,图片来自网络
执行过程文字描述
涉及的代码展示版本为Mybatis3.5.4
解析配置文件得到Configuration
- 解析配置文件,每个sql语句标签对应一个MappedStatement
- 每个namespace对应一个MappProxyFactory,存入MapperRestry中的knownMappers,key值为Mapper接口类型,value值为MapperProxyFactory。
创建sqlSessionFactory
- 根据传入的Configuration获得defaultSessionFactory
创建sqlSession,执行sqlSessionFactory.openSession()得到sqlSession。
- 创建事务
- 根据策略创建Executor(simpleExecutor,ReuseExecutor,BatchExecutor),如果二级缓存开启,则用CacheingExecutor装饰上述的Executor;然后添加Executor插件。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
- 创建defaultSqlSession并返回
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
通过sqlSession.getMapper获取Mapper代理对象
- 根据传入的Mapper.class从第一步初始化knownMappers中获取对应类型的MapperProxyFactoy代理工厂对象。
- MapperProxyFactory调用newProxyInstance获取MapperProxy代理对象生成的Mapper对象。
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
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中的invoke方式是调用Mapper接口方法时调用的真正方法逻辑。
执行代理对象Invoke方法
- MapperProxy会把sqlseesion作为构造参数传入,执行invok时会从sqlSession中获取Executor执行。
- 执行Executor方法,判断是否有开启二级缓存,如果有从二级缓存获取数据,如果没有数据则从Executor中获取一级缓存。一级缓存是是维护在Executor对象中的localCache,而Executor是和sqlSession一一对应,所以一级缓存是sqlSession级别的;二级缓存时namespace级别的。
- 二级缓存。二级缓存和事务绑定,事务提交后缓存才会生效,下次才会从缓存取。为什么要跟事务绑定,是为了防止回滚后数据不一致。一级缓存回滚会话会结束。
@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);
}
- 一级缓存
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;
}
以下描述摘自Mybatis参考稳定
本地缓存
Mybatis 使用到了两种缓存:本地缓存(local cache)和二级缓存(second level cache)。
每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询结果都会被保存在本地缓存中,所以,当再次执行参数相同的相同查询时,就不需要实际查询数据库了。本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空。
默认情况下,本地缓存数据的生命周期等同于整个 session 的周期。由于缓存会被用来解决循环引用问题和加快重复嵌套查询的速度,所以无法将其完全禁用。但是你可以通过设置 localCacheScope=STATEMENT 来只在语句执行时使用缓存。
注意,如果 localCacheScope 被设置为 SESSION,对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用。对返回的对象(例如 list)做出的任何修改将会影响本地缓存的内容,进而将会影响到在本次 session 中从缓存返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。
StatementHandler执行
- 根据配置选择一个statmentHandler处理器(CallableStatementHandler、PreparedStatementHandler、SimpleStatementHandler)。
- 增加StatementHandler插件
- 执行StatementHandler对应实现方法。