SqlSession
我们跟着上一章节的流程来看下这第一个组件。
public interface SqlSession extends Closeable {
//查询
<T> T selectOne(String var1);
<E> List<E> selectList(String var1, Object var2);
//插入
int insert(String var1, Object var2);
//修改
int update(String var1);
//删除
int delete(String var1);
//提交
void commit();
//回滚
void rollback();
List<BatchResult> flushStatements();
//关闭
void close();
void clearCache();
//获取配置
Configuration getConfiguration();
//获取mapper
<T> T getMapper(Class<T> var1);
//获取连接
Connection getConnection();
}
从sqlsession的接口来看,包括jdbc connection的大部分功能 包括增删改查,提交回滚,以及获取一些配置信息的接口。我们可以来看看sqlSession接口的实现类DefaultSqlSession。
public class DefaultSqlSession implements SqlSession {
//包括很多environment 各种组件注册器以及mappedSatement等组件列表,后续我们会讲
private final Configuration configuration;
//执行器
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
//从configuration获取mappedStatement
MappedStatement ms = this.configuration.getMappedStatement(statement);
//通过executor来执行query方法
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
从上面的DefaultSqlSession里的属性可以看到Configuration Executor 这两个组件,我们看selectList里面俩方法一个通过configuration获取mappedStatement,一个调用executor执行对应的query方法。我们一个一个来看。
configuration
public class Configuration {
//与数据源DataSource有关
protected Environment environment;
//resultSet类型 与resultSet有关
protected ResultSetType defaultResultSetType;
//ExecutorType 类型 与ExecutorType 有关
protected ExecutorType defaultExecutorType;
//MapperRegistry 注册器
protected final MapperRegistry mapperRegistry;
protected final InterceptorChain interceptorChain;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final TypeAliasRegistry typeAliasRegistry;
//MappedStatement map结构
protected final Map<String, MappedStatement> mappedStatements;
protected final Map<String, Cache> caches;
//<resultMap>标签
protected final Map<String, ResultMap> resultMaps;
protected final Map<String, ParameterMap> parameterMaps;
protected final Map<String, KeyGenerator> keyGenerators;
}
首先,在mybatis初始化过程中,会加载 mybatis-config.xml 配置文件、映射配置文件以及
Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到
Configuration 对象中 例如,mapper.xml里的<resultMap>节点(即 ResultSet 的映射规则)
会被解析成 ResultMap 对象。而MappedStatement主要保存了sql的信息以及参数返回对象。
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;
//sql元数据
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 String[] resultSets;
}
仔细梳理一下 sqlSource 是 sql语句,ParameterMap 是参数,ResultSetType 是返回类型,StatementType 是查询类型(select update delete),是不是全是xml文件里你写的配置?这样往后执行时所有信息一览无遗。
Environment
前面configuration里的属性environment在这里可以简单看一看,就是最基本的数据源信息
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
}
Executor
前面讲完Environment,现在就可以看Executor了。看名字就知道是执行器了。看看接口吧
public interface Executor {
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
}
随便挑了几个方法,乍一看跟外层的sqlSession没区别啊。别急我们接着看。
这里用到了模板方法的设计模式,spring, aqs里面源代码也有类似的,对代码完成了更好的复用,可以自己了解下.
模板方法介绍:
模板方法模式可以将模板方法以及固定不变的基本方法统一封装到父类中,而将变化的部分封装到子类中实现,这样就由父类控制整个算法的流程,而子类实现算法的某些细节,实现了这两方面的解稿。当需要修改算法的行为时,开发人员可以通过添加类的方式实现,这符合“开放 封闭”原则。
模板方法模式不仅可以复用己有的代码,还可 以充分利用了面向对象的多态性,系统可以
在运行时选择 种具体子类实现完整的算法,这就提高系统的灵活性和可扩展性。
模板方法模式与其他设计模式一样 ,都会增加系统的抽象程度。另外 ,模板方法模式在修
改算法实现细节时,会增加类的个数,也会增加系统的复杂性。
BaseExecutor
这里就是模板方法的复用基础类可以简单看下。
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
//缓存
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
protected int queryStack;
private boolean closed;
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取sql
BoundSql boundSql = ms.getBoundSql(parameter);
//获取cacheKey
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
//从缓存中查找
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//实现查询
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
DeferredLoad deferredLoad = (DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
}
这里我提几个重要的属性PerpetualCache localCache query里面有用到,先声明下是一级缓存。为什么是一级,后面再说,会重新提到。下面这张图便是mybatis调用过程的流程图。
这里有人会好奇CacheKey对象是啥?CacheKey 对象由 MappedStatement id 、对应的 offset limit SQL
语句(包含“?”占位符)、用户传递的实参以及 Environment id 这五部分构成。只要一样就说明查询过,直接拿结果就可以了。
SimpleExecutor
public class SimpleExecutor extends BaseExecutor {
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
// StatementHandler 对象 ,前面介绍过,
//其 中根据 appedStatement.statementType 选择具体的 StatementHandler 实现
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//完成 Statement 的创建和初始化 ,该 方法首先会调用 StatementHandler.prepare ()方法创建Statement 对象 然后调 StatementHandler. parameterize ()方法处理占位符
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
}
这么一看眼熟吗? 创建statement ,通过 Statement 对象执行 SQL 语句,得到 ResultSet 对象 是不是直接ok了?