最最常用的 SqlSessionFactory 和 SqlSession,你真的了解嗎?
前言
學(xué)習(xí)框架一個(gè)比較好的路徑閱讀源碼。本文介紹的SqlSessionFactory和SqlSession。可以通過(guò)了解SqlSessionFactory接口和SqlSession接口以及兩個(gè)的實(shí)現(xiàn)類入手,去看源碼了解實(shí)現(xiàn)過(guò)程。最好能把項(xiàng)目下載到本地,慢慢分析實(shí)現(xiàn)過(guò)程。

MyBatis的持久化解決方案是將用戶從原始的JDBC訪問(wèn)中解放出來(lái),用戶只需要定義需要操作的SQL語(yǔ)句,無(wú)須關(guān)注底層的JDBC操作,就可以以面向?qū)ο蟮姆绞絹?lái)進(jìn)行持久化層操作。底層數(shù)據(jù)庫(kù)連接的獲取,數(shù)據(jù)訪問(wèn)的實(shí)現(xiàn),事務(wù)控制等都無(wú)須用戶關(guān)心,從而將應(yīng)用層從底層的JDBC/JTA API抽取出來(lái)。
通過(guò)配置文件管理JDBC連接,讓MyBatis解決持久化的實(shí)現(xiàn)。在MyBatis中的常見(jiàn)對(duì)象有SqlSessionFactory和SqlSession。本文這種介紹一下兩者的概念和使用。
一、 SqlSessionFactory
SqlSessionFactory是MyBatis的關(guān)鍵對(duì)象,它是個(gè)單個(gè)數(shù)據(jù)庫(kù)映射關(guān)系經(jīng)過(guò)編譯后的內(nèi)存鏡像。SqlSessionFactory對(duì)象的實(shí)例可以通過(guò)SqlSessionFactoryBuilder對(duì)象類獲得,而SqlSessionFactoryBuilder則可以從XML配置文件或一個(gè)預(yù)先定制的Configuration的實(shí)例構(gòu)建出SqlSessionFactory的實(shí)例。
每一個(gè)MyBatis的應(yīng)用程序都以一個(gè)SqlSessionFactory對(duì)象的實(shí)例為核心。同時(shí)SqlSessionFactory也是線程安全的,SqlSessionFactory一旦被創(chuàng)建,應(yīng)該在應(yīng)用執(zhí)行期間都存在。在應(yīng)用運(yùn)行期間不要重復(fù)創(chuàng)建多次,建議使用單例模式。SqlSessionFactory是創(chuàng)建SqlSession的工廠。
//SqlSessionFactory接口源碼如下所示:
package?org.apache.ibatis.session;
import?java.sql.Connection;
public?interface?SqlSessionFactory?{
??SqlSession?openSession();//這個(gè)方法最經(jīng)常用,用來(lái)創(chuàng)建SqlSession對(duì)象.
??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
SqlSession是MyBatis的關(guān)鍵對(duì)象,是執(zhí)行持久化操作的獨(dú)享,類似于JDBC中的Connection。它是應(yīng)用程序與持久層之間執(zhí)行交互操作的一個(gè)單線程對(duì)象,也是MyBatis執(zhí)行持久化操作的關(guān)鍵對(duì)象。SqlSession對(duì)象完全包含以數(shù)據(jù)庫(kù)為背景的所有執(zhí)行SQL操作的方法,它的底層封裝了JDBC連接,可以用SqlSession實(shí)例來(lái)直接執(zhí)行被映射的SQL語(yǔ)句。
每個(gè)線程都應(yīng)該有它自己的SqlSession實(shí)例。SqlSession的實(shí)例不能被共享,同時(shí)SqlSession也是線程不安全的,絕對(duì)不能講SqlSeesion實(shí)例的引用放在一個(gè)類的靜態(tài)字段甚至是實(shí)例字段中。也絕不能將SqlSession實(shí)例的引用放在任何類型的管理范圍中,比如Servlet當(dāng)中的HttpSession對(duì)象中。使用完SqlSeesion之后關(guān)閉Session很重要,應(yīng)該確保使用finally塊來(lái)關(guān)閉它。
//SqlSession接口源碼如下所示:
package?org.apache.ibatis.session;
import?java.io.Closeable;
import?java.sql.Connection;
import?java.util.List;
import?java.util.Map;
import?org.apache.ibatis.executor.BatchResult;
public?interface?SqlSession?extends?Closeable?{
???T?selectOne(String?statement);
???T?selectOne(String?statement,?Object?parameter);
???List?selectList(String?statement) ;
???List?selectList(String?statement,?Object?parameter) ;
???List?selectList(String?statement,?Object?parameter,?RowBounds?rowBounds) ;
???Map?selectMap(String?statement,?String?mapKey) ;
???Map?selectMap(String?statement,?Object?parameter,?String?mapKey) ;
???Map?selectMap(String?statement,?Object?parameter,?String?mapKey,?RowBounds?rowBounds) ;
??void?select(String?statement,?Object?parameter,?ResultHandler?handler);
??void?select(String?statement,?ResultHandler?handler);
??void?select(String?statement,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?handler);
??int?insert(String?statement);
??int?insert(String?statement,?Object?parameter);
??int?update(String?statement);
??int?update(String?statement,?Object?parameter);
??int?delete(String?statement);
??int?delete(String?statement,?Object?parameter);
??void?commit();
??void?commit(boolean?force);
??void?rollback();
??void?rollback(boolean?force);
??List?flushStatements() ;
??void?close();
??void?clearCache();
??Configuration?getConfiguration();
???T?getMapper(Class?type) ;
??Connection?getConnection();
}
三、SqlSessionFactory和SqlSession實(shí)現(xiàn)過(guò)程
mybatis框架主要是圍繞著SqlSessionFactory進(jìn)行的,創(chuàng)建過(guò)程大概如下:
(1)、定義一個(gè)Configuration對(duì)象,其中包含數(shù)據(jù)源、事務(wù)、mapper文件資源以及影響數(shù)據(jù)庫(kù)行為屬性設(shè)置settings
(2)、通過(guò)配置對(duì)象,則可以創(chuàng)建一個(gè)SqlSessionFactoryBuilder對(duì)象
(3)、通過(guò) SqlSessionFactoryBuilder 獲得SqlSessionFactory 的實(shí)例。
(4)、SqlSessionFactory 的實(shí)例可以獲得操作數(shù)據(jù)的SqlSession實(shí)例,通過(guò)這個(gè)實(shí)例對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作
并且如果想按照上述方式得到SqlSessionFactory,最好使用下面的mybatis-config。xml類似的配置。在這里mybatis-config.xml配置文件是沒(méi)有和Spring配置文件整合過(guò)得,如果項(xiàng)目中mybaits的配置文件和Spring配置文件整合過(guò)了,則下面的代碼運(yùn)行估計(jì)會(huì)出錯(cuò),因?yàn)橐话鉺pring和mybatis整合過(guò)之后,mybatis的配置文件基本沒(méi)有存在的必要了。
之前在mybatis中配置的數(shù)據(jù)源和事務(wù)這兩個(gè)方面,一般的做法都會(huì)spring的配置文件,則下面的代碼加載mybatis-config.xml的時(shí)候,得不到必要的信息,創(chuàng)建的過(guò)程中會(huì)有問(wèn)題。所以在這里先給一份mybatis-config.xml單獨(dú)的配置文件。
??
configuration?PUBLIC?"-//mybatis.org//DTD?Config?3.0//EN"??
"http://mybatis.org/dtd/mybatis-3-config.dtd">??
<configuration>???
??????
????<properties?resource="db.properties"/>??
??????
????<typeAliases>??
????????<typeAlias?type="cn.itcast.javaee.mybatis.app04.Student"?alias="student"/>??
????typeAliases>??
??????
????<environments?default="mysql_developer">??
??????????
????????<environment?id="mysql_developer">??
??????????????
????????????<transactionManager?type="jdbc"/>??
??????????????
????????????<dataSource?type="pooled">??
??????????????????
????????????????<property?name="driver"?value="${mysql.driver}"/>??
????????????????<property?name="url"?value="${mysql.url}"/>??
????????????????<property?name="username"?value="${mysql.username}"/>??
????????????????<property?name="password"?value="${mysql.password}"/>??
????????????dataSource>??
????????environment>??
??????????
????????<environment?id="oracle_developer">??
??????????????
????????????<transactionManager?type="jdbc"/>??
??????????????
????????????<dataSource?type="pooled">??
??????????????????
????????????????<property?name="driver"?value="${oracle.driver}"/>??
????????????????<property?name="url"?value="${oracle.url}"/>??
????????????????<property?name="username"?value="${oracle.username}"/>??
????????????????<property?name="password"?value="${oracle.password}"/>??
????????????dataSource>??
????????environment>??
????environments>??
??????
????<mappers>??
????????<mapper?resource="cn/itcast/javaee/mybatis/app14/StudentMapper.xml"/>??
????mappers>??
configuration>??
下面的這行代碼功能是通過(guò)配置文件mybatis-config.xml,創(chuàng)建SqlSessionFactory對(duì)象,然后產(chǎn)生SqlSession,執(zhí)行SQL語(yǔ)句。而mybatis的初始化發(fā)生在:
SqlSessionFactory?sqlSessionFactory?=??new?SqlSessionFactoryBuilder().build(resourceAsStream);
如果是spring和mybaits整合之后的配置文件,一般以這種方式實(shí)現(xiàn),SqlSessionFactory的創(chuàng)建:
<bean?id="sqlSessionFactory"?class="org.mybatis.spring.SqlSessionFactoryBean">
????????<property?name="dataSource"?ref="dataSource">property>
????????
????????<property?name="mapperLocations"?value="classpath:com/cn/mapper/*.xml">property>
bean>
關(guān)于SqlSessionFactory和SqlSession兩個(gè)對(duì)象給一個(gè)具體的使用過(guò)程:
package?com.cn.testIUserService;
import?java.io.IOException;
import?java.io.InputStream;
import?org.apache.ibatis.io.Resources;
import?org.apache.ibatis.session.SqlSession;
import?org.apache.ibatis.session.SqlSessionFactory;
import?org.apache.ibatis.session.SqlSessionFactoryBuilder;
import?com.cn.entity.User;
public?class?MyBatisTest?{
????public?static?void?main(String[]?args)?{
????????try?{
????????????//讀取mybatis-config.xml文件
????????????InputStream?resourceAsStream?=?Resources.getResourceAsStream("mybatis-config.xml");
????????????//初始化mybatis,創(chuàng)建SqlSessionFactory類的實(shí)例
????????????SqlSessionFactory?sqlSessionFactory?=??new?SqlSessionFactoryBuilder().build(resourceAsStream);
????????????//創(chuàng)建session實(shí)例
????????????SqlSession?session?=?sqlSessionFactory.openSession();
????????????/*
?????????????*?接下來(lái)在這里做很多事情,到目前為止,目的已經(jīng)達(dá)到得到了SqlSession對(duì)象.通過(guò)調(diào)用SqlSession里面的方法,
?????????????*?可以測(cè)試MyBatis和Dao層接口方法之間的正確性,當(dāng)然也可以做別的很多事情,在這里就不列舉了
?????????????*/
????????????//插入數(shù)據(jù)
????????????User?user?=?new?User();
????????????user.setC_password("123");
????????????user.setC_username("123");
????????????user.setC_salt("123");
????????????//第一個(gè)參數(shù)為方法的完全限定名:位置信息+映射文件當(dāng)中的id
????????????session.insert("com.cn.dao.UserMapping.insertUserInformation",?user);
????????????//提交事務(wù)
????????????session.commit();
????????????//關(guān)閉session
????????????session.close();
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????}
????}
}
針對(duì)上面的代碼給出詳細(xì)的說(shuō)明關(guān)于SqlSessionFactory和SqlSession創(chuàng)建過(guò)程涉及的內(nèi)容。

結(jié)合上述SqlSessionFactory和SqlSession使用過(guò)程和結(jié)構(gòu)圖,涉及到的方法為下面步驟,結(jié)合源碼中的方法為下面的步驟:
第一步首先 SqlSessionFactoryBuilder去讀取mybatis的配置文件,然后build一個(gè)DefaultSqlSessionFactory,即得到SqlSessionFactory
//源碼中涉及的包和具體方法為:
//涉及的包為:package?org.apache.ibatis.session;
//第一個(gè)類為:SqlSessionFactoryBuilder,設(shè)計(jì)到此類的方法為下面部分:
public?SqlSessionFactory?build(InputStream?inputStream)?{
????return?build(inputStream,?null,?null);
??}
public?SqlSessionFactory?build(InputStream?inputStream,?String?environment,?Properties?properties)?{
????try?{
??????//通過(guò)XMLConfigBuilder解析配置文件,解析的配置相關(guān)信息都會(huì)封裝為一個(gè)Configuration對(duì)象
??????XMLConfigBuilder?parser?=?new?XMLConfigBuilder(inputStream,?environment,?properties);
??????//然后返回一個(gè)DefaultSqlSessionFactory
??????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.
??????}
????}
??}
?//得到DefaultSqlSessionFactory
?public?SqlSessionFactory?build(Configuration?config)?{
???return?new?DefaultSqlSessionFactory(config);
?}
//第二個(gè)類為:DefaultSqlSessionFactory,涉及的方法為:
??public?DefaultSqlSessionFactory(Configuration?configuration)?{
????this.configuration?=?configuration;
??}
第二步,獲取到SqlSessionFactory之后,就可以利用SqlSessionFactory方法的openSession來(lái)獲取SqlSession對(duì)象了。
private?SqlSession?openSessionFromDataSource(ExecutorType?execType,?TransactionIsolationLevel?level,?boolean?autoCommit)?{
??Transaction?tx?=?null;
??try?{
????//通過(guò)Confuguration對(duì)象去獲取Mybatis相關(guān)配置信息,?Environment對(duì)象包含了數(shù)據(jù)源和事務(wù)的配置
????//?execType為執(zhí)行器類型,配置文件中定義
????// SimpleExecutor -- SIMPLE 就是普通的執(zhí)行器。
????//ReuseExecutor?-執(zhí)行器會(huì)重用預(yù)處理語(yǔ)句(prepared?statements)
????//BatchExecutor?--它是批量執(zhí)行器
????final?Environment?environment?=?configuration.getEnvironment();
????final?TransactionFactory?transactionFactory?=?getTransactionFactoryFromEnvironment(environment);
????tx?=?transactionFactory.newTransaction(environment.getDataSource(),?level,?autoCommit);
????//定義執(zhí)行器,是對(duì)statement的封裝
????final?Executor?executor?=?configuration.newExecutor(tx,?execType);
????//最后返回一個(gè)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();
??}
}
得到SqlSession對(duì)象之后就可以利用SqlSession內(nèi)部的方法進(jìn)行CRUD操作了。
注意一點(diǎn),Connection對(duì)象是在SqlSession對(duì)象創(chuàng)建之后進(jìn)行CURD操作中創(chuàng)建的。深入查找之后找到在ManagedTransaction類中找到獲取Connection對(duì)象的關(guān)鍵代碼如下:
??protected?void?openConnection()?throws?SQLException?{
????if?(log.isDebugEnabled())?{
??????log.debug("Opening?JDBC?Connection");
????}
????//dataSource?來(lái)源有三種,JndiDatasource,PooledDataSource,UnpooledDataSource,配置文件中定義
????this.connection?=?this.dataSource.getConnection();
????if?(this.level?!=?null)?{
??????this.connection.setTransactionIsolation(this.level.getLevel());
????}
??}
PooledDataSource和UnPooledDataSource的區(qū)別是PooledDataSource使用了連接池。為什么使用連接池呢?
因?yàn)閯?chuàng)建一個(gè)Connection對(duì)象的過(guò)程,在底層就相當(dāng)于和數(shù)據(jù)庫(kù)建立的通信連接,在建立通信連接的過(guò)程,消耗了非常多的時(shí)間,而往往我們建立連接后(即創(chuàng)建Connection對(duì)象后),就執(zhí)行一個(gè)簡(jiǎn)單的SQL語(yǔ)句,然后就要拋棄掉,這是一個(gè)非常大的資源浪費(fèi)!
mybatis針對(duì)這一個(gè)問(wèn)題提出的PooledDataSource使用了連接池。關(guān)于數(shù)據(jù)庫(kù)連接池的知識(shí)點(diǎn),可以自行百度,在這里就不擴(kuò)展介紹了。
來(lái)源:blog.csdn.net/u013412772/article/
details/73648537
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?面試題?資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!

面試題】即可獲取