這份教程討論如何在Apache Tomcat servlet容器中為web程序安裝Hibernate 2.1。Hibernate在大多數主流J2EE應用服務器 的受管理環境中都可以良好運作,也可以作為獨立應用程序運行。在本例中的示例數據庫系統是PostgreSQL 7.3,當然也可以 很容易的換成Hibernate 支持的其它16種數據庫之一。
第一步是拷貝所有需要的運行庫到Tomcat去。在這篇教程中,我們使用一個單獨的web程序(webapps/quickstart)。我們要考慮全局庫文件搜索路徑(TOMCAT/common/lib)和本web應用程序上下文的類裝載器搜索路徑(對于jar來說是webapps/quickstart/WEB-INF/lib,對于class文件來說是webapps/quickstart/WEB-INF/classes)。我們把這兩個類裝載器級別分別稱為全局類路徑(global classpath)和上下文類路徑(context classpath)。
-
首先,把數據庫需要的JDBC驅動拷貝到全局類路徑。這是tomcat附帶的DBCP連接池軟件所要求的。對于本教程來說,把pg73jdbc3.jar庫文件(對應PostgreSQL 7.3和JDK 1.4)到全局類裝載器路徑去。如果你使用一個不同的數據庫,拷貝相應的JDBC 驅動)。
-
不要拷貝任何其他東西到全局類裝載器去。否則你可能在一些工具上遇到麻煩,比如log4j, commons-logging等。 記得要使用每個web應用程序自己的上下文類路徑,就是說把你自己的類庫拷貝到WEB-INF/lib下去,把配置文件configuration/property拷貝到WEB-INF/classes下面去。這兩個目錄默認都是上下文類路徑級別的。
-
Hibernate本身打包成一個JAR庫。hibernate2.jar文件要和你應用程序的其他庫文件一起放在上下文類路徑中。在運行時,Hibernate還需要一些第三方庫,它們在Hibernate發行包的lib/目錄下。參見Table 1.1。把你需要的第三方庫文件也拷貝到上下文類路徑去。
-
要為Tomcat和Hibernate都配置數據庫連接。也就是說Tomcat要負責提供JDBC連接池,Hibernate通過JNDI來請求這些連接。Tomcat把連接池綁定到JNDI。
Table 1.1. Hibernate 第三方庫
庫 |
描述 |
dom4j (必需) |
Hibernate在解析XML配置和XML映射元文件時需要使用dom4j。 |
CGLIB (必需) |
Hibernate在運行時使用這個代碼生成庫強化類(與Java反射機制聯合使用)。 |
Commons Collections, Commons Logging (必需) |
Hibernat使用Apache Jakarta Commons項目提供的多個工具類庫。 |
ODMG4 (必需) |
Hibernate提供了一個可選的ODMG兼容持久化管理界面。如果你需要映射集合,你就需要這個類庫,就算你不是為了使用ODMG API。我們在這個教程中沒有使用集合映射,但不管怎樣把這個JAR拷貝過去總是不錯的。 |
Log4j (可選) |
Hibernate使用Commons Logging API,后者可以使用Log4j作為實施log的機制。如果把Log4j庫放到上下文類目錄中,Commons Logging就會使用Log4j和它在上下文類路徑中找到的log4j.properties文件。在Hibernate發行包中包含有一個示例的properties文件。所以,也把log4j.jar拷貝到你的上下文類路徑去吧。 |
其他文件是不是必需的? |
請察看Hibernate發行包中的/lib/README.txt文件。這是一個Hibernate發行包中附帶的第三方類庫的列表,總是保持更新。你可以在那里找到所有必需或者可選的類庫的列表。 |
好了,現在所有的類庫已經被拷貝過去了,讓我們在Tomcat的主配置文件,TOMCAT/conf/server.xml中增加一個數據庫JDBC連接池的資源聲明,
<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就可以了。
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連接(通過聲明數據源池綁定的JNDI地址)。方言是必需的,因為不同的數據庫都和SQL "標準"有一些出入。Hibernate會替你照管這些差異之處,發行包包含了所有主流的商業和開放源代碼數據庫的方言。
SessionFactory是Hibernate的概念,對應一個數據存儲源,如果有多個數據庫,可以創建多個XML配置文件,也在你的程序中創建多個Configuration和SessionFactory對象。
在hibernate.cfg.xml中的最后一個元素聲明了Cat.hbm.xml是一個Hibernate XML映射文件,對應持久化類Cat。這個文件包含了把POJO類映射到數據庫表(或多個數據庫表)的元數據。我們稍后就回來看這個文件。讓我們先編寫這個POJO類,再在聲明它的映射元數據。
s 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;
}
public 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和float)都可以被映射,也包括Java 集合框架(Java collections framework)中的類。你可以把它們映射成為值,值集合,或者與其他實體相關聯。id是一個特殊的屬性,代表了這個類的數據庫標識符(主鍵),它對于類似于Cat這樣的實體是必需的。
持久化類不需要實現什么特別的接口,也不需要從一個特別的持久化根類繼承下來。Hibernate也不需要使用任何編譯期處理,比如字節碼增強操作,它獨立的使用Java反射機制和運行時類增強(通過CGLIB)。所以,在Hibernate中,POJO的類不需要任何前提條件,我們就可以把它映射成為數據庫表。
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" sql-type="varchar(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工具把這個步驟自動化,請參閱Chapter 19, 工具箱指南。這個工具能夠創建完整的SQL DDL,包括表定義,自定義的字段類型約束,惟一約束和索引。
我們現在可以開始Hibernate的Session了。我們用它來從數據庫中存取Cat。首先,我們要從SessionFactory中獲取一個Session(Hibernate的工作單元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
SessionFactory負責一個數據庫,也只對應一個XML配置文件(hibernate.cfg.xml)。
這篇教程的關注點在于配置Tomcat的JDBC連接,綁定到JNDI上,以及Hibernate的基礎配置。你可以用喜歡的任何方式編寫一個Servlet,包含下面的代碼,只要確保SessionFactory只創建一次。也就是說你不能把它作為你的Serlvet 的實例變量。一個好辦法是用在輔助類中用一個靜態的SessionFactory,例如這樣:
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new RuntimeException("Exception building SessionFactory: " + ex.getMessage(), 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。
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代碼中,或者在ServletFilter中,HTTP結果返回之前。
Hibernate有不同的方法來從數據庫中取回對象。最靈活的方式是使用Hibernate查詢語言(HQL),這是一種容易學習的語言,是對SQL的面向對象的強大擴展。
Transaction tx= session.beginTransaction();
Query query = session.createQuery("select cat from Cat as cat where cat.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的基本面目。請注意我們沒有在例子中包含Servlet相關代碼。你必須自行編寫Servlet,然后把你認為合適的Hibernate代碼插入。
請記住Hibernate作為數據庫訪問層,是與你的程序緊密相關的。一般,所有其他層次都依賴持久機制。請確信你理解了這種設計的含義。
posted on 2006-01-04 18:56
Oliver Zhang 閱讀(139)
評論(0) 編輯 收藏