1. Hibernate介紹
Hibernate是一個開放源代碼的O/R Mapping (對象關系映射框架),它對JDBC進行了輕量級的對象封裝,使Java程序員可以隨心所欲的使用對象編程思維來操縱數據庫。
Hibernate 是一個面向Java 環境的對象/關系數據庫映射工具。對象/關系數據庫映射(object/relational mapping (ORM))這個術語表示一種技術,用來把對象模型表示的對象映射到基于SQL 的關系模型結構中去。Hibernate 不僅僅管理Java 類到數據庫表的映射,還提供數據查詢和獲取數據的方法,可以大
幅度減少開發時人工使用SQL 和JDBC 處理數據的時間。Hibernate 的目標是對于開發者通常的數據持久化相關的編程任務,解放其中的95%。
Hibernate開發準備工作需要準備工具:
Hibernate工具,下載地址:http://www.hibernate.org.cn/download/
hibernate-2.1.2.zip,然后將其解壓縮。假設放到C:\hibernate-2.1目錄下(我們在這里設置環境變量HIBERNATE_HOME= C:\hibernate-2.1)。讓我們看看Hibernate的包情況,首先在%HIBERNATE_HOME%目錄下有一個hibernate
2.jar,該包就是我們進行Hibernate開發所需要用到的Hibernate工具包,當然Hibernate本身還需要其他lib的支持,這些支持包都在%HIBERNATE_HO
ME%\lib下面。請看表1-1,Hibernate的第三方庫
庫名稱 |
說明 |
dom4j (必需) |
Hibernate 在解析XML 配置和XML 映射元文件時需要使用dom4j。 |
CGLIB (必需) |
Hibernate 在運行時使用這個代碼生成庫強化類(與Java 反射機制聯合使用)。 |
Commons collection,Commons logging |
Hibernat 使用Apache Jakarta Commons 項目提供的多個工具類庫。
|
ODMG4 |
Hibernate 提供了一個可選的ODMG 兼容持久化管理界面。如果你需要映射集合,你就需要這個類庫,就算你不是為了使用ODMG API。 |
Log4j |
Hibernate 使用Commons Logging API,后者可以使用Log4j 作為實施log 的機制。如果把Log4j 庫放到上下文類目錄中,Commons Logging就會使用Log4j 和它在上下文類路徑中找到的log4j.properties 文件。在Hibernate 發行包中包含有一個示例的properties 文件。所以,也把log4j.jar 拷貝到你的上下文類路徑去吧。 |
其他文件是不是必須的 |
請察看Hibernate 發行包中的/lib/README.txt 文件。這是一個
Hibernate 發行包中附帶的第三方類庫的列表,總是保持更新。你可以在那里找到所有必需或者可選的類庫的列表。 |
表1-1
2. Hibernate的配置
首先,讓我們看看Hibernate的全局配置文件,這個配置文件可以有兩種形式,每種形式都可以完成Hibernate的配置工作,分別是hibernate.properties或者hibernate.cfg.xml,這個文件需要放到上下文路徑下面(假設在一個Web應用下,需要放到WEB-INF/classes下面),但請注意,雖然hibernate官方說法是必須放到該目錄下,其實是可以放在別的路徑下面,只需要在buildSessionFactory的時候加入配置文件的路徑就可以了。關于配置文件的內容,可以參看%HIBERNATE_HOME%\src目錄中hibernate.properties文件,而關于其含義解釋,可以參看:
http://www.javajia.com/article.php?id=901
http://www.hibernate.org.cn/53.html
在進入Hibernate例程之前還需要其他一些準備工作。下載應用服務器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數據庫的jdbc數據庫連接包,下載地址:
http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-3.1.4-beta.zip
當然,你還必須確定你已經安裝了j2sdk1.4.2,如果沒有請下載安裝:
http://java.sun.com/j2sk/1.4.2/download.html
還有就是你必須確定,您已經將JAVA_HOME,JBOSS_HOME,classpath等環境變量設置齊全。最好將mysql的jdbc連接包,放到%JBOSS_HOME%\
server\default\lib下面。
此外,你還需要在mysql數據庫中建立一個test庫,并且在該庫中建立一張名為courses的表,其創建語句為:
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');
這樣就創建了我們例程所需要的表和測試數據。此外我們還需要配置JBoss的數據源,在此之前,請啟動JBoss,如若不能正常啟動,請查看環境變量配置情況,或者查看JBoss網站。找到%JBOSS_HOME%\server\default\deploy目錄,查看該目錄下是否有mysql-ds.xml的mysql數據源配置文件。如果沒有請新建該文件,該文件內容如下:
<?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數據源。其名稱為MySqlDS。啟動JBoss,注意控制臺輸出信息,已經將數據源邦定到java:/MySqlDS的JNDI名稱。
3. Hibernate例程
a) 打開JBuilder新建一個Project,將其命名為TestHibernate。然后
將%HIBERNATE_HOME%\hibernate2.jar以及%HIBERNATE_HOME%\lib下面所有以上提到的包(你也可以將其下所有的包)放到一個JBuilder library下,將這個library引入TestHibernate工程的classpath下面。這樣就將所有hibernate所需要的包引入了。
b) 建立第一個可持久化類。新建一個類,將其package設com.hibernate
并且將其命名為Cours.java,其代碼如下:
package com.hibernate;
import java.util.Set;
/**
*在hibernate中代表了Course表的類。
*/
public class Cours
{
/**每個屬性和表的一個字段對應**/
private String courseId;
private String name;
/**students表示course中的學生,在后面才會用到,暫時不管**/
private Set students;
/**屬性的訪問方法**/
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)建立一個Jsp頁面以便JBuilder生成defaultroot目錄結構,編譯java源文件。
d)這個時候我們就可以找到該JBuilder工程所在的目錄,找到defaultroot下面的classes目錄,在該目錄中手工建立一個Hibernate配置文件hibernate.cfg.xml,現在我們來看配置該文件。
<!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會連接到MySql數據庫,參考前文提到的Hibernate配置文檔,這是就需要我們配置hibernate.dialect方言屬性,該屬性說明hibernate需要連接的數據庫類型,在此我們設置為MySql類型。將hibernate.dialect設置成net.sf.hibernate.dialect.MySQLDialect。
而訪問數據庫的數據源,我們在此設置成了java:/MySqlDS。我們需要注意,Hibernate自帶一個連接池配置方案(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的源代碼,會發現Hibernate自帶的連接池方案是一個非常原始的連接池,所以我們在此不用。Hibernate總共有三種連接吃方案:Hibernate自帶連接池、Hibernate帶的DBCP連接池、App Server的連接池。當然,我們選擇的是最后一種方案,采用App Server提供連接池的方案。
而hibernate.show_sql屬性是讓用戶能夠購在調試程序的時候通過控制臺看到sql語句的輸出,以便用戶調試,在這里我們選擇了關閉該輸出,當然你也可以打開該輸出,只要將該屬性設置為true值。
對于hibernate.jndi.class和hibernate.jndi.url屬性,該屬性是當你如果想從遠程客戶端直接調用處在不同的JVM下的資源的時候,你就需要通過這兩個屬性查找JNDI資源,這里設置的是通過jnp提供的NamingContextFactory來查找jnp://localhost:1099這個服務下的資源。但是需要注意的是:JBoss配置的DataSource的JNDI名稱是java:/MySqlDS,JBoss是不支持遠程客戶端對其JNDI資源的調用。所以在這里這兩個屬性并沒有實際意義,而當我們將JBoss換成如Weblogic等的應用服務器的時候,就可以在遠程客戶端通過查找JNDI名稱來獲得處于另外JVM下的服務器資源,例如連接池數據源。
對于mapping映射屬性,這是通知hibernate,讓其知道現在已經有一個映射文件來映射數據庫中的表,這個文件名是Cours.hbm.xml。
有關hibernate.cfg.xml或者hibernate.properties文件的更多配置信息,請參看前文提到的參考地址。
e)下面讓我們看看上節提到的Cours.hbm.xml,同樣,這個文件需要
放到defaultroot\classes目錄下。該文件的內容如下:
<?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>
首先讓我們來看class節點,該節點描述數據庫中的一個表對應我們創建的一個可持久化類(該持久化類類似于JavaBean,通過getter和setter方法訪問)。在這里我們可以看到name為com.hibernate.Cours的可持久化類對應于table為Courses的數據庫表,dynamic-update屬性用來標識是否對update的sql語句在運行時動態生成,并且之更新那些改變過的字段。在這里我們不需要過于關注這個屬性。
其次,觀察id子節點,該節點表示了表的關鍵字段,name=”courseId”表明了com.hibernate.Cours類中的對應courses表主關鍵字的attribute屬性是courseId。而對應的字段是column="CourseId",類型為type="string"
而對于unsaved-value="any" 一個特定的標識屬性值,用來標志該實例是剛
剛創建的,尚未保存。這可以把這種實例和從以前的session 中裝載過但未再次持久化的實例區分開來。<generator>節點定義該主關鍵字的生成規則,assign表示該主關鍵字生成規則為用戶在save之前為對象分配主關鍵字標識符。關鍵字生成規則有以下幾種類型:
increment(遞增)
用于為long, short 或者int 類型生成唯一標識。只有在沒有其他進程往同一張表中插入數據時才能使用。在集群下不要使用。
Identity
對DB2,MySQL, MS SQL Server, Sybase 和HypersonicSQL 的內置標識字段提供支持。返回的標識符是long, short 或者int 類型的。
sequence (序列)
在DB2,PostgreSQL, Oracle, SAP DB, McKoi 中使用序列(sequence),而在Interbase 中使用生成器(generator)。返回的標識符是long, short 或者 int 類型的。
hilo (高低位)
使用一個高/低位算法來高效的生成long, short 或者 int 類型的標識符。給定一個表和字段(默認分別是是hibernate_unique_key 和next)作為高位值得來源。高/低位算法生成的標識符只在一個特定的數據庫中是唯一的。在使用JTA 獲得的連接或者用戶自行提供的連接中,不要使用這種生成器。
seqhilo(使用序列的高低位)
使用一個高/低位算法來高效的生成long, short 或者 int 類型的標識符,給定一個數據庫序列(sequence)的名字。
uuid.hex
用一個128-bit 的UUID 算法生成字符串類型的標識符。在一個網絡中唯一(使用了IP地址)。UUID 被編碼為一個32 位16 進制數字的字符串。
uuid.string
使用同樣的UUID 算法。UUID 被編碼為一個16 個字符長的任意ASCII 字符組成的字符串。不能使用在PostgreSQL 數據庫中
native(本地)
根據底層數據庫的能力選擇identity, sequence 或者hilo 中的一個。
assigned(程序設置)
讓應用程序在save()之前為對象分配一個標示符。
foreign(外部引用)
使用另外一個相關聯的對象的標識符。和<one-to-one>聯合一起使用。
Hibernate用property節點來描述其他非關鍵字字段的屬性,
f)我們需要創建一個HibernateUtil類,來作為獲取Session的工具類。
我們現在可以開始Hibernate 的Session 了。我們用它來從數據庫中存取Cours。首先,我們要從SessionFactory 中獲取一個Session(Hibernate 的工作單元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
SessionFactory 負責一個數據庫,也只對應一個XML 配置文件(hibernate.cfg.xml)。你可以用喜歡的任何方式編寫一個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();
Cours course = new Cours();
course.setCourseId("aaaaaaa");
course.setName(“bbbbbbb”)
session.save(princess);
tx.commit();
HibernateUtil.closeSession();
在Session 中,每個數據庫操作都是在一個事務(transaction)中進行的,這樣就可以隔離開不同的操作(甚至包括只讀操作)。我們使用Hibernate 的Transaction API 來從底層的事務策略中(本例中是JDBC 事務)脫身。這樣,如果需要把我們的程序部署到一個由容器管理事務
的環境中去(使用JTA),我們就不需要更改源代碼。請注意,我們上面的例子沒有處理任何異常。也請注意,你可以隨心所欲的多次調用HibernateUtil.
currentSession(),你每次都會得到同一個當前線程的Session。你必須確保Session 在你的數據庫事務完成后關閉,不管是在你的Servlet 代碼中,或者在ServletFilter 中,HTTP 結果返回之前。
g)創建訪問bean類,來封裝調用hibernate訪問數據庫的方法。在這里我們暫且定義該方法為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;
}
}
該類中有兩個方法,一個是添加一條數據的方法,另外一個是查詢的方法。
最后,編譯所有文件。在剛才新建的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目錄中,啟動jboss,訪問建立的jsp頁面,然后看看數據庫中是否多了一條記錄,這表明例程運行成功。