经过源码分析SqlSession功能实现、如何建立以及在Spring中是如何集成的。html
MyBatis工做的主要Java接口,经过这些接口你能够执行命令,获取mapper和管理事务
--代码注释git
查看大图github
在图中能够看到,咱们操做数据库的方法都在里面。spring
从类图能够看到SqlSession 有 DefaultSqlSession、SqlSessionManager2个实现类sql
/** * * The default implementation for {@link SqlSession}. * Note that this class is not Thread-Safe. * SqlSession 默认实现,非线程安全 * * @author Clinton Begin */ public class DefaultSqlSession implements SqlSession { public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; } /** * 返回单个查询结果 * @param statement 惟一标识匹配的语句. * @param parameter 查询参数. * @param <T> * @return */ @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; } } /** * 返回集合结果 * @param statement 惟一标识匹配的语句 * @param parameter 查询参数 * @param rowBounds 返回结果的大小控制 * @param <E> * @return */ @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(); } } /** * 返回Map对象 * @param statement 惟一标识匹配的语句. * @param parameter 查询参数 * @param mapKey key值,字段的属性别名 * @param rowBounds 返回结果的大小控制 * @param <K> * @param <V> * @return */ @Override public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { final List<? extends V> list = selectList(statement, parameter, rowBounds); final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); final DefaultResultContext<V> context = new DefaultResultContext<V>(); for (V o : list) { context.nextResultObject(o); mapResultHandler.handleResult(context); } return mapResultHandler.getMappedResults(); } /** * 游标查询 * @param <T> * @return */ @Override public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds); registerCursor(cursor); return cursor; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * @param statement 惟一标识匹配的语句 * @param parameter 查询参数 * @param rowBounds 返回结果的大小控制 * @param handler 外部结果处理器 */ @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 增长 * @return */ @Override public int insert(String statement, Object parameter) { return update(statement, parameter); } /** * 修改 * @return */ @Override public int update(String statement) { return update(statement, null); } /** * 增删改公用方法 * @param statement 惟一标识匹配的执行语句 * @param parameter 参数 * @return 返回影响的行数 */ @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(); } } /** * 删除 * @return */ @Override public int delete(String statement) { return update(statement, null); } /** * 提交 * @param force forces connection commit */ @Override public void commit(boolean force) { try { executor.commit(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 回滚 * @param force forces connection rollback */ @Override public void rollback(boolean force) { try { executor.rollback(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 提交批处理执行 * @return 批处理提交更新记录 */ @Override public List<BatchResult> flushStatements() { try { return executor.flushStatements(); } catch (Exception e) { throw ExceptionFactory.wrapException("Error flushing statements. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 关闭 */ @Override public void close() { try { executor.close(isCommitOrRollbackRequired(false)); closeCursors(); dirty = false; } finally { ErrorContext.instance().reset(); } } /** * 获取Mapper * @param type Mapper对应的Class类型 * @param <T> * @return */ @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } // 省略其余代码 }
先看类图:数据库
查看大图apache
从图中能够看出 SqlSessionManager实现了SqlSessionFactory接口,又封装了DefaultSqlSessionFactory
代码以下:编程
public class SqlSessionManager implements SqlSessionFactory, SqlSession { //省略其余代码 public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) { return new SqlSessionManager(sqlSessionFactory); } }
SqlSessionManager与DefaultSqlSessionFactory区别主要有2个:安全
public void startManagedSession() { this.localSqlSession.set(openSession()); } @Override public SqlSession openSession() { return sqlSessionFactory.openSession(); }
在DefaultSqlSessionFactory中每次openSession都会产生一个新的DefaultSqlSessionsession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { try { //新建DefaultSqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { } finally { } }
更详细的源码参考下节中:DefaultSqlSessionFactory 源码分析
SqlSessionManager 内部提供了一个sqlSessionProxy,这个sqlSessionProxy提供了全部SqlSession接口的实现,而实现中正是使用了上面提到的本地线程保存的Sqlsession实例。
这样,在同一个线程实现不一样的sql操做,能够复用本地线程Sqlsession,避免了DefaultSqlSessionFactory实现的每个sql操做都要建立新的Sqlsession实例。
让咱们具体来看下sqlSessionProxy 的实现:
public class SqlSessionManager implements SqlSessionFactory, SqlSession { private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; //建立SqlSession代理对象 this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); } @Override public <T> T selectOne(String statement, Object parameter) { //使用代理对象执行数据库操做 return sqlSessionProxy.<T> selectOne(statement, parameter); } private class SqlSessionInterceptor implements InvocationHandler { public SqlSessionInterceptor() { // Prevent Synthetic Access } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //从本地线程变量中获取SqlSession实例 final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) { //不为null try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { //为null 则打开新链接 final SqlSession autoSqlSession = openSession(); try { final Object result = method.invoke(autoSqlSession, args); autoSqlSession.commit(); return result; } catch (Throwable t) { autoSqlSession.rollback(); throw ExceptionUtil.unwrapThrowable(t); } finally { autoSqlSession.close(); } } } } //省略其余代码 }
要了解SqlSession具体如何建立,咱们就须要知道SqlSessionFactory,也就是SqlSession工厂。
从类图能够看出 SqlSessionFactory 为具体SqlSession工厂定义
DefaultSqlSessionFactory 实现了SqlSessionFactory,SqlSession是由DefaultSqlSessionFactory生成
/** * 经过外部传入的connection 或 database 建立(打开) 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(); }
public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { #1 this.configuration = configuration; } //省略... /** * #mark 建立SqlSession * @param execType 执行器类型 * @param level 事务隔离级别 * @param autoCommit 是否自动提交 * @return */ private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { #2 Transaction tx = null; try { //传入的configuration获取环境变量对象、Environment能够配置多个环境配置 final Environment environment = configuration.getEnvironment(); //从环境对象中获取事务工厂对象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //根据DataSource、事务隔离级别、自动提交建立事务对象 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //#mark 新建执行者 20170820 final Executor executor = configuration.newExecutor(tx, execType); //#mark 建立默认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 TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { if (environment == null || environment.getTransactionFactory() == null) { return new ManagedTransactionFactory(); } return environment.getTransactionFactory(); } private void closeTransaction(Transaction tx) if (tx != null) { try { tx.close(); } catch (SQLException ignore) { // Intentionally ignore. Prefer previous error. } } } }
详细说明:
标注#1 经过构造方法传入Configuration 配置对象,Configuration是一个贯穿全剧的对象
标注#2 openSessionFromDataSource 顾名思义,从DataSource打开SqlSession,调用new DefaultSqlSession(configuration, executor, autoCommit) 构建SqlSession,具体实现查看源码备注
Executor 、ErrorContext 后续详细介绍
从前面的描述中,咱们知道SqlSession由DefaultSqlSessionFactory 产生。经过IDEA关联搜索功能,咱们找到了具体的调用类为:SqlSessionFactoryBuilder。 SqlSessionFactoryBuilder 主要是获取配置输入流,建立DefaultSqlSessionFactory实例
先看下类图:
SqlSessionFactoryBuilder 源码分析
/** * Builds {@link SqlSession} instances. * SqlSession 工厂构造器 * * @author Clinton Begin */ public class SqlSessionFactoryBuilder { //省略 /** * 经过字符流构建 * @param reader 字符流 * @param environment 环境变量 * @param properties 属性配置 * @return */ public SqlSessionFactory build(Reader reader, String environment, Properties properties) { #1 try { //从字符流中建立XML配置对象 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } //省略 /** * 经过字节流构建 * @param inputStream * @param environment * @param properties * @return */ public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } /** * #mark SqlSessionFactory 初始化 * @param config * @return */ public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
依据测试规范,咱们找到测试类 SqlSessionTest,这个类方法比较多,我精简出须要的部分。
/** * #mark 源码学习入口 */ public class SqlSessionTest extends BaseDataTest { private static SqlSessionFactory sqlMapper; @BeforeClass public static void setup() throws Exception { //初始化数据源,使用内存数据库、运行一次自动销毁 createBlogDataSource(); //资源文件地址 final String resource = "org/apache/ibatis/builder/MapperConfig.xml"; //获取资源文件字符流 final Reader reader = Resources.getResourceAsReader(resource); //构建 SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); } /** * 测试SqlSession 开启和关闭 * @throws Exception */ @Test public void shouldOpenAndClose() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); session.close(); } /** * 测试提交一个未使用的SqlSession * @throws Exception */ @Test public void shouldCommitAnUnUsedSqlSession() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); session.commit(true); session.close(); } /** * 测试提交一个未使用的SqlSession * @throws Exception */ @Test public void shouldRollbackAnUnUsedSqlSession() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); session.rollback(true); session.close(); } /** * 跟踪一个完整查询 * 查出全部做者 #20170831 * @throws Exception */ @Test public void shouldSelectAllAuthors() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); try { List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors"); assertEquals(2, authors.size()); } finally { session.close(); } } //省略部分代码
@BeforeClass
public static void setup() throws Exception {}
在这个方法中包含了具体的SqlSession的建立过程
SqlSessionFactoryBean在基本的 MyBatis 中,session 工厂可使用 SqlSessionFactoryBuilder 来建立。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来替代。
-- 官方文档
那咱们下载MyBatis-Spring源码 具体看看
SqlSessionFactoryBean实现了Spring 的3个重要接口:
关键代码
/** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { //参数检测 notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); //sqlSessionFactory 实例化 this.sqlSessionFactory = buildSqlSessionFactory(); } /** * Build a {@code SqlSessionFactory} instance. * * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a * {@code SqlSessionFactory} instance based on an Reader. * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file). * * @return SqlSessionFactory * @throws IOException if loading the config file failed */ protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; //省略 configuration 建立代码 //返回建立的SqlSessionFactory return this.sqlSessionFactoryBuilder.build(configuration); }
关键代码
/** * {@inheritDoc} */ @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } //返回建立好的SqlSessionFatory对象 return this.sqlSessionFactory; }
关键代码
/** * {@inheritDoc} */ @Override public void onApplicationEvent(ApplicationEvent event) { if (failFast && event instanceof ContextRefreshedEvent) { // fail-fast -> check all statements are completed //检测MyBatis全部配置文件语句是否完成 this.sqlSessionFactory.getConfiguration().getMappedStatementNames(); } }
关于MyBatis源码解读之SqlSession就介绍到这里。若有疑问,欢迎留言,谢谢。