1. 程式人生 > >SSH學習(十)Hibernate常用API詳解及原始碼分析

SSH學習(十)Hibernate常用API詳解及原始碼分析

學習Java的同學注意了!!! 
學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎掃描左欄的二維碼加入微信公眾號(codehzm)
我們一起學java

新接觸一個框架的目的就是想利用這個框架來為我們做一些工作,或者是讓他來簡化我們的工作,利用這個框架無非就是要利用這個框架所給我們提供的API去操作我們的資料,所以利用一個框架的好壞很大一部分取決於你對這個框架API的理解程度,所以在此篇部落格中我們就一起來看一下hibernate 的API和配置檔案的相信情況。下面我們一一來看一下:
一、使用SchemaExport自動建立資料庫表
我相信在此之前大家應該都是用最原始的方法:SQL建立資料庫相關的表,然後再Java寫對映寫配置檔案.現在我們學習了hibernate以後就可以用一些偷懶的方式了,我們可以使用SchemaExport自動建立資料庫,建立根據你的物件建立資料庫表。下面我們來看一下具體操作:
首先當然要建好POJO object, XML Mapping File(也可以使用工具根據POJO class建立),配置檔案(hibernate.cfg.xml),然後執行下面的Java程式碼
import org.hibernate.cfg.Configuration;   
import org.hibernate.tool.hbm2ddl.SchemaExport;  
public class SchemaUtil {  
    public static void main(String[] args) {  
        Configuration cfg = new Configuration().configure();  
        SchemaExport schemaExport= new SchemaExport(cfg);  
        schemaExport.create(false, true);  
    }  
} 

再看看資料庫,表是不是已經幫你建好了,對於我這樣不熟悉資料庫的人真是太方便了。
二、使用Hibernate操作資料庫需要七個步驟
(1)讀取並解析配置檔案
Configuration conf = new Configuration().configure();
(2)讀取並解析對映資訊,建立SessionFactory
SessionFactory sf = conf.buildSessionFactory();
(3)開啟Session
Session session = sf.openSession();
(4)開始一個事務(增刪改操作必須,查詢操作可選)
Transaction tx = session.beginTransaction();
(5)資料庫操作
session.save(user);//或其它操作
(6)提交事務(回滾事務)
tx.commit();(tx.rollback();)
(7)關閉session
session.close();
下面我們來詳細看一下這七大步驟的API:
Configuration:負責管理Hibernate的配置資訊,這些配置資訊都是從配置檔案hibernate.cfg.xml或者Hibernate.properties讀取的,當然也可以自定義檔名稱,只要在例項化Configuration的時候指定具體的路徑就可以了;他為什麼會自動載入hibernate.cfg.xml檔案的呢?我們看一下configure原始碼就一目瞭然了
public Configuration configure() throws HibernateException {  
	configure( "/hibernate.cfg.xml" );  
	return this;  
}
從這裡我們可以看出,在hibernate原始碼中,他就是預設的載入hibernate.cfg.xml,當然你也可以指定載入配置檔案,Configuration提供了相應的方法:
public Configuration configure(String resource)
public Configuration configure(URL url)
public Configuration configure(File configFile)

SessionFactiory:Configuration的例項會根據當前的配置資訊,構造SessionFactory例項。SessionFactory是執行緒安全的,一般情況下一個應用中一個數據庫共享一個SessionFactory例項。

 Hibernate的SessionFactory介面提供Session類的例項,Session類用於完成對資料庫的操作。由於SessionFactory例項是執行緒安全的(而Session例項不是執行緒安全的),所以每個操作都可以共用同一個SessionFactory來獲取Session。
Hibernate配置檔案分為兩種格式,一種是xml格式的配置檔案,另一種是Java屬性檔案格式的配置檔案,因此構建SessionFactory也有兩種方法,下面分別介紹。
1 . 從XML檔案讀取配置資訊構建SessionFactory
從XML檔案讀取配置資訊構建SessionFactory的具體步驟如下。
(1)建立一個Configuration物件,並通過該物件的configure()方法載入Hibernate配置檔案,程式碼如下。
Configuration config = new Configuration().configure();
(2)完成配置檔案和對映檔案的載入後,將得到一個包括所有Hibernate執行期引數的Configuration例項,通過Configuration例項的buildSessionFactory()方法可以構建一個惟一的SessionFactory,程式碼如下。
SessionFactory sessionFactory = config.buildSessionFactory();
構建SessionFactory要放在靜態程式碼塊中,因為它只在該類被載入時執行一次。
2  從Java屬性檔案讀取配置資訊構建SessionFactory
從Java屬性檔案讀取配置資訊構建SessionFactory的具體步驟如下。
(1)建立一個Configuration物件,此時Hibernate會預設載入classpath中的配置檔案hibernate.properties,程式碼如下。
Configuration config = new Configuration();
(2)由於在配置檔案中缺少相應的配置對映檔案的資訊,所以此處需要通過編碼方式載入,這可以通過Configuration物件的
addClass()方法實現,具體程式碼如下。
config.addClass(BranchForm.class);
addClass()方法用於載入實體類。
(3)完成配置檔案和對映檔案的載入後,將得到一個包括所有Hibernate執行期引數的Configuration例項,通過Configuration例項
的buildSessionFactory()方法可以構建一個惟一的SessionFactory,程式碼如下。
SessionFactory sessionFactory = config.buildSessionFactory();
Session:一般的持久化方法(CRUD)都是通過Session來呼叫的,Session是非執行緒安全的。
Session的建立與關閉 :Session是一個輕量級物件,通常將每個Session例項和一個數據庫事務繫結,也就是每執行一個數據庫事務,都應該先建立一個新的Session例項,在使用Session後,還需要關閉Session。
Session的建立
建立SessionFactory後,就可以通過SessionFactory建立Session例項,通過SessionFactory建立Session例項的程式碼如下。
Session session=sessionFactory.openSession();
建立Session後,就可以通過建立的Session進行持久化操作了。
Session的關閉
在建立Session例項後,不論是否執行事務,最後都需要關閉Session例項,釋放Session例項佔用的資源。
關閉Session例項的程式碼如下:
session.close();
 
下面來看一下:getCurrentSession 與 openSession() 的區別
1.getCurrentSession建立的session會和繫結到當前執行緒,而openSession不會。
2 getCurrentSession建立的執行緒會在事務回滾或事物提交後自動關閉,而openSession必須手動關閉
3. getCurrentSession () 使用當前的session,openSession() 重新建立一個新的session
這裡getCurrentSession本地事務(本地事務:jdbc)時 要在配置檔案裡進行如下設定
 * 如果使用的是本地事務(jdbc事務)
 <property name="hibernate.current_session_context_class">thread</property>
 * 如果使用的是全域性事務(jta事務)
 <property name="hibernate.current_session_context_class">jta</property> 
openSession() 與 getCurrentSession() 有何不同和關聯呢?
在 SessionFactory 啟動的時候,Hibernate 會根據配置建立相應的 CurrentSessionContext,在 getCurrentSession() 被呼叫的時候,實際被執行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 執行時,如果當前 Session 為空,currentSession 會呼叫 SessionFactory 的 openSession。所以 getCurrentSession() 對於 Java EE 來說是更好的獲取 Session 的方法。
事務transaction:Hibernate是對JDBC的輕量級物件封裝,Hibernate本身是不具備Transaction處理功能的,Hibernate的Transaction實際上是底層的JDBC Transaction的封裝,或者是JTA Transaction的封裝,下面我們詳細的分析:
Hibernate可以配置為JDBCTransaction或者是JTATransaction,這取決於你在hibernate.properties或者hibernate.cfg.xml中的配置,如果你什麼都不配置,預設情況下使用JDBCTransaction,如果你配置為: hibernate.transaction.factory_class =net.sf.hibernate.transaction.JTATransactionFactory 
將使用JTATransaction 。
JDBCTransaction究竟是什麼東西呢?來看看原始碼就清楚了: 
Hibernate3.3.2原始碼中的類 org.hibernate.transaction;.JDBCTransaction: 


這是提交方法,看到connection().commit() 了嗎?下面就不用我多說了,這個類程式碼非常簡單易懂,通過閱讀使我們明白Hibernate的Transaction都在幹了些什麼?我現在把用Hibernate寫的例子翻譯成JDBC,大家就一目瞭然了: 
Connection conn = ...; <--- session = sf.openSession(); 
conn.setAutoCommit(false); <--- tx = session.beginTransactioin(); ... <--- ... conn.commit(); <--- tx.commit(); (對應左邊的兩句)
conn.setAutoCommit(true); conn.close(); <--- session.close(); 
public void begin() throws HibernateException {  
	...  
	try {  
		toggleAutoCommit= jdbcContext.connection().getAutoCommit();  
		if ( log.isDebugEnabled() ) {  
			log.debug("currentautocommitstatus: " + toggleAutoCommit);  
		}  
		if (toggleAutoCommit) {  
			log.debug("disabling autocommit");  
			jdbcContext.connection().setAutoCommit(false);  
		}  
	} 
	... 
}  
這是啟動Transaction的方法,看到 connection().setAutoCommit(false) 了嗎?是不是很熟悉? 再來看
public void commit() throws HibernateException {   
	...   
	try {  
		commitAndResetAutoCommit();  
		log.debug("committed JDBC Connection");  
		committed = true;  
		if ( callback ) {  
			jdbcContext.afterTransactionCompletion( true, this );  
		}  
		notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );  


	}
	...
} 
commitAndResetAutoCommit方法原始碼:
private void commitAndResetAutoCommit() throws SQLException {  
	try {  
		jdbcContext.connection().commit();  
	}  
	finally {  
		toggleAutoCommit();  
	}
}
看明白了吧,Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫無神祕可言,只不過在Hibernate中,Session開啟的時候,就會自動conn.setAutoCommit(false),不像一般的JDBC,預設都是true,所以你最後不寫commit也沒有關係,由於Hibernate已經把AutoCommit給關掉了,所以用Hibernate的時候,你在程式中不寫Transaction的話,資料庫根本就沒有反應。