以下資料摘自Hibernate文檔
1、Hibernate 必須的第三方庫:
| 庫 |
描述 |
| dom4j (必需) |
Hibernate在解析XML配置和XML映射元檔案時需要使用dom4j。 |
| CGLIB (必需) |
Hibernate在運行時使用這個代碼產生庫強化類(與Java反射機制聯合使用)。 |
| Commons Collections, Commons Logging (必需) |
Hibernat使用Apache Jakarta Commons項目提供的多個工具類庫。 |
| ODMG4 (必需) |
Hibernate提供了一個可選的ODMG相容持久化管理介面。如果你需要映射集合,你就需要這個類庫,就算你不是為了使用ODMG API。我們在這個教程中沒有使用集合映射,但不管怎樣把這個JAR拷貝過去總是不錯的。 |
| EHCache (必需) |
Hibernate可以使用不同的第二級Cache方案。如果沒有修改配置的話,EHCache提供預設的Cache。 |
| Log4j (可選) |
Hibernate使用Commons Logging API,後者可以使用Log4j作為底層實施log的機制。如果上下文類目錄中存在Log4j庫,Commons Logging就會使用Log4j和它在上下文類路徑中找到的log4j.properties檔案。在Hibernate發行包中包含有一個樣本的properties檔案。所以,如果你想看看幕布之後到底發生了什麼,也把log4j.jar拷貝到你的上下文類路徑去吧(它位於src/目錄中)。 |
| 其他檔案是不是必需的? |
請察看Hibernate發行包中的/lib/README.txt檔案。這是一個Hibernate發行包中附帶的第三方類庫的列表,總是保持更新。你可以在那裡找到所有必需或者可選的類庫的列表。 |
我們現在來在Tomcat和Hibernate中配置共用資料庫串連池。也就是說Tomcat會提供經過池處理的JDBC串連(用它自己內建的DBCP 串連池功能),Hibernate通過JNDI來請求這些串連。Tomcat 把串連池綁定到JNDI,我們要在Tomcat的主設定檔(TOMCAT/conf/server.xml)中加一行資源聲明:
<Context path="/quickstart" docBase="quickstart">
<Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/quickstart">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- DBCP database connection settings -->
<parameter>
<name>url</name>
<value>jdbc:postgresql://localhost/quickstart</value>
</parameter>
<parameter>
<name>driverClassName</name><value>org.postgresql.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>quickstart</value>
</parameter>
<parameter>
<name>password</name>
<value>secret</value>
</parameter>
<!-- DBCP connection pooling options -->
<parameter>
<name>maxWait</name>
<value>3000</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>100</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>10</value>
</parameter>
</ResourceParams>
</Context>
這個例子中我們要配置的上下文叫做quickstart,它位於TOMCAT/webapp/quickstart目錄。要訪問任何Servlet,在你的瀏覽器中訪問http://localhost:8080/quickstart就可以了(當然,在後面加上在你的web.xml檔案中列出的servlet的名字)。你現在可以建立一個只有空的process()方法的簡單servlet了。
Tomcat在這個配置下,使用DBCP串連池,通過JNDI位置:java:comp/env/jdbc/quickstart提供帶有緩衝池的JDBCConnections。如果你在讓串連池工作的時候遇到困難,請查閱Tomcat文檔。如果你得到了JDBC驅動的exception資訊,請先不要用Hibernate,測試JDBC串連池本身是否正確。Tomcat和JDBC的教程可以在Web上查到。
下一步是配置hibernate,來使用綁定到JNDI的串連池中提供的串連。我們使用XML格式的Hibernate配置。當然,使用 properties檔案的方式在功能上也是一樣的,也不提供什麼特別好處。我們用XML配置的原因,是因為一般會更方便。XML設定檔放在上下文類路 徑(WEB-INF/classes)下面,稱為hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
<property name="show_sql">false</property>
<property name="dialect">net.sf.hibernate.dialect.PostgreSQLDialect</property>
<!-- Mapping files -->
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>
我們關閉了SQL命令的log,告訴Hibernate使用哪種SQL資料庫方言(dialet),還有如何得到JDBC串連(通過Tomcat聲明資料 源池綁定的JNDI地址)。方言是必需的,因為不同的資料庫都和SQL "標準"有一些出入。Hibernate會替你照管這些差異之處,發行包包含了所有主流的商業和開放原始碼資料庫的方言。
SessionFactory是Hibernate的概念,對應一個資料存放區源,如果有多個資料庫,可以建立多個XML設定檔,也在你的程式中建立多個Configuration和SessionFactory對象。
在hibernate.cfg.xml中的最後一個元素宣告了Cat.hbm.xml是一個Hibernate XML對應檔,對應持久化類Cat。這個檔案包含了把POJO類映射到資料庫表(或多個資料庫表)的中繼資料。我們稍後就回來看這個檔案。讓我們先編寫這個POJO類,再在聲明它的映射中繼資料。
1.2. 第一個可持久化類
Hibernate最好的使用方法是使用普通的Java對象(Plain Old Java Objects ,就是POJOs,有時候也稱作Plain Ordinary Java Objects)這種編程模型來進行持久化。一個POJO很像JavaBean,屬性通過getter和setter方法訪問,對外隱藏了內部實現的細 節。
package net.sf.hibernate.examples.quickstart;
public class Cat {
private String id;
private String name;
private char sex;
private float weight;
public Cat() {
}
public String getId() {
return id;
}
private void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
}
Hibernate對屬性使用的類型不加限制。所有的Java JDK類型和原始類型(比如String,char和Date)都可以被映射,也包括Java 集合架構(Java collections framework)中的類。你可以把它們映射成為值,值集合,或者與其他實體相關聯。id是一個特殊的屬性,代表了這個類的資料庫標識符(主鍵),對於類似於Cat這樣的實體我們強烈建議使用。Hibernate可以只在內部使用標識符,但這樣我們會失去一些程式結構方面的靈活性。
持久化類不需要實現什麼特別的介面,也不需要從一個特別的持久化根類繼承下來。Hibernate也不需要使用任何編譯期處理,比如位元組碼增強操作,它獨 立的使用Java反射機制和運行時類增強(通過CGLIB)。所以,在Hibernate中,POJO的類不需要任何前提條件,我們就可以把它映射成為數 據庫表。
1.3. 映射cat
Cat.hbm.xml對應檔包含了對象/關係映射所需的中繼資料。中繼資料套件含持久化類的聲明和屬性到資料庫的映射(指向欄位和其他實體的外部索引鍵關聯)。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="net.sf.hibernate.examples.quickstart.Cat" table="CAT">
<!-- A 32 hex character is our surrogate key. It's automatically
generated by Hibernate with the UUID pattern. -->
<id name="id" type="string" unsaved-value="null" >
<column name="CAT_ID" sql-type="char(32)" not-null="true"/>
<generator class="uuid.hex"/>
</id>
<!-- A cat has to have a name, but it shouldn' be too long. -->
<property name="name">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="sex"/>
<property name="weight"/>
</class>
</hibernate-mapping>
每個持久化類都應該需要一個識別屬性(實際上,只是哪些代表實體的類,而不是代表值對象的類,後者會被映射稱為實體物件中的一個組件)。這個屬性用來區分持久化對象:如果catA.getId().equals(catB.getId())結果是true的話,兩隻貓就是相同的。這個概念稱為資料庫標識。Hiernate 附帶了幾種不同的標識符產生器,用於不同的場合(包括資料庫本地的順序(sequence)產生器、hi/lo高低位標識模式、和程式自己定義的標識 符)。我們在這裡使用UUID產生器(只在測試時建議使用,如果使用資料庫自己產生的整數類型的索引值更好),並指定CAT表的CAT_ID欄位(作為表的主鍵)存放產生的標識值。
Cat的其他屬性都映射到同一個表。對name屬性來說,我們把它顯式地宣告對應到一個資料庫欄位。如果資料庫schema是由映射聲明使用Hibernate的SchemaExport工具自動產生的(作為SQL DDL指令),這特別有用。所有其它的屬性都用Hibernate的預設值映射,大多數情況你都會這樣做。資料庫中的CAT表看起來是這樣的:
Column | Type | Modifiers
--------+-----------------------+-----------
cat_id | character(32) | not null
name | character varying(16) | not null
sex | character(1) |
weight | real |
Indexes: cat_pkey primary key btree (cat_id)
你現在可以在你的資料庫中首先建立這個表了,如果你需要使用SchemaExport工具把這個步驟自動化,請參閱第 15 章 工具箱指南。這個工具能夠建立完整的SQL DDL,包括表定義,自訂的欄位類型約束,惟一約束和索引。
1.4. 與貓同樂
我們現在可以開始Hibernate的Session了。它是持久化管理器介面,我們用它來從資料庫中存取Cat。首先,我們要從SessionFactory中擷取一個Session(Hibernate的工作單元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
SessionFactory負責一個資料庫,也只對應一個XML設定檔(hibernate.cfg.xml)。通過在建立 SessionFactory之前(它是不可變的),你可以訪問Configuration來設定其他屬性(甚至修改映射的中繼資料)。我們應該在哪兒建立SessionFactory,在我們的程式中又如何訪問它呢? SessionFactory通常只是被初始化一次,比如通過一個load-on-startup servlet。這意味著你不應該在serlvet中把它作為一個執行個體變數來持有,應該放在其他地方。更深入一些的說,我們需要某種單例(Singleton)模式,我們才能更容易的在程式中訪問SessionFactory。下面的方法同時解決了兩個問題:配置和容易地訪問SessionFactory。
我們實現一個HibernateUtil輔助類:
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session s = (Session) session.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null)
s.close();
}
}
這個類不但在它的靜態屬性中使用了SessionFactory,還使用了ThreadLocal來為當前背景工作執行緒儲存Session。在你想用這個輔助類之前,請確保你理解了thread-local變數這個Java概念。
SessionFactory是安全執行緒的,很多線程可以同時訪問它,擷取Session。Session不是安全執行緒的,它代表與資料庫之間的一次操作。Session通過SessionFactory開啟,在所有的工作完成後,需要關閉:
Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
tx.commit();
HibernateUtil.closeSession();
在Session中,每個資料庫操作都是在一個事務(transaction)中進行的,這樣就可以隔離開不同的操作(甚至包括唯讀操作)。我們使用Hibernate的Transaction API來從底層的事務策略中(本例中是JDBC事務)脫身。這樣,如果需要把我們的程式部署到一個由容器管理事務的環境中去(使用JTA),我們就不需要更改原始碼。請注意,我們上面的例子沒有處理任何異常。
也請注意,你可以隨心所欲的多次調用HibernateUtil.currentSession();,你每次都會得到同一個當前線程的Session。你必須確保Session在你的資料庫訪問工作完成後關閉,不管是在你的servlet代碼中,或者在servlet filter中,HTTP結果返回之前。後者的一個附帶好處是,可以容易的使用延遲裝載:Session在渲染view層的時候仍然開啟著,所以你在遍曆對象圖的時候可以裝載所需的對象。
Hibernate有不同的方法來從資料庫中取回對象。最靈活的方式是使用Hibernate查詢語言(HQL),這是一種容易學習的語言,是對SQL的物件導向的強大擴充。
Transaction tx= session.beginTransaction();
Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
out.println("Female Cat: " + cat.getName() );
}
tx.commit();
Hibernate也提供一種物件導向的按條件查詢API,可以執行公式化的型別安全的查詢。當然,Hibernate在所有與資料庫的互動中都使用PrepatedStatement和參數綁定。你也可以使用Hibernate的直接SQL查詢功能,或者在特殊情況下從Session擷取一個原始的JDBC串連。
1.5. 結語
在這個短小的教程中,我們只描繪了Hibernate的基本面目。請注意我們沒有在例子中包含servlet相關代碼。你必須自行編寫servlet,然後把你認為合適的Hibernate代碼插入。
請記住Hibernate作為資料庫訪問層,是與你的程式緊密相關的。一般,所有其他層次都依賴持久機制。請確信你理解了這種設計的含義。