1. Hibernate介紹
Hibernate是一個(gè)開(kāi)放源代碼的O/R Mapping (對(duì)象關(guān)系映射框架),它對(duì)JDBC進(jìn)行了輕量級(jí)的對(duì)象封裝,使Java程序員可以隨心所欲的使用對(duì)象編程思維來(lái)操縱數(shù)據(jù)庫(kù)。
Hibernate 是一個(gè)面向Java 環(huán)境的對(duì)象/關(guān)系數(shù)據(jù)庫(kù)映射工具。對(duì)象/關(guān)系數(shù)據(jù)庫(kù)映射(object/relational mapping (ORM))這個(gè)術(shù)語(yǔ)表示一種技術(shù),用來(lái)把對(duì)象模型表示的對(duì)象映射到基于SQL 的關(guān)系模型結(jié)構(gòu)中去。Hibernate 不僅僅管理Java 類(lèi)到數(shù)據(jù)庫(kù)表的映射,還提供數(shù)據(jù)查詢(xún)和獲取數(shù)據(jù)的方法,可以大
幅度減少開(kāi)發(fā)時(shí)人工使用SQL 和JDBC 處理數(shù)據(jù)的時(shí)間。Hibernate 的目標(biāo)是對(duì)于開(kāi)發(fā)者通常的數(shù)據(jù)持久化相關(guān)的編程任務(wù),解放其中的95%。
Hibernate開(kāi)發(fā)準(zhǔn)備工作需要準(zhǔn)備工具:
Hibernate工具,下載地址:http://www.hibernate.org.cn/download/
hibernate-2.1.2.zip,然后將其解壓縮。假設(shè)放到C:\hibernate-2.1目錄下(我們?cè)谶@里設(shè)置環(huán)境變量HIBERNATE_HOME= C:\hibernate-2.1)。讓我們看看Hibernate的包情況,首先在%HIBERNATE_HOME%目錄下有一個(gè)hibernate
2.jar,該包就是我們進(jìn)行Hibernate開(kāi)發(fā)所需要用到的Hibernate工具包,當(dāng)然Hibernate本身還需要其他lib的支持,這些支持包都在%HIBERNATE_HO
ME%\lib下面。請(qǐng)看表1-1,Hibernate的第三方庫(kù)
庫(kù)名稱(chēng) |
說(shuō)明 |
dom4j (必需) |
Hibernate 在解析XML 配置和XML 映射元文件時(shí)需要使用dom4j。 |
CGLIB (必需) |
Hibernate 在運(yùn)行時(shí)使用這個(gè)代碼生成庫(kù)強(qiáng)化類(lèi)(與Java 反射機(jī)制聯(lián)合使用)。 |
Commons collection,Commons logging |
Hibernat 使用Apache Jakarta Commons 項(xiàng)目提供的多個(gè)工具類(lèi)庫(kù)。
|
ODMG4 |
Hibernate 提供了一個(gè)可選的ODMG 兼容持久化管理界面。如果你需要映射集合,你就需要這個(gè)類(lèi)庫(kù),就算你不是為了使用ODMG API。 |
Log4j |
Hibernate 使用Commons Logging API,后者可以使用Log4j 作為實(shí)施log 的機(jī)制。如果把Log4j 庫(kù)放到上下文類(lèi)目錄中,Commons Logging就會(huì)使用Log4j 和它在上下文類(lèi)路徑中找到的log4j.properties 文件。在Hibernate 發(fā)行包中包含有一個(gè)示例的properties 文件。所以,也把log4j.jar 拷貝到你的上下文類(lèi)路徑去吧。 |
其他文件是不是必須的 |
請(qǐng)察看Hibernate 發(fā)行包中的/lib/README.txt 文件。這是一個(gè)
Hibernate 發(fā)行包中附帶的第三方類(lèi)庫(kù)的列表,總是保持更新。你可以在那里找到所有必需或者可選的類(lèi)庫(kù)的列表。 |
表1-1
2. Hibernate的配置
首先,讓我們看看Hibernate的全局配置文件,這個(gè)配置文件可以有兩種形式,每種形式都可以完成Hibernate的配置工作,分別是hibernate.properties或者hibernate.cfg.xml,這個(gè)文件需要放到上下文路徑下面(假設(shè)在一個(gè)Web應(yīng)用下,需要放到WEB-INF/classes下面),但請(qǐng)注意,雖然hibernate官方說(shuō)法是必須放到該目錄下,其實(shí)是可以放在別的路徑下面,只需要在buildSessionFactory的時(shí)候加入配置文件的路徑就可以了。關(guān)于配置文件的內(nèi)容,可以參看%HIBERNATE_HOME%\src目錄中hibernate.properties文件,而關(guān)于其含義解釋?zhuān)梢詤⒖?/SPAN>:
http://www.javajia.com/article.php?id=901
http://www.hibernate.org.cn/53.html
在進(jìn)入Hibernate例程之前還需要其他一些準(zhǔn)備工作。下載應(yīng)用服務(wù)器JBoss3.2.6,其下載地址為:
http://voxel.dl.sourceforge.net/sourceforge/jboss/jboss-3.2.6.zip
下載mysql5.0.0,其下載地址為:
http://mysql.linuxforum.net/Downloads/MySQL-5.0/mysql-5.0.0a-alpha-win.zip
以及mysl數(shù)據(jù)庫(kù)的jdbc數(shù)據(jù)庫(kù)連接包,下載地址:
http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-3.1.4-beta.zip
當(dāng)然,你還必須確定你已經(jīng)安裝了j2sdk1.4.2,如果沒(méi)有請(qǐng)下載安裝:
http://java.sun.com/j2sk/1.4.2/download.html
還有就是你必須確定,您已經(jīng)將JAVA_HOME,JBOSS_HOME,classpath等環(huán)境變量設(shè)置齊全。最好將mysql的jdbc連接包,放到%JBOSS_HOME%\
server\default\lib下面。
此外,你還需要在mysql數(shù)據(jù)庫(kù)中建立一個(gè)test庫(kù),并且在該庫(kù)中建立一張名為courses的表,其創(chuàng)建語(yǔ)句為:
create database if not exists `test`;
use `test`;
drop table if exists `courses`;
CREATE TABLE `courses` (
`CourseId` varchar(32) NOT NULL default '',
`name` varchar(32) default NULL,
PRIMARY KEY (`CourseId`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `courses` VALUES
('2313213213','eeeeeee'),
('12345656','Jacky'),
('Bonnie','Bonnie');
這樣就創(chuàng)建了我們例程所需要的表和測(cè)試數(shù)據(jù)。此外我們還需要配置JBoss的數(shù)據(jù)源,在此之前,請(qǐng)啟動(dòng)JBoss,如若不能正常啟動(dòng),請(qǐng)查看環(huán)境變量配置情況,或者查看JBoss網(wǎng)站。找到%JBOSS_HOME%\server\default\deploy目錄,查看該目錄下是否有mysql-ds.xml的mysql數(shù)據(jù)源配置文件。如果沒(méi)有請(qǐng)新建該文件,該文件內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/test</connection-url>
<driver-class>org.gjt.mm.mysql.Driver</driver-class>
<user-name>root</user-name>
<password></password>
</local-tx-datasource>
</datasources>
這樣就配置好了Jboss的jdbc數(shù)據(jù)源。其名稱(chēng)為MySqlDS。啟動(dòng)JBoss,注意控制臺(tái)輸出信息,已經(jīng)將數(shù)據(jù)源邦定到java:/MySqlDS的JNDI名稱(chēng)。
3. Hibernate例程
a) 打開(kāi)JBuilder新建一個(gè)Project,將其命名為TestHibernate。然后
將%HIBERNATE_HOME%\hibernate2.jar以及%HIBERNATE_HOME%\lib下面所有以上提到的包(你也可以將其下所有的包)放到一個(gè)JBuilder library下,將這個(gè)library引入TestHibernate工程的classpath下面。這樣就將所有hibernate所需要的包引入了。
b) 建立第一個(gè)可持久化類(lèi)。新建一個(gè)類(lèi),將其package設(shè)com.hibernate
并且將其命名為Cours.java,其代碼如下:
package com.hibernate;
import java.util.Set;
/**
*在hibernate中代表了Course表的類(lèi)。
*/
public class Cours
{
/**每個(gè)屬性和表的一個(gè)字段對(duì)應(yīng)**/
private String courseId;
private String name;
/**students表示course中的學(xué)生,在后面才會(huì)用到,暫時(shí)不管**/
private Set students;
/**屬性的訪問(wèn)方法**/
public void setCourseId(String string) {
courseId = string;
}
public String getCourseId() {
return courseId;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setStudents(Set stud){
this.students=stud;
}
public Set getStudents(){
return this.students;
}
}
c)建立一個(gè)Jsp頁(yè)面以便JBuilder生成defaultroot目錄結(jié)構(gòu),編譯java源文件。
d)這個(gè)時(shí)候我們就可以找到該JBuilder工程所在的目錄,找到defaultroot下面的classes目錄,在該目錄中手工建立一個(gè)Hibernate配置文件hibernate.cfg.xml,現(xiàn)在我們來(lái)看配置該文件。
<!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:/MySqlDS</property>
<property name="show_sql">false</property>
<property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
<property name="jndi.class">org.jnp.interfaces.NamingContextFactory</property>
<property name="jndi.url">jnp://localhost:1099/</property>
<!-- Mapping files -->
<mapping resource="Cours.hbm.xml"/>
</session-factory>
</hibernate-configuration>
首先我們要確保Hibernate會(huì)連接到MySql數(shù)據(jù)庫(kù),參考前文提到的Hibernate配置文檔,這是就需要我們配置hibernate.dialect方言屬性,該屬性說(shuō)明hibernate需要連接的數(shù)據(jù)庫(kù)類(lèi)型,在此我們?cè)O(shè)置為MySql類(lèi)型。將hibernate.dialect設(shè)置成net.sf.hibernate.dialect.MySQLDialect。
而訪問(wèn)數(shù)據(jù)庫(kù)的數(shù)據(jù)源,我們?cè)诖嗽O(shè)置成了java:/MySqlDS。我們需要注意,Hibernate自帶一個(gè)連接池配置方案(hibernate.properties方案):
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql:///test
hibernate.connection.username root
hibernate.connection.password
如果你去研究Hibernate的源代碼,會(huì)發(fā)現(xiàn)Hibernate自帶的連接池方案是一個(gè)非常原始的連接池,所以我們?cè)诖瞬挥谩?/SPAN>Hibernate總共有三種連接吃方案:Hibernate自帶連接池、Hibernate帶的DBCP連接池、App Server的連接池。當(dāng)然,我們選擇的是最后一種方案,采用App Server提供連接池的方案。
而hibernate.show_sql屬性是讓用戶能夠購(gòu)在調(diào)試程序的時(shí)候通過(guò)控制臺(tái)看到sql語(yǔ)句的輸出,以便用戶調(diào)試,在這里我們選擇了關(guān)閉該輸出,當(dāng)然你也可以打開(kāi)該輸出,只要將該屬性設(shè)置為true值。
對(duì)于hibernate.jndi.class和hibernate.jndi.url屬性,該屬性是當(dāng)你如果想從遠(yuǎn)程客戶端直接調(diào)用處在不同的JVM下的資源的時(shí)候,你就需要通過(guò)這兩個(gè)屬性查找JNDI資源,這里設(shè)置的是通過(guò)jnp提供的NamingContextFactory來(lái)查找jnp://localhost:1099這個(gè)服務(wù)下的資源。但是需要注意的是:JBoss配置的DataSource的JNDI名稱(chēng)是java:/MySqlDS,JBoss是不支持遠(yuǎn)程客戶端對(duì)其JNDI資源的調(diào)用。所以在這里這兩個(gè)屬性并沒(méi)有實(shí)際意義,而當(dāng)我們將JBoss換成如Weblogic等的應(yīng)用服務(wù)器的時(shí)候,就可以在遠(yuǎn)程客戶端通過(guò)查找JNDI名稱(chēng)來(lái)獲得處于另外JVM下的服務(wù)器資源,例如連接池?cái)?shù)據(jù)源。
對(duì)于mapping映射屬性,這是通知hibernate,讓其知道現(xiàn)在已經(jīng)有一個(gè)映射文件來(lái)映射數(shù)據(jù)庫(kù)中的表,這個(gè)文件名是Cours.hbm.xml。
有關(guān)hibernate.cfg.xml或者hibernate.properties文件的更多配置信息,請(qǐng)參看前文提到的參考地址。
e)下面讓我們看看上節(jié)提到的Cours.hbm.xml,同樣,這個(gè)文件需要
放到defaultroot\classes目錄下。該文件的內(nèi)容如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
<class
name="com.hibernate.Cours"
table="Courses"
dynamic-update="false"
>
<id
name="courseId"
column="CourseId"
type="string"
unsaved-value="any"
>
<generator class="assigned"/>
</id>
<property
name="name"
type="string"
update="true"
insert="true"
column="Name"
/>
</class>
</hibernate-mapping>
首先讓我們來(lái)看class節(jié)點(diǎn),該節(jié)點(diǎn)描述數(shù)據(jù)庫(kù)中的一個(gè)表對(duì)應(yīng)我們創(chuàng)建的一個(gè)可持久化類(lèi)(該持久化類(lèi)類(lèi)似于JavaBean,通過(guò)getter和setter方法訪問(wèn))。在這里我們可以看到name為com.hibernate.Cours的可持久化類(lèi)對(duì)應(yīng)于table為Courses的數(shù)據(jù)庫(kù)表,dynamic-update屬性用來(lái)標(biāo)識(shí)是否對(duì)update的sql語(yǔ)句在運(yùn)行時(shí)動(dòng)態(tài)生成,并且之更新那些改變過(guò)的字段。在這里我們不需要過(guò)于關(guān)注這個(gè)屬性。
其次,觀察id子節(jié)點(diǎn),該節(jié)點(diǎn)表示了表的關(guān)鍵字段,name=”courseId”表明了com.hibernate.Cours類(lèi)中的對(duì)應(yīng)courses表主關(guān)鍵字的attribute屬性是courseId。而對(duì)應(yīng)的字段是column="CourseId",類(lèi)型為type="string"
而對(duì)于unsaved-value="any" 一個(gè)特定的標(biāo)識(shí)屬性值,用來(lái)標(biāo)志該實(shí)例是剛
剛創(chuàng)建的,尚未保存。這可以把這種實(shí)例和從以前的session 中裝載過(guò)但未再次持久化的實(shí)例區(qū)分開(kāi)來(lái)。<generator>節(jié)點(diǎn)定義該主關(guān)鍵字的生成規(guī)則,assign表示該主關(guān)鍵字生成規(guī)則為用戶在save之前為對(duì)象分配主關(guān)鍵字標(biāo)識(shí)符。關(guān)鍵字生成規(guī)則有以下幾種類(lèi)型:
increment(遞增)
用于為long, short 或者int 類(lèi)型生成唯一標(biāo)識(shí)。只有在沒(méi)有其他進(jìn)程往同一張表中插入數(shù)據(jù)時(shí)才能使用。在集群下不要使用。
Identity
對(duì)DB2,MySQL, MS SQL Server, Sybase 和HypersonicSQL 的內(nèi)置標(biāo)識(shí)字段提供支持。返回的標(biāo)識(shí)符是long, short 或者int 類(lèi)型的。
sequence (序列)
在DB2,PostgreSQL, Oracle, SAP DB, McKoi 中使用序列(sequence),而在Interbase 中使用生成器(generator)。返回的標(biāo)識(shí)符是long, short 或者 int 類(lèi)型的。
hilo (高低位)
使用一個(gè)高/低位算法來(lái)高效的生成long, short 或者 int 類(lèi)型的標(biāo)識(shí)符。給定一個(gè)表和字段(默認(rèn)分別是是hibernate_unique_key 和next)作為高位值得來(lái)源。高/低位算法生成的標(biāo)識(shí)符只在一個(gè)特定的數(shù)據(jù)庫(kù)中是唯一的。在使用JTA 獲得的連接或者用戶自行提供的連接中,不要使用這種生成器。
seqhilo(使用序列的高低位)
使用一個(gè)高/低位算法來(lái)高效的生成long, short 或者 int 類(lèi)型的標(biāo)識(shí)符,給定一個(gè)數(shù)據(jù)庫(kù)序列(sequence)的名字。
uuid.hex
用一個(gè)128-bit 的UUID 算法生成字符串類(lèi)型的標(biāo)識(shí)符。在一個(gè)網(wǎng)絡(luò)中唯一(使用了IP地址)。UUID 被編碼為一個(gè)32 位16 進(jìn)制數(shù)字的字符串。
uuid.string
使用同樣的UUID 算法。UUID 被編碼為一個(gè)16 個(gè)字符長(zhǎng)的任意ASCII 字符組成的字符串。不能使用在PostgreSQL 數(shù)據(jù)庫(kù)中
native(本地)
根據(jù)底層數(shù)據(jù)庫(kù)的能力選擇identity, sequence 或者hilo 中的一個(gè)。
assigned(程序設(shè)置)
讓?xiě)?yīng)用程序在save()之前為對(duì)象分配一個(gè)標(biāo)示符。
foreign(外部引用)
使用另外一個(gè)相關(guān)聯(lián)的對(duì)象的標(biāo)識(shí)符。和<one-to-one>聯(lián)合一起使用。
Hibernate用property節(jié)點(diǎn)來(lái)描述其他非關(guān)鍵字字段的屬性,
f)我們需要?jiǎng)?chuàng)建一個(gè)HibernateUtil類(lèi),來(lái)作為獲取Session的工具類(lèi)。
我們現(xiàn)在可以開(kāi)始Hibernate 的Session 了。我們用它來(lái)從數(shù)據(jù)庫(kù)中存取Cours。首先,我們要從SessionFactory 中獲取一個(gè)Session(Hibernate 的工作單元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
SessionFactory 負(fù)責(zé)一個(gè)數(shù)據(jù)庫(kù),也只對(duì)應(yīng)一個(gè)XML 配置文件(hibernate.cfg.xml)。你可以用喜歡的任何方式編寫(xiě)一個(gè)Servlet,包含下面的代碼,只要確保SessionFactory只創(chuàng)建一次。也就是說(shuō)你不能把它作為你的Serlvet 的實(shí)例變量。一個(gè)好辦法是用在輔助類(lèi)中用一個(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è)類(lèi)不但在它的靜態(tài)屬性中使用了SessionFactory,還使用了ThreadLocal 來(lái)為當(dāng)前工作線程保存Session。Session 不是線程安全的,代表與數(shù)據(jù)庫(kù)之間的一次操作。Session 通過(guò)SessionFactory 打開(kāi),在所有的工作完成后,需要關(guān)閉:
Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
Cours course = new Cours();
course.setCourseId("aaaaaaa");
course.setName(“bbbbbbb”)
session.save(princess);
tx.commit();
HibernateUtil.closeSession();
在Session 中,每個(gè)數(shù)據(jù)庫(kù)操作都是在一個(gè)事務(wù)(transaction)中進(jìn)行的,這樣就可以隔離開(kāi)不同的操作(甚至包括只讀操作)。我們使用Hibernate 的Transaction API 來(lái)從底層的事務(wù)策略中(本例中是JDBC 事務(wù))脫身。這樣,如果需要把我們的程序部署到一個(gè)由容器管理事務(wù)
的環(huán)境中去(使用JTA),我們就不需要更改源代碼。請(qǐng)注意,我們上面的例子沒(méi)有處理任何異常。也請(qǐng)注意,你可以隨心所欲的多次調(diào)用HibernateUtil.
currentSession(),你每次都會(huì)得到同一個(gè)當(dāng)前線程的Session。你必須確保Session 在你的數(shù)據(jù)庫(kù)事務(wù)完成后關(guān)閉,不管是在你的Servlet 代碼中,或者在ServletFilter 中,HTTP 結(jié)果返回之前。
g)創(chuàng)建訪問(wèn)bean類(lèi),來(lái)封裝調(diào)用hibernate訪問(wèn)數(shù)據(jù)庫(kù)的方法。在這里我們暫且定義該方法為StoreAccessBean.java。源文件如下:
package com.hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import java.util.Iterator;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class StoreAccessBean{
public StoreAccessBean(){
}
public void addStoreAccess(Cours store){
try {
Session session = HibernateUtil.currentSession();
Transaction trans = session.beginTransaction();
session.save(store);
trans.commit();
HibernateUtil.closeSession();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public Iterator getAllCourses()throws HibernateException
{
String queryString = "select storeaccess from storeaccess as storeaccess";
Session session = HibernateUtil.currentSession();
net.sf.hibernate.Query query = session.createQuery(queryString);
Iterator it= query.iterate();
HibernateUtil.closeSession();
return it;
}
}
該類(lèi)中有兩個(gè)方法,一個(gè)是添加一條數(shù)據(jù)的方法,另外一個(gè)是查詢(xún)的方法。
最后,編譯所有文件。在剛才新建的jsp中加入以下代碼:
com.hibernate.Cours access = new com.hibernate.Cours(); access.setCourseId("aaaaaaa");
access.setName("bbbbbb");
com.hibernate.StoreAccessBean bean = new com.hibernate.StoreAccessBean();
最后,打成war包,部署到%JBOSS_HOME%\server\default\deploy目錄中,啟動(dòng)jboss,訪問(wèn)建立的jsp頁(yè)面,然后看看數(shù)據(jù)庫(kù)中是否多了一條記錄,這表明例程運(yùn)行成功。