接上文在拿到SqlSession对象后,就可以使用此类提供的接口对数据库操作了。
从上图可以很直观的看出,SqlSession类提供的一些对数据库的crud操作,以及提交回滚事务。
@Test
void shouldSelectAllAuthors() {
try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
assertEquals(2, authors.size());
}
}
通过Mybatis的源码测试类shouldSelectAllAuthors()进行调试。
//来到此行代码
List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
- selectList
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
//获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
dirty |= ms.isDirtySelect();
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
- 获取MappedStatement
在Configuration类中
protected final Map<String, MappedStatement> mappedStatements = new StrictMap( "Mapped Statements collection") .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); 对象获取到 MappedStatement
上图是我们调用接口的Mapper类和xml配置sql文件,mappedStatements对象的是一个map类型,key是AhthorMapper类 selectAllAuthors方法全限定名:org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors value是一个MappedStatement对象,也就是一个select标签
接着往下走到excutor.query()。excutor是创建SqlSession流程的时候构建的CachingExcutor对象(详细可以看上文)。
- query()
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException {
//获取sql
BoundSql boundSql = ms.getBoundSql(parameterObject);
//创建CacheKey
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这里我们重点关注下mybatis二级缓存key是怎么生成的。
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
//0org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors
cacheKey.update(ms.getId());
//0
cacheKey.update(rowBounds.getOffset());
//2147483647 Integer.MAX_VALUE
cacheKey.update(rowBounds.getLimit());
//select * from author
cacheKey.update(boundSql.getSql());
//以下是参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
MetaObject metaObject = null;
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
if (metaObject == null) {
metaObject = configuration.newMetaObject(parameterObject);
}
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
//数据源
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
可已看出key是由方法全限定类名,分页偏移,sql,参数,数据构成。也就是说满足这些条件相同的sql查询才会走缓存。 最终的key值: 1456362818:1472358054:org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors:0:2147483647:select * from author:development
继续往下走到
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
//cache
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 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//flushCacheRequired==true 也回清除缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//从一级缓存获取数据
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//不走缓存,查询db获取数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
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;
}
关于缓存的一些操作,有缓存数据则拿到缓存数据,没有的话查询db拿数据
下面来看doQuery()方法,获取StatementHandler和Statement,然后执行查询。
@Override
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();
//获取RoutingStatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
boundSql);
//获取Statement
stmt = prepareStatement(handler, ms.getStatementLog());
//执行查询
return handler.query(stmt, ```
Statement
```);
} finally {
closeStatement(stmt);
}
}
执行 handler.query()
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//jdbc查询操作
ps.execute();
//处理结果集
return resultSetHandler.handleResultSets(ps);
}