每個MyBatis應用都是以一個SqlSessionFactory
的實例爲核心構建的。SqlSessionFactory
的核心做用是什麼?sql
從類的名稱上能夠看出來,SqlSessionFactory
是產生SqlSession
的工廠。SqlSessionFactory
是經過SqlSessionFactoryBuilder
這個構建器來構建的。
SqlSessionFactory
是一個接口,其中定義了獲取SqlSession
的方法。數據庫
public interface SqlSessionFactory { //獲取一個SqlSession SqlSession openSession(); //獲取一個SqlSession,參數設置事務是否自動提交 SqlSession openSession(boolean var1); //經過指定Connection中的參數獲取 SqlSession openSession(Connection var1); //獲取SqlSession,設置事務的隔離級別,NONE(0),READ_COMMITTED(2),READ_UNCOMMITTED(1),REPEATABLE_READ(4),SERIALIZABLE(8); SqlSession openSession(TransactionIsolationLevel var1); //獲取SqlSession,同時制定執行的類別,支持三種SIMPLE(這種模式下,將爲每一個語句建立一個PreparedStatement),REUSE(這個模式下重複使用preparedStatment),BATCH(批量更新,insert時候,若是沒有提交,沒法獲取自增id); SqlSession openSession(ExecutorType var1); SqlSession openSession(ExecutorType var1, boolean var2); SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2); SqlSession openSession(ExecutorType var1, Connection var2); //獲取全部的配置項 Configuration getConfiguration(); }
SqlSessionFactory
包含兩個實現:DefaultSqlSessionFactory
和SqlSessionManager
SqlSessionFactory
的實例如何建立呢?
一、一般咱們是在只有MyBatis的項目中是使用下面的代碼段來建立的:設計模式
String resource = "org/mybatis/example/mybatis-config.xml"; //讀取mybatis配置的問題 InputStream inputStream = Resources.getResourceAsStream(resource); //經過SqlSessionFactoryBuilder的build方式建立 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
二、在Spring boot和MyBatis結合的項目中我會使用下面的代碼段來建立:緩存
MyBatis-Spring-Boot-Starter依賴將會提供以下
@Bean public SqlSessionFactory sqlSessionFactory(@Qualifier("druidDataSource") DataSource druidDataSource) throws Exception { //先建立一個SqlSessionFactoryBean SqlSessionFactoryBean fb = new SqlSessionFactoryBean(); //在這個bean裏設置須要的參數 fb.setDataSource(this.dynamicDataSource(druidDataSource)); fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package")); fb.setTypeHandlersPackage(env.getProperty("mybatis.type-handlers-package")); fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations"))); //經過這個方法獲取一個SqlSessionFactory return fb.getObject(); }
在代碼裏一直向下查找的時候就會發現:這個過程其實和上面的過程同樣。
SqlSessionFactoryBean會將一系列的屬性封裝成一個Configuration對象,而後調用this.sqlSessionFactoryBuilder.build(configuration)
來建立。而在sqlSessionFactoryBuilder裏主要就是解析資源的內容,而後進行建立。安全
在這裏有分別使用了兩個設計模式:session
SqlSessionFactory
就是一個工廠模式——簡單工廠模式的變形實現。
經過SqlSessionFactory
中重載的不一樣openSession()方法來獲取不一樣類型的實例。mybatis
SqlSessionFactoryBuilder
建立SqlSessionFactory
就是建造者模式的實現。在建立的過程當中須要解析不少的文件,生成對象,進行緩存等操做,因此一個方法是很難直接寫完,因此其中應用了大量的build模式:多線程
protected SqlSessionFactory buildSqlSessionFactory() throws IOException { XMLConfigBuilder xmlConfigBuilder = null; Configuration configuration; if (this.configuration != null) { ...... } else if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { ...... } ...... if (xmlConfigBuilder != null) { try { //builder解析 xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception var22) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var22); } finally { ErrorContext.instance().reset(); } } ...... if (!ObjectUtils.isEmpty(this.mapperLocations)) { Resource[] var29 = this.mapperLocations; var27 = var29.length; for(var5 = 0; var5 < var27; ++var5) { Resource mapperLocation = var29[var5]; if (mapperLocation != null) { try { //Mapper文件build XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception var20) { } finally { //單例模式 ErrorContext.instance().reset(); } } } } //build一個對象返回 return this.sqlSessionFactoryBuilder.build(configuration); }
SqlSessionFactory
建立完成以後,就能夠經過SqlSessionFactory
對象來獲取一個SqlSession
實例.SqlSession
是一次與數據庫的會話.在他的接口中定義了一些列的CRUD和事務的操做接口。SqlSession
是暴露給用戶使用的API,一個SqlSession
對應着一次數據庫會話。SqlSession
不是線程安全的,因此在使用的時候必定要保證他是局部變量。
他對應的類圖以下:SqlSession
有幾種常見的實現:DefaultSqkSession
是默認的非線程安全的實現,SqlSessionManager
是Mybatis中對SqlSession
的線程安全實現,在內部是使用的private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal();
的形式來保證線程安全的,SqlSessionTemplate
是MyBatis-Spring 的核心。做爲 SqlSession 的一個實現,這意味着可使用它無縫代替你代碼中已經在使用的 SqlSession。SqlSessionTemplate 是線程安全的,能夠被多個 DAO 或映射器所共享使用。app
由於SqlSessionTemplate
是線程安全的,因此當SqlSessionTemplate
是單例的時候,多線程調用SqlSessionTemplate
仍然使用的是同一個SqlSession
,接下來看一下SqlSessionTemplate
是如何保證線程安全的呢?函數
首先咱們看一下SqlSessionTemplate
的建立過程:
/** * 構造函數1,須要傳入參數SqlSessionFactory */ public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true)); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); Assert.notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; //建立代理類的實例,該代理類實現了SqlSession接口,這裏使用的是基於JDK的動態代理,SqlSessionInterceptor也是實現了InvocationHandler接口,最後代理對象的操做都會通過invoke執行 //class SqlSessionInterceptor implements InvocationHandler this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor()); }
invoke的實現是實現線程安全的核心:
private class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //獲取真實的SqlSession,這個是真實使用的,是MyBatis生成的DefaultSqlSession,獲取是線程安全實現的核心 SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); Object unwrapped; try { //執行操做 Object result = method.invoke(sqlSession, args); //若是不是事務類型的,那麼設置爲自動提交 if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } //執行結果包裝 unwrapped = result; } catch (Throwable var11) { unwrapped = ExceptionUtil.unwrapThrowable(var11); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped); if (translated != null) { unwrapped = translated; } } throw (Throwable)unwrapped; } finally { if (sqlSession != null) { SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } //返回執行結果 return unwrapped; } }
接着看getSqlSession()的代碼:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sessionFactory, "No SqlSessionFactory specified"); Assert.notNull(executorType, "No ExecutorType specified"); //SqlSessionHolder是對SqlSession的一個功能包裝,TransactionSynchronizationManager是一個事務同步管理器,維護當前線程事務資源,信息以及TxSync集合,getResource會從 ThreadLocal<Map<Object, Object>> resources 中獲取當前線程SqlSessionHolder實例 SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } //若是沒有獲取成功,那麼開啓一個SqlSession session = sessionFactory.openSession(executorType); //註冊SessionHolder registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } }
registerSessionHolder()實現?
好難啊~~~~~