mybatis的执行流程(来源于网络):
前几篇分析过Executor,执行器在StatementHandler上添加了缓存和事务的功能,但api还是比较偏底层,因此需要再Executor上再封装、增强一层,因此就有了SqlSession。
使用实例:
// 构造 SqlSessionFactory
Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);
// 创建SqlSession实例
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
Object param = new Object();
Object result = sqlSession.selectOne("statementId", param);
跟着实例的顺序解析
SqlSessionFactory构造器,build方法参数为Configuration,
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
其他build的重载版本最终是为了解析获得Configuration。
SqlSession工厂,就只负责SqlSession的创建。
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
创建SqlSession方法分为几类:
SqlSessionFactory目前就一个实现类 DefaultSqlSessionFactory
所有的重载版本可归为两类:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取环境信息
final Environment environment = configuration.getEnvironment();
//获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建一个新的事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建一个新的执行器
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();
}
}
创建步骤:
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
步骤与FromDataSource一模一样,区别点在于创建事务时,一个通过数据源,一个通过数据源连接,用于Connection传递复用的场景。
SqlSession关于sql执行方法的实现主要还是依靠Executor,因此创建SqlSession的主要点就是创建Executor。
Executor只有查询和update两个方法,明显对于上层用户是很不方便使用的。
SqlSession在此进行细粒度的拆分,对返回值类型也进行了扩充。
查询分为了:
修改分为了:
初次之外还有事务、缓存相关的api。
来看下SqlSession的实现类 DefaultSqlSession。
public class DefaultSqlSession implements SqlSession {
// 配置
private final Configuration configuration;
// 执行器
private final Executor executor;
// 是否自动提交
private final boolean autoCommit;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
}
挑几个重点方法看下实现
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/*
* 根据statementId找到唯一的statement即sql语句
*/
MappedStatement ms = configuration.getMappedStatement(statement);
/*
* 直接委托给 executor执行器 来完成sql命令
*/
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();
}
}
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
sql相关方法比较简单,就是对不同的返回值、sql类型做了封装。
想一下,如果直接使用SqlSession的话,调用select、insert等方法,每次需要传入StatementId,这是一个字符串,非常的不方便,万一传错了那么就会报错,编译时还发现不了。
mybatis也想到了这点,所以有更方便使用的方式,那就是为Mapper创建代理对象,直接调用mapper方法自动完成sql的执行。
创建Mapper代理对象的方法就在SqlSession中。
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
实际实现是由Configuration完成的。
这里我们来看下这里代理对象是如何实现的
MapperMethod:根据方法、参数类型路由到SqlSession的具体方法
MapperProxy:Mapper代理实现类,缓存MapperMethod,拦截Mapper方法。
MapperProxyFactory:MapperProxy工厂,负责创建MapperProxy。
SqlSession是mybatis提供给上层使用的核心类,提供了比较简单的api,为了让使用更方便,添加了代理的方式,让sql执行更加丝滑。