摘要:不是線程安全的,所以在使用的時候一定要保證他是局部變量。他對應的類圖如下有幾種常見的實現是默認的非線程安全的實現是中對的線程安全實現,在內部是使用的的形式來保證線程安全的是的核心。是線程安全的,可以被多個或映射器所共享使用。
MyBatis核心類
每一個MyBatis應用都是以一個SqlSessionFactory的實例為核心構建的。SqlSessionFactory的核心作用是什么?
從類的名稱上可以看出來,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的實例如何創建呢?
1、通常我們是在只有MyBatis的項目中是使用下面的代碼段來創建的:
String resource = "org/mybatis/example/mybatis-config.xml"; //讀取mybatis配置的問題 InputStream inputStream = Resources.getResourceAsStream(resource); //通過SqlSessionFactoryBuilder的build方式創建 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2、在Spring boot和MyBatis結合的項目中我會使用下面的代碼段來創建:
MyBatis-Spring-Boot-Starter依賴將會提供如下
自動檢測現有的DataSource
將創建并注冊SqlSessionFactory的實例,該實例使用SqlSessionFactoryBean將該DataSource作為輸入進行傳遞
將創建并注冊從SqlSessionFactory中獲取的SqlSessionTemplate的實例。
自動掃描您的mappers,將它們鏈接到SqlSessionTemplate并將其注冊到Spring上下文,以便將它們注入到您的bean中。
就是說,使用了該Starter之后,只需要定義一個DataSource即可(application.properties中可配置),它會自動創建使用該DataSource的SqlSessionFactory Bean以及SqlSessionTemplate。會自動掃描你的Mappers,連接到SqlSessionTemplate,并注冊到Spring上下文中.
@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里主要就是解析資源的內容,然后進行創建。
在這里有分別使用了兩個設計模式:
工廠模式
SqlSessionFactory就是一個工廠模式——簡單工廠模式的變形實現。
通過SqlSessionFactory中重載的不同openSession()方法來獲取不同類型的實例。
建造者模式(build模式)
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
因為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
registerSessionHolder()實現?
好難啊~~~~~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74713.html
摘要:目錄其中每個章節知識點都是相關連由淺入深的一步步全面分析了技術原理以及實戰由于文案較長想深入學習以及對于該文檔感興趣的朋友們可以加群免費獲取。這些場景在大量的編碼中使用,具備較強的實用價值,這些內容都是通過實戰得來的,供讀者們參考。 前言系統掌握MyBatis編程技巧已經成了用Java構建移動互聯網網站的必要條件 本文主要講解了Mybatis的應用,解析了其原理,從而形成一個完整的知識...
摘要:執行沒有,批處理不支持,將所有都添加到批處理中,等待統一執行,它緩存了多個對象,每個對象都是完畢后,等待逐一執行批處理。 Mybatis常見面試題 #{}和${}的區別是什么? #{}和${}的區別是什么? 在Mybatis中,有兩種占位符 #{}解析傳遞進來的參數數據 ${}對傳遞進來的參數原樣拼接在SQL中 #{}是預編譯處理,${}是字符串替換。 使用#{}可以有效的防止...
摘要:最終能和面試官聊的開心愉快投緣的叫面霸。能夠與很好的集成提供映射標簽,支持對象與數據庫的字段關系映射提供對象關系映射標簽,支持對象關系組件維護。使用可以有效的防止注入,提高系統安全性。 showImg(https://segmentfault.com/img/bVbsSlt?w=358&h=269); 一、概述 面試,難還是不難?取決于面試者的底蘊(氣場+技能)、心態和認知及溝通技巧。...
摘要:跳槽時時刻刻都在發生,但是我建議大家跳槽之前,先想清楚為什么要跳槽。切不可跟風,看到同事一個個都走了,自己也盲目的開始面試起來期間也沒有準備充分,到底是因為技術原因影響自己的發展,偏移自己規劃的軌跡,還是錢給少了,不受重視。 跳槽時時刻刻都在發生,但是我建議大家跳槽之前,先想清楚為什么要跳槽。切不可跟風,看到同事一個個都走了,自己也盲目的開始面試起來(期間也沒有準備充分),到底是因為技...
摘要:前言嗨,小伙伴們,這篇博文將帶大家手寫,讓大家對的核心原理以及工作流程有更加深刻的理解。模塊顧名思義,就是框架配置類,用于解析配置文件加載相關環境。配置模塊這里的對框架的配置使用了簡單的,主要原因還是簡單易懂然后節省時間。 前言 (????)??嗨,小伙伴們,這篇博文將帶大家手寫mybatis,讓大家對mybaits的核心原理以及工作流程有更加深刻的理解。在上篇Spring-Mybat...
摘要:從使用到原理學習線程池關于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實現在軟件開發中,分散于應用中多出的功能被稱為橫切關注點如事務安全緩存等。 Java 程序媛手把手教你設計模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經風雨慢慢變老,回首走過的點點滴滴,依然清楚的記得當初愛情萌芽的模樣…… Java 進階面試問題列表 -...
閱讀 1707·2023-04-26 02:30
閱讀 1033·2021-11-10 11:36
閱讀 1380·2021-10-08 10:14
閱讀 3496·2021-09-28 09:35
閱讀 1552·2021-08-23 09:47
閱讀 2544·2019-08-30 15:56
閱讀 1469·2019-08-30 15:44
閱讀 1751·2019-08-30 13:59