mybatis执行器 - 969251639/study GitHub Wiki
首先,看下mybatis的查询是怎么操作流程的
在DefaultSqlSession方法中早到selectOne方法
@Override
public <T> T selectOne(String statement) {
return this.<T>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.<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;
}
}
可以看到selectOne和selectList是一样的,selectOne只是返回了List中第一个元素(也只能有一个,否则抛出TooManyResultsException异常)
@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 {
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
mybatis在查询之前需要获取Mapper,因为具体的sql定义是被封装到Mapper中的也就是MappedStatement对象
public final class MappedStatement {
private String resource;
private Configuration configuration;
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
...
}
可以看到MappedStatement中的属性就是映射mapper xml中配置项
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="test.Blog" >
<select id="findById" resultType="test.Blog" parameterType="int" >
SELECT * FROM blog WHERE id=#{id}
</select>
<select id="add" parameterType="test.Blog" >
INSERT INTO blog(name, type, create_time) VALUES('myblog', 1, now())
</select>
</mapper>
注:mapper在mybatis启动时就已经创建好了的,基本都是缓存中进行操作
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
获取到MappedStatement 之后就基本知道sql的样貌了,包括它的参数信息,返回信息等,然后调用执行器进行操作
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
默认的执行器是SimpleExecutor,执行器是在打开会话时创建的
final Executor executor = configuration.newExecutor(tx, execType);//获取一个sql执行器
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;
}
另外有一个特殊的执行器是CachingExecutor,也就是mybtais的二级缓存,它是对三个基础执行器(BATCH,REUSE,SIMPLE)的包装,主要用于缓存处理,最后将其加入到plugin链中,用于mybatis的扩展。
SIMPLE就是一个sql查询器,封装了最基础的CRUD
BATCH顾名思义,主要用来操作批量操作,内部将sql收集到list中,然后一起发送给数据库处理
最终,获取到执行器之后就可以做真正的数据库操作了