這份教程討論如何在Apache Tomcat servlet容器中為web程序安裝Hibernate 2.1。Hibernate在大多數(shù)主流J2EE應(yīng)用服務(wù)器 的受管理環(huán)境中都可以良好運(yùn)作,也可以作為獨(dú)立應(yīng)用程序運(yùn)行。在本例中的示例數(shù)據(jù)庫系統(tǒng)是PostgreSQL 7.3,當(dāng)然也可以 很容易的換成Hibernate 支持的其它16種數(shù)據(jù)庫之一。
第一步是拷貝所有需要的運(yùn)行庫到Tomcat去。在這篇教程中,我們使用一個(gè)單獨(dú)的web程序(webapps/quickstart)。我們要考慮全局庫文件搜索路徑(TOMCAT/common/lib)和本web應(yīng)用程序上下文的類裝載器搜索路徑(對于jar來說是webapps/quickstart/WEB-INF/lib,對于class文件來說是webapps/quickstart/WEB-INF/classes)。我們把這兩個(gè)類裝載器級別分別稱為全局類路徑(global classpath)和上下文類路徑(context classpath)。
-
首先,把數(shù)據(jù)庫需要的JDBC驅(qū)動拷貝到全局類路徑。這是tomcat附帶的DBCP連接池軟件所要求的。對于本教程來說,把pg73jdbc3.jar庫文件(對應(yīng)PostgreSQL 7.3和JDK 1.4)到全局類裝載器路徑去。如果你使用一個(gè)不同的數(shù)據(jù)庫,拷貝相應(yīng)的JDBC 驅(qū)動)。
-
不要拷貝任何其他東西到全局類裝載器去。否則你可能在一些工具上遇到麻煩,比如log4j, commons-logging等。 記得要使用每個(gè)web應(yīng)用程序自己的上下文類路徑,就是說把你自己的類庫拷貝到WEB-INF/lib下去,把配置文件configuration/property拷貝到WEB-INF/classes下面去。這兩個(gè)目錄默認(rèn)都是上下文類路徑級別的。
-
Hibernate本身打包成一個(gè)JAR庫。hibernate2.jar文件要和你應(yīng)用程序的其他庫文件一起放在上下文類路徑中。在運(yùn)行時(shí),Hibernate還需要一些第三方庫,它們在Hibernate發(fā)行包的lib/目錄下。參見Table 1.1。把你需要的第三方庫文件也拷貝到上下文類路徑去。
-
要為Tomcat和Hibernate都配置數(shù)據(jù)庫連接。也就是說Tomcat要負(fù)責(zé)提供JDBC連接池,Hibernate通過JNDI來請求這些連接。Tomcat把連接池綁定到JNDI。
Table 1.1. Hibernate 第三方庫
庫 |
描述 |
dom4j (必需) |
Hibernate在解析XML配置和XML映射元文件時(shí)需要使用dom4j。 |
CGLIB (必需) |
Hibernate在運(yùn)行時(shí)使用這個(gè)代碼生成庫強(qiáng)化類(與Java反射機(jī)制聯(lián)合使用)。 |
Commons Collections, Commons Logging (必需) |
Hibernat使用Apache Jakarta Commons項(xiàng)目提供的多個(gè)工具類庫。 |
ODMG4 (必需) |
Hibernate提供了一個(gè)可選的ODMG兼容持久化管理界面。如果你需要映射集合,你就需要這個(gè)類庫,就算你不是為了使用ODMG API。我們在這個(gè)教程中沒有使用集合映射,但不管怎樣把這個(gè)JAR拷貝過去總是不錯(cuò)的。 |
Log4j (可選) |
Hibernate使用Commons Logging API,后者可以使用Log4j作為實(shí)施log的機(jī)制。如果把Log4j庫放到上下文類目錄中,Commons Logging就會使用Log4j和它在上下文類路徑中找到的log4j.properties文件。在Hibernate發(fā)行包中包含有一個(gè)示例的properties文件。所以,也把log4j.jar拷貝到你的上下文類路徑去吧。 |
其他文件是不是必需的? |
請察看Hibernate發(fā)行包中的/lib/README.txt文件。這是一個(gè)Hibernate發(fā)行包中附帶的第三方類庫的列表,總是保持更新。你可以在那里找到所有必需或者可選的類庫的列表。 |
好了,現(xiàn)在所有的類庫已經(jīng)被拷貝過去了,讓我們在Tomcat的主配置文件,TOMCAT/conf/server.xml中增加一個(gè)數(shù)據(jù)庫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>
這個(gè)例子中我們要配置的上下文叫做quickstart,它位于TOMCAT/webapp/quickstart目錄。要訪問任何Servlet,在你的瀏覽器中訪問http://localhost:8080/quickstart就可以了。
Tomcat在這個(gè)配置下,使用DBCP連接池,通過JNDI位置:java:comp/env/jdbc/quickstart提供帶有緩沖池的JDBCConnections。如果你在讓連接池工作的時(shí)候遇到困難,請查閱Tomcat文檔。如果你得到了JDBC驅(qū)動的exception信息,請先不要用Hibernate,測試JDBC連接池本身是否正確。Tomcat和JDBC的教程可以在Web上查到。
下一步是配置hibernate,來使用綁定到JNDI的連接池中提供的連接。我們使用XML格式的Hibernate配置。當(dāng)然,使用properties文件的方式在功能上也是一樣的,也不提供什么特別好處。我們用XML配置的原因,是因?yàn)橐话銜奖恪ML配置文件放在上下文類路徑(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>
我們關(guān)閉了SQL命令的log,告訴Hibernate使用哪種SQL數(shù)據(jù)庫方言(dialet),還有如何得到JDBC連接(通過聲明數(shù)據(jù)源池綁定的JNDI地址)。方言是必需的,因?yàn)椴煌臄?shù)據(jù)庫都和SQL "標(biāo)準(zhǔn)"有一些出入。Hibernate會替你照管這些差異之處,發(fā)行包包含了所有主流的商業(yè)和開放源代碼數(shù)據(jù)庫的方言。
SessionFactory是Hibernate的概念,對應(yīng)一個(gè)數(shù)據(jù)存儲源,如果有多個(gè)數(shù)據(jù)庫,可以創(chuàng)建多個(gè)XML配置文件,也在你的程序中創(chuàng)建多個(gè)Configuration和SessionFactory對象。
在hibernate.cfg.xml中的最后一個(gè)元素聲明了Cat.hbm.xml是一個(gè)Hibernate XML映射文件,對應(yīng)持久化類Cat。這個(gè)文件包含了把POJO類映射到數(shù)據(jù)庫表(或多個(gè)數(shù)據(jù)庫表)的元數(shù)據(jù)。我們稍后就回來看這個(gè)文件。讓我們先編寫這個(gè)POJO類,再在聲明它的映射元數(shù)據(jù)。
s Hibernate讓普通的Java對象(Plain Old Java Objects ,就是POJOs,有時(shí)候也稱作Plain Ordinary Java Objects)變成持久化類。一個(gè)POJO很像JavaBean,屬性通過getter和setter方法訪問,對外隱藏了內(nèi)部實(shí)現(xiàn)的細(xì)節(jié)。
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)中的類。你可以把它們映射成為值,值集合,或者與其他實(shí)體相關(guān)聯(lián)。id是一個(gè)特殊的屬性,代表了這個(gè)類的數(shù)據(jù)庫標(biāo)識符(主鍵),它對于類似于Cat這樣的實(shí)體是必需的。
持久化類不需要實(shí)現(xiàn)什么特別的接口,也不需要從一個(gè)特別的持久化根類繼承下來。Hibernate也不需要使用任何編譯期處理,比如字節(jié)碼增強(qiáng)操作,它獨(dú)立的使用Java反射機(jī)制和運(yùn)行時(shí)類增強(qiáng)(通過CGLIB)。所以,在Hibernate中,POJO的類不需要任何前提條件,我們就可以把它映射成為數(shù)據(jù)庫表。
Cat.hbm.xml映射文件包含了對象/關(guān)系映射所需的元數(shù)據(jù)。
元數(shù)據(jù)包含了持久化類的聲明和把它與其屬性映射到數(shù)據(jù)庫表的信息(屬性作為值或者是指向其他實(shí)體的關(guān)聯(lián))。
<?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>
每個(gè)持久化類都需要一個(gè)標(biāo)識屬性(實(shí)際上,只是哪些代表一手對象的類,而不是代表值對象的類,后者會被映射稱為一手對象中的一個(gè)組件)。這個(gè)屬性用來區(qū)分持久化對象:如果catA.getId().equals(catB.getId())結(jié)果是true的話,兩只貓就是相同的。這個(gè)概念稱為數(shù)據(jù)庫標(biāo)識。Hiernate附帶了幾種不同的標(biāo)識符生成器,用于不同的場合(包括數(shù)據(jù)庫本地的順序(sequence)生成器和hi/lo高低位標(biāo)識模式)。我們在這里使用UUID生成器,并指定CAT表的CAT_ID字段(作為表的主鍵)存放生成的標(biāo)識值。
Cat的其他屬性都映射到同一個(gè)表。對name屬性來說,我們把它顯式地聲明映射到一個(gè)數(shù)據(jù)庫字段。如果數(shù)據(jù)庫schema是由映射聲明使用Hibernate的SchemaExport工具自動生成的(作為SQL DDL指令),這特別有用。所有其它的屬性都用Hibernate的默認(rèn)值映射,大多數(shù)情況你都會這樣做。數(shù)據(jù)庫中的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)
你現(xiàn)在可以在你的數(shù)據(jù)庫中首先創(chuàng)建這個(gè)表了,如果你需要使用SchemaExport工具把這個(gè)步驟自動化,請參閱Chapter 19, 工具箱指南。這個(gè)工具能夠創(chuàng)建完整的SQL DDL,包括表定義,自定義的字段類型約束,惟一約束和索引。
我們現(xiàn)在可以開始Hibernate的Session了。我們用它來從數(shù)據(jù)庫中存取Cat。首先,我們要從SessionFactory中獲取一個(gè)Session(Hibernate的工作單元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
SessionFactory負(fù)責(zé)一個(gè)數(shù)據(jù)庫,也只對應(yīng)一個(gè)XML配置文件(hibernate.cfg.xml)。
這篇教程的關(guān)注點(diǎn)在于配置Tomcat的JDBC連接,綁定到JNDI上,以及Hibernate的基礎(chǔ)配置。你可以用喜歡的任何方式編寫一個(gè)Servlet,包含下面的代碼,只要確保SessionFactory只創(chuàng)建一次。也就是說你不能把它作為你的Serlvet 的實(shí)例變量。一個(gè)好辦法是用在輔助類中用一個(gè)靜態(tài)的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();
}
}
這個(gè)類不但在它的靜態(tài)屬性中使用了SessionFactory,還使用了ThreadLocal來為當(dāng)前工作線程保存Session。
Session不是線程安全的,代表與數(shù)據(jù)庫之間的一次操作。Session通過SessionFactory打開,在所有的工作完成后,需要關(guān)閉:
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中,每個(gè)數(shù)據(jù)庫操作都是在一個(gè)事務(wù)(transaction)中進(jìn)行的,這樣就可以隔離開不同的操作(甚至包括只讀操作)。我們使用Hibernate的Transaction API來從底層的事務(wù)策略中(本例中是JDBC事務(wù))脫身。這樣,如果需要把我們的程序部署到一個(gè)由容器管理事務(wù)的環(huán)境中去(使用JTA),我們就不需要更改源代碼。請注意,我們上面的例子沒有處理任何異常。
也請注意,你可以隨心所欲的多次調(diào)用HibernateUtil.currentSession();,你每次都會得到同一個(gè)當(dāng)前線程的Session。你必須確保Session在你的數(shù)據(jù)庫事務(wù)完成后關(guān)閉,不管是在你的Servlet代碼中,或者在ServletFilter中,HTTP結(jié)果返回之前。
Hibernate有不同的方法來從數(shù)據(jù)庫中取回對象。最靈活的方式是使用Hibernate查詢語言(HQL),這是一種容易學(xué)習(xí)的語言,是對SQL的面向?qū)ο蟮膹?qiáng)大擴(kuò)展。
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也提供一種面向?qū)ο蟮?span id="sakewci" class=emphasis>按條件查詢API,可以執(zhí)行公式化的類型安全的查詢。當(dāng)然,Hibernate在所有與數(shù)據(jù)庫的交互中都使用PrepatedStatement和參數(shù)綁定。
在這個(gè)短小的教程中,我們只描繪了Hibernate的基本面目。請注意我們沒有在例子中包含Servlet相關(guān)代碼。你必須自行編寫Servlet,然后把你認(rèn)為合適的Hibernate代碼插入。
請記住Hibernate作為數(shù)據(jù)庫訪問層,是與你的程序緊密相關(guān)的。一般,所有其他層次都依賴持久機(jī)制。請確信你理解了這種設(shè)計(jì)的含義。
posted on 2006-01-04 18:56
Oliver Zhang 閱讀(143)
評論(0) 編輯 收藏