SimpleExecutor
SimpleExecutor继承自BaseExecutor类,是最简单的Executor实现类,增删改查功能都依赖其持有的StatementHandler实例来完成。
doUpdate
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//构建Statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
//调用StatementHandler的具体实例完成更新操作
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
doQuery
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对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//构建Statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
//调用StatementHandler的具体实例完成查询操作
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQueryCursor
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
//创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
//构建Statement对象
Statement stmt = prepareStatement(handler, ms.getStatementLog());
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}
doFlushStatements
不提供批处理功能,直接返回空集合。
public List<BatchResult> doFlushStatements(boolean isRollback) {
return Collections.emptyList();
}
prepareStatement
用于构建Statement对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
//填入实参
handler.parameterize(stmt);
return stmt;
}
ReuseExecutor
在JDBC编程中,重用Statement对象是常用的一种优化手段,该优化手段可以减少SQL预编译的开销以及创建和销毁Statement对象的开销,从而提高性能。
ReuseExecutor就是提供了Statement重用功能的一种实现,他通过一个Map来缓存使用过的Statement对象。
属性
Statement缓存,其中key为带?占位符的sql语句
private final Map<String, Statement> statementMap = new HashMap<>();
prepareStatement
创建Statement对象
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {//如果缓存命中,则复用缓存中的Statement对象
stmt = getStatement(sql);
//设置超时时间
applyTransactionTimeout(stmt);
} else {
//获取连接对象
Connection connection = getConnection(statementLog);
//创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
//将Statement保存在缓存中
putStatement(sql, stmt);
}
//用实参填充sql
handler.parameterize(stmt);
return stmt;
}
hasStatementFor
判断sql是否命中缓存
private boolean hasStatementFor(String sql) {
try {
//从缓存中获取对应的Statement对象
Statement statement = statementMap.get(sql);
return statement != null && !statement.getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
getStatement
获取缓存对象
private Statement getStatement(String s) {
return statementMap.get(s);
}
putStatement
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
doUpdate
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
doQuery
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
doQueryCursor
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.queryCursor(stmt);
}
CachingExecutor
与二级缓存有关的配置
1、全局配置文件的cacheEnabled属性,是二级缓存配置的总开关
<!-- 全局配置参数,需要时再设置 -->
<settings>
<!-- 开启二级缓存 默认值为true -->
<setting name="cacheEnabled" value="true"/>
</settings>
2、mapper.xml文件中配置<cache>
或<cache-ref>
节点
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。
这里采用的是LRU, 移除最长时间不用的对象
flushInterval:刷新间隔时间,单位为毫秒,如果你不配置它,那么当SQL被执行的时候 才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
-->
<cache eviction="回收策略" type="缓存类"/>
3、查询节点中配置useCache属性,为true表示查询结果需要保存到二级缓存中
二级缓存的生命周期和作用域
二级缓存的作用域是一个namespace,在这个namespace中操作的所有SqlSession都可以共享这个二级缓存。
二级缓存的生命周期是整个应用级别。
二级缓存的结构
TransactionalCache
TransactionalCache是CachingExecutor依赖的组件,TransactionalCache实现了Cache接口,作用是保存某个sqlSession的某个事务中需要向某个二级缓存中添加的缓存数据,换句话说就是:某些缓存数据会先保存在这里,然后再提交到二级缓存中,他是二级缓存防止脏读的关键。
属性
private static final Log log = LogFactory.getLog(TransactionalCache.class);
//被装饰的二级缓存对象
private final Cache delegate;
//当clearOnCommit为true时,在事务提交的时候会将delegate清空,此时表示当前缓存不可查询
private boolean clearOnCommit;
//事务提交时需要被添加到二级缓存的数据
private final Map<Object, Object> entriesToAddOnCommit;
//缓存未命中的CacheKey对象,防止缓存击穿
private final Set<Object> entriesMissedInCache;
构造函数
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<>();
this.entriesMissedInCache = new HashSet<>();
}
getObject
public Object getObject(Object key) {
// issue #116
//查询二级缓存中是否存在
Object object = delegate.getObject(key);
if (object == null) {
//如果二级缓存中不存在则将其key添加到entriesMissedInCache中
entriesMissedInCache.add(key);
}
//clearOnCommit为true表示在提交事务后会情况二级缓存,所以这里会返回一个null
if (clearOnCommit) {
return null;
} else {
return object;
}
}
putObject
将数据暂存到entriesToAddOnCommit 中,等提交事务的时候再将其保存到二级缓存中
@Override
public void putObject(Object key, Object object) {
entriesToAddOnCommit.put(key, object);
}
commit
//提交事务
public void commit() {
if (clearOnCommit) {
//如果需要提交则将二级缓存清除
delegate.clear();
}
//将entriesToAddOnCommit中暂存的数据写入二级缓存
flushPendingEntries();
reset();
}
rollback
回滚事务
public void rollback() {
unlockMissedEntries();
reset();
}
flushPendingEntries
将entriesToAddOnCommit中暂存的数据写入二级缓存
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
//没有命中的key写入二级缓存的目的是什么
delegate.putObject(entry, null);
}
}
}
unlockMissedEntries
在真实缓存中把这部分key和对应的value删除,从而保证数据的一致性
private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
try {
delegate.removeObject(entry);
} catch (Exception e) {
log.warn("Unexpected exception while notifiying a rollback to the cache adapter. "
+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);
}
}
}
TransactionalCacheManager
TransactionalCacheManager通过一个transactionalCaches属性来管理多个TransactionalCache对象,transactionalCaches属性定义如下:
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
其中Map的key为二级缓存对象,value为对应的TransactionalCache对象
TransactionalCacheManager和TransactionalCache的关系如下:
CachingExecutor的代码如下
/**
* @author Clinton Begin
* @author Eduardo Macarron
* 二级缓存执行器
*/
public class CachingExecutor implements Executor {
//持有的执行器
private final Executor delegate;
//事务性缓存管理器
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
@Override
public Transaction getTransaction() {
return delegate.getTransaction();
}
//关闭执行器
@Override
public void close(boolean forceRollback) {
try {
// issues #499, #524 and #573
if (forceRollback) {
tcm.rollback();
} else {
tcm.commit();
}
} finally {
//关闭执行器
delegate.close(forceRollback);
}
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
//是否刷新缓存
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
flushCacheIfRequired(ms);
return delegate.queryCursor(ms, parameter, rowBounds);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
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 {
//调用 MappedStatement#getCache() 方法,获得Cache对象,即当前MappedStatement对象所对应的二级缓存。
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);
}
@Override
public List<BatchResult> flushStatements() throws SQLException {
return delegate.flushStatements();
}
@Override
public void commit(boolean required) throws SQLException {
delegate.commit(required);
tcm.commit();
}
@Override
public void rollback(boolean required) throws SQLException {
try {
delegate.rollback(required);
} finally {
if (required) {
tcm.rollback();
}
}
}
private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
if (ms.getStatementType() == StatementType.CALLABLE) {
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
if (parameterMapping.getMode() != ParameterMode.IN) {
throw new ExecutorException("Caching stored procedures with OUT params is not supported. Please configure useCache=false in " + ms.getId() + " statement.");
}
}
}
}
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
}
@Override
public boolean isCached(MappedStatement ms, CacheKey key) {
return delegate.isCached(ms, key);
}
@Override
public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
delegate.deferLoad(ms, resultObject, property, key, targetType);
}
@Override
public void clearLocalCache() {
//清空一级缓存
delegate.clearLocalCache();
}
//通过@Options(flushCache = Options.FlushCachePolicy.TRUE) 或 <select flushCache="true"> 方式,开启需要清空缓存
//此时清空的只是当前事务中查询数据产生的缓存。而真正的清空,是在事务的提交时完成的
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
@Override
public void setExecutorWrapper(Executor executor) {
throw new UnsupportedOperationException("This method should not be called");
}
}
查询流程如下: