一、sql执行流程
根据sql语句使用xml进行维护或者在注解上配置,sql语句执行的入口分为两种:
第一种,调用org.apache.ibatis.session.SqlSession的crud方法比如selectList/selectOne传递完整的语句id直接执行;
第二种,先调用SqlSession的getMapper()方法得到mapper接口的一个实现,然后调用具体的方法。除非早期,现在实际开发中,我们一般采用这种方式。
我们看下第一种方式执行selectOne方法
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
}
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.<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执行
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
默认是交给SimpleExectutor执行器进行执行
SQL语句执行方式二 SqlSession.getMapper实现
从configuration中根据type获取mapper的代理对象
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
MapperRegistry又将创建代理的任务委托给MapperProxyFactory,MapperProxyFactory首先为Mapper接口创建了一个实现了InvocationHandler方法调用处理器接口的代理类MapperProxy,并实现invoke接口(其中为mapper各方法执行sql的具体逻辑),最后才调用JDK的
java.lang.reflect.Proxy为Mapper接口创建动态代理类并返回
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
动态sql
只要mybatis的的crud语句中包含了、等标签或者
之
后
,
就
已
经
算
是
动
态
s
q
l
了
,
所
以
只
要
在
m
y
b
a
t
i
s
加
载
m
a
p
p
e
r
文
件
期
间
被
解
析
为
非
S
t
a
t
i
c
S
q
l
S
o
u
r
c
e
,
就
会
被
当
做
动
态
s
q
l
处
理
,
在
执
行
s
e
l
e
c
t
X
X
X
或
者
u
p
d
a
t
e
/
i
n
s
e
r
t
/
d
e
l
e
t
e
期
间
,
就
会
调
用
对
应
的
S
q
l
N
o
d
e
接
口
和
T
e
x
t
S
q
l
N
o
d
e
.
i
s
D
y
n
a
m
i
c
(
)
处
理
各
自
的
标
签
以
及
{}之后,就已经算是动态sql了,所以只要在mybatis加载mapper文件期间被解析为非StaticSqlSource,就会被当做动态sql处理,在执行selectXXX或者update/insert/delete期间,就会调用对应的SqlNode接口和TextSqlNode.isDynamic()处理各自的标签以及
之后,就已经算是动态sql了,所以只要在mybatis加载mapper文件期间被解析为非StaticSqlSource,就会被当做动态sql处理,在执行selectXXX或者update/insert/delete期间,就会调用对应的SqlNode接口和TextSqlNode.isDynamic()处理各自的标签以及{},并最终将每个sql片段处理到StaticTextSqlNode并生成最终的参数化静态SQL语句为止。所以,可以说,在绝大部分非PK查询的情况下,我们都是在使用动态SQL。
Mybatis一级与二级缓存
一级缓存
mybatis的一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中用一个HashMap来存储数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象是无法访问的
流程:
第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来,key是查询的完成sql语句,里面有参数,不同的参数是不同的key
第二次执行select会从缓存中查询数据,如果select相同并且参数一样,那么就能从缓存汇总返回数据,不用去查数据库了,从而提高效率
二级缓存
二级缓存是mapper级别的缓存,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个缓存区域。
springboot项目中使用的配置
在启动类中加上@EnableCaching注解
在需要缓存的mapper中加上@CacheNamespace(implementation = MybatisRedisCache.class)这个注解和二级缓存的类就可以了
对于一级缓存,commit/rollback都会清空一级缓存。
对于二级缓存,DML操作或者显示设置语句层面的flushCache属性都会使得二级缓存失效。
在二级缓存容器的具体回收策略实现上,有下列几种:
LRU – 最近最少使用的:移除最长时间不被使用的对象,也是默认的选项,其实现类是org.apache.ibatis.cache.decorators.LruCache。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们,其实现类是org.apache.ibatis.cache.decorators.FifoCache。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象,其实现类是org.apache.ibatis.cache.decorators.SoftCache。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象,其实现类是org.apache.ibatis.cache.decorators.WeakCache。
三、小结
mybatis在执行期间,主要有四大核心接口对象:
1、执行器Executor,执行器负责整个SQL执行过程的总体控制。
2、参数处理器ParameterHandler,参数处理器负责PreparedStatement入参的具体设置。
3、语句处理器StatementHandler,语句处理器负责和JDBC层具体交互,包括prepare语句,执行语句,以及调用ParameterHandler.parameterize()设置参数。
4、结果集处理器ResultSetHandler,结果处理器负责将JDBC查询结果映射到java对象。
执行器Executor
我们在应用层通过sqlSession执行的各类selectXXX和增删改操作在做了动态sql和参数相关的封装处理后,都被委托给具体的执行器去执行,包括一、二级缓存的管理,事务的具体管理,Statement和具体JDBC层面优化的实现等等。所以执行器比较像是sqlSession下的各个策略工厂实现,用户通过配置决定使用哪个策略工厂。
拦截器Interceptor
public interface Interceptor {
//执行代理类方法
Object intercept(Invocation invocation) throws Throwable;
// 用于创建代理对象
Object plugin(Object target);
// 插件自定义属性
void setProperties(Properties properties);
}
mybatis提供了为插件配置提供了两个注解:org.apache.ibatis.plugin.Signature和org.apache.ibatis.plugin.Intercepts。