mybatis-3.4.6.release.
图1
Executor是个接口,具体实现是在BaseExecutor和4个子类中。
1.BatchExecutor
BatchExecutor与其它的区别是update方法中,使用的是StatementHandler的batch方法,如下List-1
List-1
public class BatchExecutor extends BaseExecutor {
...
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
// handler.parameterize(stmt);
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
...
2.ReuseExecutor
ReuseExecutor与其它executor的区别是重用jdbc的Statement,有个map属性,ReuseExecutor的doUpdate、doQuery中,调用prepareStatement时,如下List-2,会根据sql来判断是否已经存在对应的Statement,如果map中已经有了则直接使用,如果不存在,则通过PrepareStatementHandler.prepare方法获取对Statement,之后放入到map中。
List-2
public class ReuseExecutor extends BaseExecutor {
...
private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
...
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
private Statement getStatement(String s) {
return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
3.SimpleExecutor
SimpleExecutor与ReuseExecutor和BatchExecutor不同,即没有重用Statement,在update时也没有使用StatementHandler的batch方法,而是用了update方法。
4.CachingExecutor
CachingExecutor有点特殊,使用了Delete模式,此外用到了Cache,如下List-3
List-3
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);
}
...
来看TransactionalCacheManager,如下List-4,通过Cache这个key,得到TransactionalCache,当调用getObject方法时,间接调用TransactionalCache.getObject.
List-4
public class TransactionalCacheManager {
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
List-5
public class TransactionalCache implements Cache {
private final Cache delegate;
private boolean clearOnCommit;
private final Map<Object, Object> entriesToAddOnCommit;
private final Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<Object, Object>();
this.entriesMissedInCache = new HashSet<Object>();
}
@Override
public Object getObject(Object key) {
// issue #116
Object object = delegate.getObject(key);
if (object == null) {
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}
List-5中,getObject方法中,从委托的cache中getObject如果不是null则返回,不为null则还需要更新统计。
所以mybatis中可以使用二级缓存就是通过这个Cache来实现的。
在没有特殊设置的情况下,mybatis中,如下List-6,默认使用的executor是SimpleExecutor,StatementHandler是RoutingStatementHandler,内部委托给PrepareStatementHandler。
List-6
<select id="findByUsername" resultType="Person" parameterType="Person">
select * from person where username=#{username};
</select>
如下List-7,没有参数设置,默认情况下使用的还是SimpleExecutor、RoutingStatementHandler——内部委托给PrepareStatementHandler.
List-7
<select id="findAll" resultType="Person">
select * from person
</select>