上一節我們講到了Hibernate的測試,并且給出了測試代碼,剛開始看見這個測試代碼的同學估計是一頭霧水把,是以這一節我們來講一下測試代碼。
本節主要内容:
- Configuration
- SessionFactory
- Session
首先我們再來看一下上一節的測試代碼:
//加載配置檔案
Configuration config = new Configuration().configure();
//根據配置檔案建立會話工廠
SessionFactory factory = config.buildSessionFactory();
//根據會話工廠建立會話
Session session = factory.getCurrentSession();
//建立一個事物對象
Transaction tx = session.beginTransaction();
//new 一個學生對象
Student student = new Student("小三‰");
//将對象持久化到資料表中
session.save(student);
//送出事務
tx.commit();
//關閉會話
session.close();
//關閉工廠
factory.close();
Configuration:
首先是Configuration,它是用來解析我們配置的一個類,它加載配置的時候使用了這段代碼:
Configuration config = new Configuration().configure();
看到這段代碼的第一感覺是為啥還要在後面加一個方法,直接new一個Configuration不就行了嗎,為啥還加了一個configure方法。
我們來打開源碼瞧瞧:
public Configuration configure() throws HibernateException {
return configure( StandardServiceRegistryBuilder.DEFAULT_CFG_RESOURCE_NAME );
}
看到了沒,configure方法傳回了一個有參的方法,且參數為:
StandardServiceRegistryBuilder.DEFAULT_CFG_RESOURCE_NAME
我們跟進去會發現這個參數是StandardServiceRegistryBuilder類定義的一個名字叫hibernate.config.xml的成員變量。
是以我們可以得出一個結論:如果你采用以下段代碼來加載配置檔案那麼你的主配置檔案名字必須叫作hibernate.config.xml
Configuration config = new Configuration().configure();
當然你也可以自定義,例如這樣随意指定:
Configuration config = new Configuration().configure("xxx.xml');
但是我們建議使用hibernate指定的配置檔案名。
這裡我們隻介紹這些,是以對于Configuration我們僅需要知道它是用來加載配置檔案即可,以後具體分析,現在我們先将hibernate的基本原理講明白就好。
SessionFactory:
我們知道主配置檔案裡面最外面的便是
<hibernate-configuration>
接下來就是
<session-factory>
,是以通過Configuration執行個體對象的buildSessionFactory可以根據我們的配置檔案建立會話工廠。值得注意的是SessionFactory是一個重量級的元件,是一個單例的,線程安全的。按理說單例對象一定是被共享的是線程不安全的,我們将它的實作類SessionFactoryimpl打開看發現它的大多數的成員變量都是final的,是以它是線程安全的。
一般SessionFactory執行個體都不進行關閉(開銷太大),而是在應用結束的時候自動将其銷毀。
Session:
session由SessionFactory的getCurrentSession()或者openSession()進行建立,在web應用中,每當有一個使用者通路時就會為這個使用者建立一個Session,是以Session是多例的,它包含了許多非final變量,對同一個使用者可能會産生多個事務,若多事務同時對Session的同一個變量進行通路就會引起并發的問題進而導緻線程不安全。
上面提到了Session的兩種擷取方式,接下來我們說一下兩種方式的差別:
getCurrentSession無論執行多少次隻要是在同一個線程中它擷取到的都是同一個Session對象,使用這個方法擷取的Session對象是由ThreadLocal變量存儲的(ThreadLocal在我的多線程文章裡面有介紹),它的底層是一個Map,key就為線程的名字,是以用這個方法建立的Session對象是線程唯一的。
它建立Session對象的時候會進行判斷,判斷線程是是否已經存在session,如果不存在便進行建立。以下便是它的實作源碼(有源碼有真相):
public Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}
如果不存在那麼久進行建立,底層也是通過openSession建立:
protected Session buildOrObtainSession() {
return baseSessionBuilder()
.autoClose( isAutoCloseEnabled() )
.connectionReleaseMode( getConnectionReleaseMode() )
.flushBeforeCompletion( isAutoFlushEnabled() )
.openSession();//就是這裡
}
具體的邏輯太多,不友善一直跟下去,是以有興趣的可以一直跟下去。
openSession進行建立時,每執行一次便會建立一個Session對象,是以需要我們手動關閉,而getCurrentSession不用關閉。
getCurrentSession獲得的Session對象不用注冊便能夠使用而openSession無需注冊。
getCurrentSession獲得的session對象一定要在事務裡面指定,而openSession獲得的對象可以不在事務裡面執行。
以上就是對兩種擷取方式的簡單總結。