在過去幾年里,Hibernate不斷發(fā)展,幾乎成為Java數(shù)據(jù)庫(kù)持久性的事實(shí)標(biāo)準(zhǔn)。它非常強(qiáng)大、靈活,而且具備了優(yōu)異的性能。在本文中,我們將了解如何使用Java 5 注釋來(lái)簡(jiǎn)化Hibernate代碼,并使持久層的編碼過程變得更為輕松。
傳統(tǒng)上,Hibernate的配置依賴于外部 XML 文件:數(shù)據(jù)庫(kù)映射被定義為一組 XML 映射文件,并且在啟動(dòng)時(shí)進(jìn)行加載。創(chuàng)建這些映射有很多方法,可以從已有數(shù)據(jù)庫(kù)模式或Java類模型中自動(dòng)創(chuàng)建,也可以手工創(chuàng)建。無(wú)論如何,您最終將獲得大量的 Hibernate 映射文件。此外,還可以使用工具,通過javadoc樣式的注釋生成映射文件,盡管這樣會(huì)給您的構(gòu)建過程增加一個(gè)步驟。
在最近發(fā)布的幾個(gè)Hibernate版本中,出現(xiàn)了一種基于 Java 5 注釋的更為巧妙的新方法。借助新的 Hibernate Annotation 庫(kù),即可一次性地分配所有舊映射文件——一切都會(huì)按照您的想法來(lái)定義——注釋直接嵌入到您的 Java 類中,并提供一種強(qiáng)大及靈活的方法來(lái)聲明持久性映射。籍由自動(dòng)代碼完成和語(yǔ)法突出顯示功能,最近發(fā)布的Java IDE也為其提供了有力的支持。
Hibernate Annotation還支持新的 EJB 3 持久性規(guī)范。這些規(guī)范旨在提供一種標(biāo)準(zhǔn)化的 Java 持久性機(jī)制。由于 Hibernate 3 還提供了一些擴(kuò)展,因此您可以十分輕松地遵從這些標(biāo)準(zhǔn),并使用 EJB 3 編程模型來(lái)對(duì) Hibernate 持久層進(jìn)行編碼。
現(xiàn)在,讓我們來(lái)動(dòng)手使用Hibernate Annotation。
安裝 Hibernate Annotation
要使用 Hibernate Annotation,您至少需要具備 Hibernate 3.2和Java 5。可以從 Hibernate 站點(diǎn) 下載 Hibernate 3.2 和 Hibernate Annotation庫(kù)。除了標(biāo)準(zhǔn)的 Hibernate JAR 和依賴項(xiàng)之外,您還需要 Hibernate Annotations .jar 文件(hibernate-annotations.jar)、Java 持久性 API (lib/ejb3-persistence.jar)。如果您正在使用 Maven,只需要向 POM 文件添加相應(yīng)的依賴項(xiàng)即可,如下所示:
... <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>3.2.1.ga</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.2.0.ga</version> </dependency> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> </dependency> ...
下一步就是獲取 Hibernate 會(huì)話工廠。盡管無(wú)需驚天的修改,但這一工作與使用 Hibernate Annotations有所不同。您需要使用 AnnotationConfiguration 類來(lái)建立會(huì)話工廠:
sessionFactory = new
AnnotationConfiguration().buildSessionFactory();
盡管通常使用 <mapping> 元素來(lái)聲明持久性類,您還是需要在 Hibernate 配置文件(通常是 hibernate.cfg.xml)中聲明持久性類:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <mapping class="com.onjava.modelplanes.domain.PlaneType"/> <mapping class="com.onjava.modelplanes.domain.ModelPlane"/> </session-factory> </hibernate-configuration>
近期的許多 Java 項(xiàng)目都使用了輕量級(jí)的應(yīng)用框架,例如 Spring。如果您正在使用 Spring 框架,可以使用 AnnotationSessionFactoryBean 類輕松建立一個(gè)基于注釋的 Hibernate 會(huì)話工廠,如下所示:
<!-- Hibernate session factory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> ... </props> </property> <property name="annotatedClasses"> <list> <value>com.onjava.modelplanes.domain.PlaneType</value> <value>com.onjava.modelplanes.domain.ModelPlane</value> ... </list> </property> </bean>
第一個(gè)持久性類
既然已經(jīng)知道了如何獲得注釋所支持的 Hibernate 會(huì)話,下面讓我們來(lái)了解一下帶注釋的持久性類的情況:
像在其他任何 Hibernate應(yīng)用程序中一樣,帶注釋的持久性類也是普通 POJO。差不多可以說是。您需要向 Java 持久性 API (javax.persistence.*)添加依賴項(xiàng),如果您正在使用任何特定于 Hibernate的擴(kuò)展,那很可能就是 Hibernate Annotation 程序包(org.hibernate.annotations.*),但除此之外,它們只是具備了持久性注釋的普通 POJO 。下面是一個(gè)簡(jiǎn)單的例子:
@Entity public class ModelPlane { private Long id; private String name; @Id public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
正像我們所提到的,這非常簡(jiǎn)單。@Entity 注釋聲明該類為持久類。@Id 注釋可以表明哪種屬性是該類中的獨(dú)特標(biāo)識(shí)符。事實(shí)上,您既可以保持字段(注釋成員變量),也可以保持屬性(注釋getter方法)的持久性。后文中將使用基于屬性的注釋。基于注釋的持久性的優(yōu)點(diǎn)之一在于大量使用了默認(rèn)值(最大的優(yōu)點(diǎn)就是 “慣例優(yōu)先原則(convention over configuration)”)。例如,您無(wú)需說明每個(gè)屬性的持久性——任何屬性都被假定為持久的,除非您使用 @Transient 注釋來(lái)說明其他情況。這簡(jiǎn)化了代碼,相對(duì)使用老的 XML 映射文件而言也大幅地減少了輸入工作量。
生成主鍵
Hibernate 能夠出色地自動(dòng)生成主鍵。Hibernate/EBJ 3 注釋也可以為主鍵的自動(dòng)生成提供豐富的支持,允許實(shí)現(xiàn)各種策略。下面的示例說明了一種常用的方法,其中 Hibernate 將會(huì)根據(jù)底層數(shù)據(jù)庫(kù)來(lái)確定一種恰當(dāng)?shù)逆I生成策略:
@Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; }
定制表和字段映射
默認(rèn)情況下,Hibernate 會(huì)將持久類以匹配的名稱映射到表和字段中。例如,前一個(gè)類可以與映射到以如下代碼創(chuàng)建的表中:
CREATE TABLE MODELPLANE ( ID long, NAME varchar )
如果您是自己生成并維護(hù)數(shù)據(jù)庫(kù),那么這種方法很有效,通過省略代碼可以大大簡(jiǎn)化代碼維護(hù)。然而,這并不能滿足所有人的需求。有些應(yīng)用程序需要訪問外部數(shù)據(jù)庫(kù),而另一些可能需要遵從公司的數(shù)據(jù)庫(kù)命名慣例。如果有必要,您可以使用 @Table 和 @Column 注釋來(lái)定制您自己的持久性映射,如下所示:
@Entity @Table(name="T_MODEL_PLANE") public class ModelPlane { private Long id; private String name; @Id @Column(name="PLANE_ID") public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Column(name="PLANE_NAME") public String getName() { return name; } public void setName(String name) { this.name = name; } }
該內(nèi)容將映射到下表中:
CREATE TABLE T_MODEL_PLANE ( PLANE_ID long, PLANE_NAME varchar )
也可以使用其他圖和列的屬性來(lái)定制映射。這使您可以指定諸如列長(zhǎng)度、非空約束等詳細(xì)內(nèi)容。Hibernate支持大量針對(duì)這些注釋的屬性。下例中就包含了幾種屬性:
... @Column(name="PLANE_ID", length=80, nullable=true) public String getName() { return name; } ...
映射關(guān)系
Java 持久性映射過程中最重要和最復(fù)雜的一環(huán)就是確定如何映射表間的關(guān)系。像其他產(chǎn)品一樣, Hibernate 在該領(lǐng)域中提供了高度的靈活性,但卻是以復(fù)雜度的增加為代價(jià)。我們將通過研究幾個(gè)常見案例來(lái)了解如何使用注釋來(lái)處理這一問題。
其中一種最常用的關(guān)系就是多對(duì)一的關(guān)系。假定在以上示例中每個(gè) ModelPlane 通過多對(duì)一的關(guān)系(也就是說,每個(gè)飛機(jī)模型只與一種飛機(jī)類型建立聯(lián)系,盡管指定的飛機(jī)類型可以與七種飛機(jī)模型建立聯(lián)系)來(lái)與 PlaneType 建立聯(lián)系。可如下進(jìn)行映射:
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) public PlaneType getPlaneType() { return planeType; }
CascadeType 值表明 Hibernate 應(yīng)如何處理級(jí)聯(lián)操作。
另一種常用的關(guān)系與上述關(guān)系相反:一對(duì)多再對(duì)一關(guān)系,也稱為集合。在老式的 Hibernate 版本中進(jìn)行映射或使用注釋時(shí),集合令人頭疼,這里我們將簡(jiǎn)要加以探討,以使您了解如何處理集合,例如,在以上示例中每個(gè) PlaneType 對(duì)象都可能會(huì)包含一個(gè) ModelPlanes 集合。可映射如下:
@OneToMany(mappedBy="planeType", cascade=CascadeType.ALL, fetch=FetchType.EAGER) @OrderBy("name") public List<ModelPlane> getModelPlanes() { return modelPlanes; }
命名查詢
Hibernate 最優(yōu)秀的功能之一就在于它能夠在您的映射文件中聲明命名查詢。隨后即可通過代碼中的名稱調(diào)用此類查詢,這使您可以專注于查詢,而避免了 SQL 或者 HQL 代碼分散于整個(gè)應(yīng)用程序中的情況。
也可以使用注釋來(lái)實(shí)現(xiàn)命名查詢,可以使用 @NamedQueries 和 @NamedQuery 注釋,如下所示:
@NamedQueries( { @NamedQuery( name="planeType.findById", query="select p from PlaneType p left join fetch p.modelPlanes where id=:id" ), @NamedQuery( name="planeType.findAll", query="select p from PlaneType p" ), @NamedQuery( name="planeType.delete", query="delete from PlaneType where id=:id" ) } )
一旦完成了定義,您就可以像調(diào)用其他任何其他命名查詢一樣來(lái)調(diào)用它們。
結(jié)束語(yǔ)
Hibernate 3 注釋提供了強(qiáng)大而精致的 API,簡(jiǎn)化了 Java 數(shù)據(jù)庫(kù)中的持久性代碼,本文中只進(jìn)行了簡(jiǎn)單的討論。您可以選擇遵從標(biāo)準(zhǔn)并使用 Java 持久性 API,也可以利用特定于 Hibernate的擴(kuò)展,這些功能以損失可移植性為代價(jià)提供了更為強(qiáng)大的功能和更高的靈活性。無(wú)論如何,通過消除對(duì) XML 映射文件的需求,Hibernate 注釋將簡(jiǎn)化應(yīng)用程序的維護(hù),同時(shí)也可以使您對(duì)EJB 3 有初步認(rèn)識(shí)。來(lái)試試吧!