Hibernate初始化過程
如果要寫一個Hibernate的Hello World程式,我們大概需要做這麼幾件事情:
1. 建立對應表的實體類並編寫其到對應表的對映檔案;
2. 編寫hibernate的配置檔案,預設是hibernate.cfg.xml,你當然可以用其它的名字,只需自己傳遞配置檔名稱給Configure就好;
3. 編寫具體操作資料庫的dataservice;
對於第三步,又可分為:建立SessionFactory;獲取session;通過session物件執行資料庫操作;等等
如Hibernate中一個Tutorial:
- public class HibernateUtil {
- private
- static {
- try {
- // Create the SessionFactory from hibernate.cfg.xml
- sessionFactory = new Configuration().configure().buildSessionFactory();
- } catch (Throwable ex) {
- // Make sure you log the exception, as it might be swallowed
- System.err.println("Initial SessionFactory creation failed."
- throw new ExceptionInInitializerError(ex);
- }
- }
- public static SessionFactory getSessionFactory() {
- return sessionFactory;
- }
- }
這個工具類中建立了sessionFactory物件,其它的資料服務類都可以使用。(我們應該知道SessionFactory是一個比較“重”的物件,建立的開銷過大,一般一個數據庫使用一個SessionFactory)
- protected void doGet(HttpServletRequest request,
- throws ServletException, IOException {
- ……
- try {
- // Begin unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().beginTransaction(); //通過SessionFactory獲取session並開始事務
- ……
- createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate)); //資料庫操作
- ……
- } catch (Exception ex) {
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().rollback(); //異常則回滾事務
- throw new ServletException(ex);
- }
- }
- protected void createAndStoreEvent(String title, Date theDate) {
- Event theEvent = new Event();
- theEvent.setTitle(title);
- theEvent.setDate(theDate);
- HibernateUtil.getSessionFactory()
- .getCurrentSession().save(theEvent); //通過session的save方法儲存物件到資料庫
- }
這裡無關的程式碼我都給刪除了,可以看到其流程跟前面提到的第三部基本是一致的。下面便來分析一下這段程式碼的背後,Hibernate做了什麼?整個過程的時序圖如下:
Configuration建構函式中會建立Mapping和SettingsFactory,它們會在後續用於解析SessionFactory的配置屬性,之後呼叫Configuration的configure()函式,在configure()中將通過doConfigure函式完成對Hibernate配置檔案hibernate.cfg.xml的解析。
解析完配置檔案後,便可以通過BuildSessionFactory()建立SessionFactory了,在這個函式中,首先在buildSettings()中通過之前建立的SettingsFactory建立各種屬性設定,之後通過new一個SessionFactoryImpl完成SessionFactory的建立。
有了SessionFactory,Client端便可以通過它獲取Session,並執行各種資料庫操作了。
從getCurrentSession()函式中可以知道,Session是從CurrentSessionContext獲取的,那麼CurrentSessionContext是怎麼來的?可以來看看buildCurrentSessionContext函式的實現:
- private CurrentSessionContext buildCurrentSessionContext() {
- String impl = this.properties.getProperty("hibernate.current_session_context_class");
- if ((impl == null) && (this.transactionManager != null)) {
- impl = "jta";
- }
- if (impl == null) {
- return null;
- }
- if ("jta".equals(impl)) {
- if (this.settings.getTransactionFactory().areCallbacksLocalToHibernateTransactions()) {
- log.warn("JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()");
- }
- return new JTASessionContext(this);
- }
- if ("thread".equals(impl)) {
- return new ThreadLocalSessionContext(this);
- }
- if ("managed".equals(impl)) {
- return new ManagedSessionContext(this);
- }
- try
- {
- Class implClass = ReflectHelper.classForName(impl);
- return ((CurrentSessionContext)implClass.getConstructor(new Class[] { SessionFactoryImplementor.class }).newInstance(new Object[] { this }));
- }
- catch (Throwable t)
- {
- log.error("Unable to construct current session context [" + impl + "]", t); }
- return null;
- }
可以看到,Hibernate會根據“hibernate.current_session_context_class”的屬性值來決定建立哪一類上下文會話(Contextual Session),可以看看Hibernate的參考文件對這個屬性的解釋:“為當前Session的作用域選擇一個自定義策略,可選的會話上下文有:jta|thread|managed|自定義類”。我們以thread型別會話來分析,ThreadLocalSessionContext會藉助ThreadLocal實現每個執行緒有一個對應的session。
有了CurrentSessionContext,來看看getCurrentSession函式:
- public Session getCurrentSession()
- throws HibernateException
- {
- if (this.currentSessionContext == null) {
- throw new HibernateException("No CurrentSessionContext configured!");
- }
- return this.currentSessionContext.currentSession();
- }
它會通過CurrentSessionContext的currentSession()函式來獲取session,針對ThreadLocalSessionContext,也即:
- public final org.hibernate.classic.Session currentSession()
- throws HibernateException
- {
- org.hibernate.classic.Session current = existingSession(this.factory); //檢查ThreadLocal中是否有session,有則直接返回,保證一個執行緒一個session
- if (current == null) {
- current = buildOrObtainSession(); //通過openSession建立一個新的session
- current.getTransaction().registerSynchronization(buildCleanupSynch());
- if (needsWrapping(current)) {
- current = wrap(current); //包裝session,主要是針對代理的情況
- }
- doBind(current, this.factory); //當然是把新建的session繫結到ThreadLocal中啦
- }
- return current;
- }
到了這裡,基本就完成了,後面就是通過session執行各種資料操作以及事務管理之類的了。
歡迎關注公眾號: