mybatis 核心逻辑处理篇:
通过解析返回的 Configuration 对象,
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
【new DefaultSqlSessionFactory(config);】
核心我们从 DefaultSqlSessionFactory.class 源码说起。 通过创建工厂获取到 SqlSession 。然而创建 SqlSession 的因素需要 DataSource 和 TransactionFactory 二个。通过数据源及事务工厂我们可以得到 Transaction 对象。 (Transaction 对象可以获取数据库连接、可以提交事务、可以回滚事务、关闭连接、可以获取到事务超时)。得到 Transaction 事务可以使用 Configuration 创建执行器 Executor 。 而 SqlSession 就是封装 Executor 的对象来操作数据库。
private final Configuration configuration;
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);//无参数
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取环境 (事务工厂、数据源)
final Environment environment = configuration.getEnvironment();
// 获取事务工厂 (JdbcTransactionFactory / 默认ManagedTransactionFactory / SpringManagedTransactionFactory)
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 通过工厂创建事务 (JdbcTransaction / ManagedTransaction / SpringManagedTransaction)
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 通过事务和 ExecutorType 创建执行器 (SimpleExecutor / CachingExecutor / BatchExecutor)
final Executor executor = configuration.newExecutor(tx, execType);// 通过拦截器处理 执行器(是否创建代理)
// 通过执行器创建 SqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
根据代码可以发现,可以通过 Environment 对象获取到事务工厂及数据源 (TransactionFactory \ DataSource) , 然后通过事务工厂可以创建事务对象 (Transaction)
Transaction transaction = transactionFactory.newTransaction(dataSource, null, false);
例如:
JdbcTransactionFactory 创建 => JdbcTransaction
ManagedTransactionFactory 创建 => ManagedTransaction
到这说下 DataSource 跟 TransactionFactory 的创建 ,上一章解析篇没分析mybatis-config.xml 中的 environments 节点解析。。 在这简单说下解析逻辑
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
解析对应的节点, 获取属性type 然后找到对应的class 初始化。在通过封装成 Environment 对象储存到 Configuration 配置中心
//解析<environments> 节点
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
//获取子节点 <environment id="development">
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// TransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// DataSourceFactory
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
// DataSource
DataSource dataSource = dsFactory.getDataSource();
// Environment
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
// 解析 <transactionManager type="JDBC"/>
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
// 如: type 为 JDBC 就会创建 JdbcTransactionFactory
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
// 解析 <dataSource type="POOLED">
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
其中 resolveClass() 方法作用就是通过name找到已经注册的class 然后实例化.
这下我们就可以知道通过配置属性值找个对应的DataSource 及 TransactionFactory 。
然后可以看到通过 DataSource 创建了 Transaction 事务对象,在通过事务对象(Transaction)和执行器类型(ExecutorType)创建 对应的执行器(Executor)
final Executor executor = configuration.newExecutor(tx, execType); // CachingExecutor
Executor 讲下: 比较重要的二个方法 update() 与 query().
可以通过 mybatis 中 Interceptor 接口来进行拦截进行sql 语句的修改,例如分页功能
根据执行器(Executor)创建 SqlSession
SqlSession sqlSession = new DefaultSqlSession(configuration, executor, false);
List entitys = sqlSession.selectList(“findAll”);
DefaultSqlSession#selectList() 核心方法
逻辑:根据 id 标识找到对应的标签封装对象 MappedStatement , 在通过 执行器 Executor 执行对应的sql 。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据id 查询对应的映射语句对象
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();
}
}
执行器 Executor => BaseExecutor 中四个抽象方法
1、SimpleExecutor 简单执行器(默认)
2、BatchExecutor 分批执行器
3、ReuseExecutor 重用执行器
4、CachingExecutor 缓存执行器(装饰模式)
逻辑:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {//
//通过MappedStatement 获取sql语句
BoundSql boundSql = ms.getBoundSql(parameter);
//创建CacheKey
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);//
}
// rowBounds
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.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++; //查询次数+1
//先从缓存读取, 如若找到就不查询数据库
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//处理本地缓存参数(一般不做处理)
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);//处理本地缓存输出参数
} else {
// 如果缓存为空就查询数据库
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); //先储存为null
try {
//doQuery 抽象方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) { //默认 PREPARED
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
抽象方法
//修改
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
// 查询
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
核心逻辑先分析到这, 下章会继续介绍 缓存篇(PerpetualCache)与事务篇