程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> 在Java SE中使用Hibernate處理數據

在Java SE中使用Hibernate處理數據

編輯:JAVA編程入門知識

  如今,Hibernate正在迅速成為非常流行的(如果不是最流行的)J2EE O/R映射程序/數據集成框架。它為開發人員提供了處理企業中的關系數據庫的整潔、簡明且強大的工具。但如果外部需要訪問這些已被包裝在J2EE Web應用程序中的實體又該怎麼辦?是開發獨立但相同的實體來訪問數據,還是另外編寫Web組件來管理內部訪問的數據?

  在某種程度上,這些問題是一定會發生的,對於我來說,當我的公司要向數據庫中加載來自多個供應商的多種文件格式的記錄時,就出現了這些問題。我考慮過以前常用的方法:用shell和SQL腳本(甚至存儲過程)來加載數據。但由於數據模型過於復雜,我決定盡量利用現有的實體、Spring DAO以及Web應用程序之外的服務,並開發定制的J2SE命令行數據加載程序。

  重要問題:我們是否應該這樣做?

  目前與Hibernate有關的大部分文檔和例子都基於在容器中使用Hibernate。無論Hibernate是用於Web應用程序還是內部的“胖應用程序”,總是涉及到一個容器。這樣做是有原因:容器支持各種特性,比如事務處理、線程和安全性。現在,要開發中型和企業應用程序,有一些工具是必需的。但當我們需要在容器外部訪問實體對象時要怎麼做?是使用現有的基礎架構和代碼呢,還是從另一種角度甚至還可能使用另一種語言去解決問題?當然,這個問題沒有正確答案,在本文余下的部分中我將解釋我所使用的方法,即,在Spring容器外重用現有的實體/POJO。

  腳本語言(如:Perl、Python、Ruby,甚至是Tcl)乍一看都有一些優點。很多時候,腳本語言可以快速開發,並易於獲得初始結果,它還可以繞過Hibernate底層的復雜性。有可能在短短數行內就連接到數據庫、選擇一些結果並將其打印到屏幕或某個日志文件中。但受數據模型的影響,事情可能(通常情況下都會)變得非常復雜。假設有一張person表,其中有一個到address表的外鍵,在插入數據時,address沒有被正確插入,這會導致person也不能被插入:這是典型的事務問題。有人可能會辯解說在腳本語言中這個問題並不難解決,就像在主應用程序中所做的那樣。但還是有問題存在:為什麼要這樣做?如果邏輯已經存在於應用程序中,為什麼還要再次進行編碼?而且這並不是唯一的問題,我們將需要復制工作和邏輯,還可能由此產生許多錯誤。

  有些人可能認為這些都不是大問題,並用自認為是最合適的工具來解決這些問題。也許您已經由於編碼之外的原因使用了某種獨立的基礎架構。也許您事先將數據上傳到獨立的數據庫中並進行充分測試,然後再將數據遷移到生產數據庫中。又或者您的數據庫維護工作已經外包出去,您只需要將文件發送給合作伙伴公司,由他們來解決這些問題。最後,可能還有許多其他原因造成您並沒有使用現有的Hibernate數據層——不管這些原因正確與否。但如果您可以並打算在應用程序之外使用現有的代碼庫,請繼續往下讀。我將介紹一些技巧,並解決一些令人頭疼的問題。

  配置

  一旦決定在容器之外使用現有的Hibernate對象,那麼首先就必須自己管理所有的配置。下文介紹的方法是使用一個獨立的Java命令行應用程序。既然已經設置了Hibernate XML配置文件,那麼您應該知道哪些參數是必需的,比如JNDI DataSource名稱、實體映射文件以及用於記錄SQL的各種屬性。如果您決定使用命令行應用程序,那就一定要解決如何分析XML文件並把它添加到新配置中的問題。分析XML文檔不是不可能的,但是這有時會帶來一些其他的小任務。因此我建議使用常規的屬性文件。屬性文件的加載非常簡單,而且從其中取值也很容易。下面的例子示范了配置Hibernate所需的最小屬性集(沒有任何實體映射)。

hibernate.dialect=net.sf.hibernate.dialect.PostgreSQLDialect
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.connection.url=jdbc:postgresql://devserver/devdb
hibernate.connection.username=dbuser
hibernate.connection.password=dbpassword
hibernate.query.substitutions yes 'Y'
  正如您所看到的,上面的屬性指定了數據庫的非標准語言,JDBC驅動類、數據庫服務器名稱、用戶名、密碼以及是否使用查詢替換。一旦定義這些屬性並保存到hibernate.properties文件(應該在類路徑下)中,就很容易加載它們並傳遞給Hibernate Configuration對象。

Properties props = new Properties();
try {
 props.load(props.getClass().getResourceAsStream("hibernate.properties"));
}catch(Exception e){
 System.out.println("Error loading hibernate "+"properties.");
 e.printStackTrace();
 System.exit(0);
}

String driver = props.getProperty("hibernate.connection." + "driver_class");
String connUrl = props.getProperty("hibernate.connection.url");
String username = props.getProperty("hibernate.connection." + "username");
String password = props.getProperty("hibernate.connection.password");

// In my examples, I use Postgres, but Hibernate
// supports virtually every popular dbms out there.
Class.forName("org.postgresql.Driver");
Connection conn = DriverManager.getConnection(connUrl, username, password);

Configuration cfg = new Configuration();
cfg.setProperties( props );
SessionFactory sessions = cfg.buildSessionFactory();
Session session = sessions.openSession(conn);
  上面這段代碼提供了一個即時可用的Hibernate Session對象。但我們依然需要解決如何使用現有的實體映射的問題。《Hibernate in Action》一書的第2.3.1節說明了如何在實體XML映射文件中進行加載,如下:

Configuration cfg = new Configuration();
cfg.addResource("hello/Message.hbm.xml");
cfg.setProperties( System.getProperties() );
SessionFactory sessions = cfg.buildSessionFactory();
  這段代碼描述了如何從hello包加載Message的實體定義。但這種方式只適用於某些情況,對大部分實體來說這樣做是乏味且容易出錯的,這些代碼必須人工維護,每次增加新的實體都要更新加載程序代碼 。真令人厭煩!有一種更容易的發現並加載這些映射的方法,可以使這些映射與.jar一樣經常保持最新。

  首先,正如在web應用程序或企業應用程序中一樣,映射文件必須保存在類路徑中,這樣Hibernate才能正常工作。這是一件好事,因為只需使用同樣的.jar文件並找到這些映射文件名。如果在類路徑中有多個.jar文件,則需要指定哪個文件包含映射。下面的代碼是尋找映射的方法之一。

String cp = System.getProperty("java.class.path");
String jarFile = null;
List hbmList = null;

String[] cparr = cp.split("\:");
for(int j=0;j<cparr.length;j++){
 // The following assumes our entities
 // are wrapped up in a jar file
 // called 'dbobjs.jar'
 if(cparr[j].indexOf("dbobjs.jar") != -1)
 jarFile=(cparr[j]);
}

if(jarFile != null){
 JarFile jar = new JarFile(new File(jarFile));
 Enumeration e = jar.entries();
 if(e.hasMoreElements()){
  hbmList = new ArrayList();
  while(e.hasMoreElements()){
   // Object comes back
   // as JarFile
   JarEntry entry = (JarEntry)e.nextElement();
   if(entry.getName().indexOf(".hbm.xml") != -1){
    hbmList.add(entry.getName());
   }
  }
 }else {
  System.out.println("Error: The entity "+ "jar dbobjs.jar was not found in " +"classpath: " + cp);
 }
}
  上面這段代碼基本上是獲取了初始化虛擬機的類路徑系統屬性,尋找包含實體映射文件的.jar文件,分析文件名,然後將文件名添到ArrayList中。當每個實體映射的全部名稱都保存到ArrayList後,就可以傳遞給如下的Hibernate Configuration對象:

Configuration cfg = new Configuration();

Iterator iterator = hbmFileNames.iterator();
while(iterator.hasNext()){
 cfg.addResource((String)iterator.next());
}
  當Hibernate Session對象設置了正確的映射後,就可以進行下一步:使用實體。

  使用Session

  關於這方面的具體內容,因為可以找到各種與Hibernate、持久化和查詢對象有關的文章、教程或者大量關於使用事務的例子,所以我不再介紹任何細節。而是考慮要用實體作什麼,以及它們是如何影響Hibernate Session對象的。能否使用現有的業務對象甚至數據訪問對象?在設置數據層時,我使用Spring以及它所提供的一些管理連接、事務和Session的類。這些對象都使用與Spring緊密集成的各種規則和關系定義在XML配置文件中。首先,DAO對象通過Spring的依賴注入(參見Bruce Tate的《Five Things I Love About Spring》)注入到服務中,然後配置服務以捕獲特定的DAO異常(在XML配置文件中),這種異常是Spring可以正確處理的。雖然我覺得將Spring集成到數據加載應用程序中的工作量很大,我還是對DAO對象進行了細微的調整。這樣,這些對象就可以在web應用程序之外使用了。

  假設我在PersonDAO中有一個用來保存person對象的方法。由於容器已經設置好了Hibernate Session,我就不能在容器外重用這一DAO方法,因為它需要使用已經存在並完全配置好的Session對象。下面的代碼是使用Spring容器所提供的Session支持後的典型PersonDAO:

import org.springframework.orm.hibernate.HibernateTemplate;
import test.pojos.Person;

public class PersonDAO extends HibernateTemplate {

 public PersonDAO(){}

 public Person save(Person aPerson) {
  if(aPerson != null) super.save(person);
  return person;
 }
}
  上面的類擴展了Spring的HibernateTemplate類,該類提供了使用Hibernate的所有基本方法。因為HibernateTemplate管理了大部分普通操作,您只需將注意力集中在特定的持續需求上。當然這裡也應該有適當的異常操作,但作為示例來說上面的代碼已經足夠了。
現在,要給Session增加在容器外使用的支持方法,只需做少量改動:

import org.springframework.orm.hibernate.HibernateTemplate;
import net.sf.hibernate.Session;
import test.pojos.Person;

public class PersonDAO extends HibernateTemplate {

 public PersonDAO(){}

 public void setExternalSessionFactory(Session aSession){
  setSessionFactory(session.getSessionFactory());
 }

 public Person save(Person aPerson) {
  if(aPerson != null) super.save(person);
  return person;
 }
}
  因為HibernateTemplate擴展了HibernateAccessor,我就可以從選中的任何Session對象設置SessionFactory。這是Spring高度靈活的設計之一,可以使代碼重用變得更容易。

  也許您現在沒有使用Spring,那麼就要采取完全不同的方法。假設您沒有使用Spring奇妙的依賴注入,那麼從JNDI中查找Session對象的代碼如下:

import net.sf.hibernate.Session;

public class PersonDAO {

 // This example assumes that there is a Hibernate
 // Session object at the following JNDI location
 // on a Tomcat 5.5 server:
 // java:/comp/env/obj/hibernateSession

 private Session session;

 public PersonDAO(){
  try {
   Context initCtx = new InitialContext();
   Context envCtx = (Context)
   initCtx.lookup("java:comp/env");
   session = (Session)envCtx.
   lookup("obj/hibernateSession");
  }catch(Exception e) {
   e.printStackTrace();
  }
 }

 public Person save(Person aPerson) {
  if(aPerson != null) session.save(person);
  return person;
 }
}
  上面的代碼依靠應用容器使Hibernate Session對象可用。在容器之外使用這些對象的最簡單的方法是再增加一個接收Session對象的構造函數:

import net.sf.hibernate.Session;

public class PersonDAO {

 // This example assumes that there is a Hibernate
 // Session object at the following JNDI location
 // on a Tomcat 5.5 server:
 // java:/comp/env/obj/hibernateSession

 private Session session;

 public PersonDAO(){
  try {
   Context initCtx = new InitialContext();
   Context envCtx = (Context)
   initCtx.lookup("java:comp/env");
   session = (Session)envCtx.
   lookup("obj/hibernateSession");
  }catch(Exception e) {
   e.printStackTrace();
  }
 }

 public PersonDAO(Session aSession){
  session = aSession;
 }

 public Person save(Person aPerson) {
  if(aPerson != null) session.save(person);
  return person;
 }
}
  顯然,我們沒有處理許多異常和事務問題,或者說我們在許多方法之間共享了Session對象,但根據容器或框架處理對象實例的方式,這可能會引起一些並發問題。但我認為上面的示例清楚地演示了可以重用許多現有的數據層代碼,這只需要一點創造性思維。記住,如果打算在應用服務器之外使用實體和DAO,那麼就要測試,測試,再測試!

  結束語

  正如我們所看到的,在web容器外使用Hibernate實體和DAO有很多竅門,但是這確實可以做到。最大的挑戰是找到實體映射並解決如何重新配置或擴充已有的數據訪問對象(DAO)的問題。對於後者,要小心管理自己的事務,因為有可能無法依賴於任何現有的業務服務。但是到最後,您可以訪問所有實體以及用於對它們進行持久化的對象,這可以節省重新開發所花費的大量時間。祝您好運!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved