IBM WebSphere 開發者技術期刊: 開發用於 WebSphere Application Server 的 Hibernate 應用程序

級別: 中級

Sunil Patil[email protected]
軟件工程師, IBM
2004 年 11 月

本文一步一步地指導我們在創建 Hibernate 應用程序的時候,使用 Websphere® Application Server 的連接以及事務管理。

獲取本文中所使用的產品以及工具
如果您是 developerWorks 的訂戶,就會擁有單用戶的使用許可,以使用 WebSphere Application Server 和其他 DB2®, Lotus®, Rational®, Tivoli® 產品,包括基於 Eclipse 的 WebSphere Studio IDE -- 來開發、測試、評估以及演示您的應用程序。如果您不是 developerWorks 的訂戶,現在可以預定

前言
Hibernate 是一個流行的開源對象關係映射工具,運行在 Java 環境中。用基於 SQL 的模式可以把數據呈現從對象模型映射到關係數據模型,對象關係映射引用了這項技術。這也就意味着在與數據庫交互時,Hibernate 提供了多級抽象方法。

Hibernate 非常靈活,並且支持多種使用方法。在一種情況下,使用 Hibernate API 的最小子集,它僅僅可以用來同數據庫交互,在這種情況下,應用程序必須爲自己提供連接並且管理自己的事務;例如 WebSphere Application Server 這樣的中間件可以實現這些功能。在另一種情況下,即使您沒有運行中間件,您也可以使用完整版本的 Hibernate,在這種情況下,您向 Hibernate 提供數據庫配置信息,這時它不僅僅爲您創建和管理連接,而且還可以通過把事務委派到底層數據庫來管理它們。

使用 Hibernate,您需要創建 Java 類來描述數據庫中的表,並且將類中的實例變量映射到數據庫中的列。這時,正如您所想的那樣,您可以調用 Hibernate 的方法來選擇、插入、更新和刪除底層表中的記錄,而不是自己創建和執行查詢。

Hibernate 架構有三個主要的組件:

  • 連接管理
    因爲在於數據庫交互時,打開以及關閉數據庫的開銷是非常昂貴的,您應該將您的數據庫連接放在池中並且能重新使用。
  • 事務管理
    在一個批處理中執行多個查詢時,會用到事務;結果可能是所有的查詢都成功,或者是所有的都失敗。
  • 對象關係映射
    在這部分中,特定的 Java 對象 Hibernate 來插入或者更新數據;例如,當您傳遞一個對象的實例到 Session.save() 方法時,Hibernate 將讀取這個對象實例變量的狀態,並且創建和執行必須的查詢。在選擇查詢的情況下,將返回描述結果集的對象。

 

Hibernate 非常的靈活,並且提供多種途徑來使用這些組件:

  • “Lite”架構
    當您僅僅想要使用 Hibernate 對象關係映射組件時使用。在這種情況下,您需要自己實現連接以及事務管理,例如,用 WebSphere Application Server。
  • “Full cream”架構
    當您想要應用所有三個 Hibernate 組件時使用。Hibernate 可以爲您管理連接,但是您必須通過 XML 配置提供如驅動程序類名、用戶名、密碼以及其他一些連接信息。當管理事務的時候,您可以在 Hibernate 對象上調用開始、提交以及回滾方法。

 

當涉及到對象關係映射時,Hibernate 是非常好的工具,但是在連接管理以及事務管理方面,它卻缺乏必要的性能和能力。幸運的是,我們可以將 Hibernate 的對象關係映射與 WebSphere Application Server 的連接和事務管理結合起來,創建強大的應用程序。

本文假設您已經具備了 Hibernate、Enterprise JavaBean(EJB)組件以及 Struts 的基礎知識。如果您剛剛接觸 Hibernate,請參考沒有對象的對象關係映射,這篇文章討論了怎麼用 Hibernate 來實現簡單的插入、更新以及刪除操作。

關於示例應用程序
爲了實現本文的目的,我們將創建一個簡單的 Struts Web 應用程序,它允許用戶來向 Contact 表中插入新的記錄。我們的示例應用程序將使用 AddContact.jsp 來接收用戶的輸入。AddContactAction.java 是活動類,它將用 Hibernate 來插入一個新的聯繫人到數據庫中去。如果聯繫人成功插入,這時用戶被重定向到 success.jsp,這個頁面將顯示最新插入聯繫人的詳細信息。如果遇到了問題,將顯示 failure.jsp 頁面。

圖 1. 實例應用程序插入頁面
圖 1. 實例應用程序插入頁面

使用 Hibernate
開始時,我們將創建實例應用程序,這個程序使用 Hibernate 自己的連接池以及事務管理(同樣請參考 "full cream" 架構):

  1. 創建一個 Struts 應用程序,既可以使用 WebSphere Studio Application Developer(以後稱作 Application Developer),也可以通過導入包含在 Struts 發佈裏面的 struts-blank.war 來實現。爲示例應用程序創建必須的 JSP 頁面和活動類,您也可以從下載文件 sample1.zip中導入。
  2. Hibernate 網站上下載 Hibernate 二進制版本:複製 hibernate2.jar 以及 JAR 庫文件(dom4j.jar,xmlapi.jar及其他),到 WEB-INF/lib 目錄中。
  3. 創建 Contact.java 類,並且爲 Contact 中的每一列創建一個成員變量。每個字段都應該創建 Getter 和 Setter 方法。同樣需要創建 contact.hbm.xml 文件,這個文件將把 Contact.java 的實例變量映射到 Contact 表的列上。
  4. 在您的源代碼根目錄中創建 hibernate.cfg.xml。這個文件爲 Hibernate 保存應用程序級別的配置信息,正如清單 1 所示的那樣。

    清單 1

     <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class"> COM.ibm.db2.jdbc.app.DB2Driver</property> <property name="hibernate.connection.url">jdbc:db2:SAMPLE</property> <property name="hibernate.connection.username">db2admin</property> <property name="hibernate.connection.password">db2admin</property> <property name="hibernate.connection.pool_size">10</property> <property name="show_sql">true</property> <property name="dialect">net.sf.hibernate.dialect.DB2Dialect</property> <property name="hibernate.hbm2ddl.auto">create-drop</property> <!-- Mapping files --> <mapping resource="contact.hbm.xml"/> </session-factory> </hibernate-configuration> 
    在這個文件中:
    • 您將注意到如果需要用純 JDBC 打開連接,我們必須在這裏指定同樣的信息:
      • hibernate.connection.driver_class 是我們決定要使用的 JDBC 驅動器類。
      • hibernate.connection.passwordhibernate.connection.username 是數據庫的信任狀。
      • hibernate.connection.url 是用來連接數據庫的 JDBC URL。
    • dialect 屬性決定了在獲取查詢的時候用到的專用語。Hibernate 支持所有流行的關係數據庫管理系統(RDBMS)的專用語,例如 DB2 或者 Oracle™。因此,例如,如果您在開發過程中使用 Oracle,並且在生產的時候想要轉移到 DB2 上,僅僅需要改變 hibernate.cfg.xml 文件。
    • hibernate.hbm2ddl.auto 可以用來指示 Hibernate,當我們初始化它時,從映射文件爲我們的應用程序創建 DDL,並且在數據庫中創建這些表和序列。create-drop 的值意味着當 Hibernate 關閉時將刪除由 Hibernate 創建的表。
    • contact.hbm.xml 是 Contact 標的映射文件。如果您有多個表,您即可以創建多個映射文件(每個表一個),以及添加多個 <mapping> 元素,也可以在一個文件中創建多個映射,並且添加一個 <mapping> 元素。
    在我們的應用程序接下來的部分,我們將讀取由用戶輸入的值,並且向 Contact 表中插入一條新的記錄。AddContactAction.java 類執行這個方法:

    清單 2

     SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.openSession();  //Create new instance of Contact and set values in it by reading them from form object Contact contact = new Contact(); contact.setFirstName((String)contactForm.get("firstName")); session.save(contact);  // Actual contact insertion will happen at this step session.flush(); session.close(); 
  5. 通過指定 new Configuration().configure().buildSessionFactory() 來初始化 Hibernate。Hibernate 會設法在根目錄中定位 /hibernate.cfg.xml,如果發現的話就會加載它,並且提供給我們一個到 SessionFactory 的引用,我們可以用它來同 Hibernate 交互。會話(session)指示了一個邏輯上的交互;在一個會話中您可以激活多個查詢。
  6. 爲了插入聯繫人,創建一個 Contact 類的新實例。通過調用 setter 方法來用輸入的聯繫人信息設定各個字段的值,這時把這個聯繫人對象傳遞給 Hibernate,調用 session.save() 來保存。Hibernate 將負責生成和執行插入查詢。當聯繫人被插入時,Contact 類中 ID 字段的值將和 Contact 表中最新插入的記錄主鍵值相同。如果出現任何問題,Hibernate 將會拋出異常。

 

請您確認複製 JDBC 驅動器 JAR 文件到 WEB-INF/lib 目錄中,否則將出現異常。

爲了嘗試這個示例,請下載 sample1.zip 文件。

通過 WebSphere Application Server 連接池來使用 Hibernate
基於上面我們已經完成的內容,我們可以改進示例聯繫人管理應用程序,它包含 WebSphere Application Server 一些關鍵特性的使用。

首先,在開發上面的應用程序時,我們用了 Hibernate 中的連接池。這個連接池算法有點不成熟,並且並不能用在生產環境裏面,您應該考慮使用第三方的連接池比如 Apache's DBCP。我們將更改我們的應用程序,來使用 WebSphere Application Server 的連接池:

  1. 爲了使用 WebSphere Application Server 連接池,需要創建一個 JDBC 數據源並設定 JNDI 名,比如 jdbc/ds1。(參考 WebSphere Application Servers Information Center 來查看配置數據源的細節。)在 hibernate.cfg.xml 文件中,移除 hibernate.connection.driver_class、URL 以及其他屬性,並且添加這一行:

    <property name="hibernate.connection.datasource">jdbc/ds1</property>

  2. AddContactAction.java 執行方法中,我們每次都創建一個 SessionFactory 對象,但這並不是一個好主意,因爲使用 SessionFactory 創建由所有應用程序線程共享的線程安全對象是非常昂貴的。我們應該僅僅在應用程序啓動的時候創建 SessionFactory 對象。我們可以創建一個 SessionContextListener 類並且通過配置 SessionFactory 來實現它,也可以通過更好的辦法,創建一個配置 SessionFactory 的 Struts 插件來實現,如清單 3 所示。

    清單 3

     public class HibernateStrutsPlugIn implements PlugIn{  	public void init(ActionServlet arg0, ModuleConfig arg1) 	throws ServletException { 		try { 			SessionFactory sessionFactory = new  			Configuration().configure().buildSessionFactory(); 		} catch (HibernateException e) { 				System.out.println("Error in  			configuring SessionFactory"); 				e.printStackTrace();  		} 	}  	// Other methods } 
    爲了創建 Structs 插件:
    1. 創建實現 PlugIn 接口的 HibernateStrutsPlugin 類。在這個類的 init() 方法中,調用 Configuration().configure().buildSessionFactory() 方法。
    2. struts-config.xml 中爲新的插件類添加條目,既可以通過使用 Application Developer(圖 2)來實現,也可以通過手動編輯 struts-config.xml 並且在末端添加清單 4 中的那一行。

      圖 2. Struts Configuration File Editor
      圖 2. Struts Configuration File Editor

      清單 4

       <struts-config> <plug-in className="com.sample.util.HibernateStrutsPlugIn"> </plug-in> </struts-config> 
    3. 我們將對 hibernate.cfg.xml 文件做一些修改,使重新使用 SessionFactory 更加得容易。通過添加下面的條目,我們告訴 Hibernate 當我們調用 Configuration().configure() 時,它應該創建 SessionFactory 的一個實例,並且將它同 HibernateSessionFactory 綁定在一起。當把 SessionFactory 綁定到 JNDI 時,Hibernate 將在初始化 InitialContext 時使用 hibernate.jndi.url 以及 hibernate.jndi.class 的值。如果這些值沒有指定,將使用默認的 InitialContext 值。

      <property name="hibernate.session_factory_name"> HibernateSessionFactory</property>

  3. 我們將更新我們的應用程序並且添加兩個新表:Phone 和 Address。同樣我們必須更改 AddContact.jsp 來添加這兩個新的輸入。我們想要應用程序完成的功能是,如果用戶指定了電話號碼或者是地址,一條新的記錄將添加到合適的表中。另外,所有這些都應該是一個事務的一部分,也就是說,在添加聯繫人和電話號碼以後,在添加地址是突然出現了錯誤,這時所有與該事務關聯活動都應該回滾,並且將向用戶顯示出錯頁面。

    創建 Address.javaPhone.java 來描述 Address 以及 Phone 表,並且通過 address.hbm.xmlcontact.hbm.xml 來映射。在 hibernate.cfg.xml 中爲這兩個文件添加 <mapping> 元素。

    清單 5

     try { 	Context ctx = new InitialContext(); 	Object obj = ctx.lookup("HibernateSessionFactory"); 	sessionFactory = (SessionFactory) obj; } catch (NamingException e) { 		e.printStackTrace(); } session = sessionFactory.openSession(); Transaction contactInsertTransaction =  try{ 	session.beginTransaction(); 	//Create new instance of Contact and set values in it by reading them from form object 	Contact contact = new Contact(); 	// Prepare contact object 	session.save(contact);  	//Check if value for phone is not null if yes then insert it in db 	String phoneStr = (String) contactForm.get("phone"); 	if (phoneStr.length() != 0) { 		//Code for inserting new phone here } 	 	//If you uncomment this line you will find out contact gets inserted  	//even if there is some problem afterwards/ 	/*	if(true) 		throw new RuntimeException("Test transaction exception"); */ 	// Code for inserting new Address 	contactInsertTransaction.commit(); } catch (HibernateException e) { 	contactInsertTransaction.rollback(); 	return mapping.findForward("failure"); }finally{ 	session.flush(); session.close(); } 

 

由於這些改變,我們已經:

  • 改變了執行方法,從 JNDI 中獲得 SessionFactory 引用。
  • 改變了執行方法,使聯繫人、電話和地址的插入操作運行在 Hibernate 自己管理的事務中。

 

如果所有的東西都運行正常,我們將在關閉會話以前提交事務。如果出現了任何問題,我們將調用事務對象中的 rollback() 方法。

利用 WebSphere Application Server 事務管理來使用 Hibernate
第二,現在我們想要更改我們的應用程序,使非 Web 客戶端也可以訪問。爲了實現這個目標,我們將創建一個無狀態會話 Bean,並且對外公開一個 addContact() 方法,並且,把所有的業務邏輯從 AddContactAction 類中移動到 ContactBean.java 中。

因爲移動到無狀態會話 Bean 的緣故,我們必須初始化 Hibernate 的方法。對於綁定對象來說,JNDI 一旦可用,就應該初始化 Hibernate,並且應該把 SessionFactory 對象綁定到 JNDI。當應用程序服務器已經完成了啓動以後,WebSphere Application Server 就會允許我們註冊回滾通知。

  1. 在 Application Developer 中創建一個名爲 sampleListener 的 Java 項目,並且在這個項目中,創建 HibernateJNDIListener.java,如清單 6 所示。

    清單 6

     public class HibernateJNDIListener implements CustomService,  NotificationListener { 	public void initialize(Properties arg0) throws  	Exception { 		NotificationFilterSupport filter = new  	NotificationFilterSupport(); 		filter.enableType(NotificationConstants.TYPE_J2EE 	_STATE_RUNNING); 		ObjectName target = new  		ObjectName("WebSphere:*,type=Server"); 		AdminServiceFactory.getAdminService().addNotificationListenerExtended(target,this,filter,null); 	}  	public void handleNotification(Notification arg0, Object arg1) { 	try { 		TestConfiguration.init(); 		System.out.println("Hibernate configured sucessfully"); 		} catch (HibernateException e) { 			e.printStackTrace(); 		} 	} } 
  2. 使用 WebSphere Application Server 中的管理控制檯,創建並且註冊名爲(HibernateJNDIListener)的類,這個類實現了 CustomService 接口。

    圖 3. WebSphere 管理控制檯
    圖 3. WebSphere 管理控制檯

    當應用程序服務器或者節點啓動時,這個類也將啓動,並且 initialize() 方法中獲取控制權,該方法將註冊一個 HibernateJNDIListner 實例用來接收 TYPE_J2EE_STATE_RUNNING 通知。HibernateJNDIListner 類實現了 NotificationListener 來指明它現在已經可以接收通知。當綁定對象可以使用 WebSphere Application Server 中的 JNDI 時,它將調用 HibernateJNDIListener 中的 Notification() 處理方法。在這個方法中,我們將初始化 SessionFactory 並且將其在 JNDI 中綁定。

  3. 如果要使其工作正常,所有的 Hibernate 映射類和 .hbm.xml 文件需要與 sampleListener.jar 在同一個級別上。創建一個新的 Java 項目,sampleDB,並且將 com.sample.db Java 文件移動到這個項目中,.hbm.xmlhibernate.cfg.xml 文件也是一樣處理。
  4. sampleDB.jarsampleListener.jar 複製到 appserver/lib 中,以下文件也一樣處理:
    • hibernate2.jar
    • do dom4j.jar
    • cgilib-full-2.0.1.jar
    • xml-apis.jar
    • commons-collection-2.1.jar
    • ecache-0.7.jar.
  5. 啓動 WebSphere Application Server,並且檢查 systemout.log,找到表明 Hibernate 配置成功的消息。
  6. 現在,我們將使用 WebSphere Application Server 事務管理來更改我們的應用程序,這樣我們添加的聯繫人信息將變爲現有事務的一部分。例如,在插入新的聯繫人信息以後,我們可能想要發送一個確認信給用戶。如果我們已經有了一個消息驅動 Bean 來發送 e-mail,這個附加的信息應該是一個事務中的一部分。我們可以通過在 hibernate.cfg.xml 中添加這個條目,來與 WebSphere Application Server 事務管理結合在一起。

    <property name="net.sf.hibernate.transaction.JTATransactionFactory"> net.sf.hibernate.transaction.WebSphereTransactionManagerLookup</property>

 

在這個例子中,我們不會手動指定事務的邊界。WebSphere Application Server 在它調用 addContact() 方法時將開始一個新的事務。如果該方法正常完成了執行操作,沒有拋出任何錯誤,這時事務將被提交,否則將回滾。

結束語
當使用 Hibernate 的時候,如果您沒有應用服務器,就應該使用完整的架構;例如,您有一個要與數據庫交互的 SWING 應用程序。在這種情況下 Hibernate 可以節省您很多低級別的消耗。然而,如果您運行了中間件,您可以使用 WebSphere Application Server 中配置的數據源,因爲它爲性能做了優化,並且您可以在部屬的多個應用程序中共享同樣的連接池。

對於企業應用程序,您可能有數據庫、消息系統以及各種 EIS 系統,並且您想所有需要的系統都可以運行在一個事務中。在這種情況下,您應該在 Hibernate 中使用 WebSphere Application Server 提供的 Java Transaction API(JTA);當 Hibernate 管理事務時,它通過把調用委託給底層數據庫來實現這一點,但是當它是跨系統而不是數據庫時,將會出現問題。

參考資料

下載
Name Size Download method
sample1.zip 5.0 MB FTP|HTTP
sample2.zip 5.0 MB FTP|HTTP
sample3.zip 5.6 MB FTP|HTTP
關於作者
Sunil Patil 是一個在 IBM 印度公司 Lotus 部門的一位軟件工程師。他具有四年的 J2EE 經驗。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章