作者 |
|
此文章已经观看 466 次 而且有 28 篇回复 |
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
一。入门 1。簡介Hibernate 傳統的資料庫程式設計,必須直接在程式中硬編碼(hard code)SQL陳述,JDBC統一了Java程式與資料庫之間的操作介面,讓程式設計人員可以不用關係與資料庫特定相關的API操作,然而撰寫SQL陳述或自行封裝SQL仍是不可避免或必要的目標,而在物件導向程式設計中,物件與物件之間的關係,在匹配到關聯式資料庫中表格與表格之間的關係,並無法進行簡單的轉換以進行匹配。
Hibernate是「物件/關係對應」(Object/relational mapping)的解決方案,簡寫為ORM,所謂的ORM,簡單的說就是將Java中的物件與物件關係,對應到關聯式資料庫中的表格與表格之間的關係,Hibernate提供了這個過程中自動化對應轉換的方案,相反的,也提供關聯式資料庫中表格與表格之間的關係,對應至Java程式中,物件與物件的關係。
Hibernate在Java程式與資料庫之間進行轉換,Java程式設計人員只要事先定義好物件與資料庫表格之間的對應,之後Java程式設計人員可以用所熟悉的物件導向程式方法撰寫程式,而不用特定轉換SQL,所有SQL的轉換交由Hibernate進行處理。
Hibernate的官方網站在: http://www.hibernate.org/
有關Hibernate介紹的簡體中文網站在: http://www.hibernate.org.cn/
想要學習Hibernate,可以從官方網站的Hibernate参考手册開始,在上面的簡體中文網站中,有Hibernate參考手冊的簡體中文翻譯,這可以當作設定Hibernate相關功能時的參考手冊,書籍方面,可以看Manning的Hibernate in Action與Oreilly的Hibernate: A Developer's Notebook,Hibernate in Action當中介紹了很多關於持久層設計的觀念與理論,而A Developer's Notebook當中提供了較多實作的範例參考,另外,也可以在網路上找夏昕的Hibernate開發指南,可以讓您在短時間內瞭解Hibernate的概貌。
在我所撰寫的介紹中,主要是學習過程中,將一些配置及較關鍵性的觀念作一個記錄,但必要強調的是,有關於Hibernate的學習,並不完全在於如何設定與使用Hibernate,而是在於學習當中持久層設計的概念,這些概念如果能在學習Hibernate的過程中體會並吸收,日後即使不使用Hibernate撰寫程式,也可以發揮Hibernate持久層設計的概念於其他程式之中。
資料庫的設計是我正在學習的,有關於Hibernate持久層設計的概念,我個人的知識與學習並不足於讓我在這邊造次,這邊所建議的是看看Hibernate in Action,初看您會覺得當中儘是文字性描述,缺少範例說明,但多看個幾次,慢慢的您會發現,當中有相當多的觀念您只要瞭解,自然就知道如何使用了,設定方面通常您只要看看參考手冊按當中的說明進行就可以了。
2。Hibernate所需類別庫
Hibernate是ORM的解決方案,其底層對資料庫的操作依賴於JDBC,所以您必須先取得JDBC驅動程式,在這邊我們使用的是MySQL,所以您必須至以下網址先取得MySQL的JDBC驅動程式: http://www.mysql.com/products/connector/j/
接下來取得Hibernate,在撰寫此文的同時,Hibernate最後的穩定版本是2.1.6,而3.0版還在測試階段,這邊的介紹將以2.1.6為主,所以請至以下網址取得hibernate-2.1.6.zip: http://www.hibernate.org/
解開hibernate-2.1.6.zip後,當中的hibernate2.jar是必要的,而在lib目錄中還包括了許多jar檔案,其中dom4j、CGLIB、Commons Collections、Commons Logging、ODMG4、EHCache是必要的,而Log4j則是建議使用的,為何使用這些jar,在Hibernate參考手冊中有說明,您可以開啟doc\reference中的參考手冊,有英文版與簡體中文版的介紹,檔案格式則提供有html與pdf兩種,以下列出簡體中文中的說明:
dom4j(必需):Hibernate在解析XML配置和XML映射元文件時需要使用dom4j。
CGLIB(必需):Hibernate在運行時使用這個代碼生成庫強化類(與Java反射機制聯合使用)。
Commons Collections, Commons Logging(必需):Hibernat使用Apache Jakarta Commons項目提供的多個工具類庫。
ODMG4(必需):Hibernate提供了一個可選的ODMG兼容持久化管理界面。如果你需要映射集合,你就需要這個類庫,就算你不是為了使用ODMG API。
EHCache(必需):Hibernate可以使用不同的第二級Cache方案。如果沒有修改配置的話,EHCache提供默認的Cache。
Log4j(可選):Hibernate使用Commons Logging API,後者可以使用Log4j作為底層實施log的機制。如果上下文類目錄中存在Log4j庫,Commons Logging就會使用Log4j和它在上下文類路徑中找到的log4j.properties文件。在Hibernate發行包中包含有一個示例的properties文件。所以,如果你想看看幕後到底發生了什麼,也把log4j.jar拷貝到你的上下文類路徑去吧(它位於src/目錄中)。
以上是Hibernate參考手冊所列出的jar檔案,Hibernate底層還需要Java Transaction API,所以您還需要jta.jar,到這邊為止,總共需要十個jar檔案: 代碼: mysql-connector-java-3.0.14-production-bin.jar jta.jar hibernate2.jar cglib-full-2.0.2.jar commons-collections-2.1.1.jar commons-logging-1.0.4.jar dom4j-1.4.jar ehcache-0.9.jar log4j-1.2.8.jar odmg-3.0.jar
其它的jar檔案則視您的需要來設定,例如您應該也會使用到Ant,這對於自動化建構Hibernate有相當的幫助,您可以先查看我另一個版面上有關於Ant的介紹: http://www.caterpillar.onlyfun.net/phpBB2/viewtopic.php?t=1354
Hibernate可以運行於單機之上,也可以運行於Web應用程式之中,如果是運行於單機,則將所有用到的jar檔案(包括JDBC驅動程式)設定至CLASSPATH中,如果是運行於Web應用程式中,則將jar檔案置放於WEB-INF/lib中,其中JDBC驅動程式也可以依賴於JNDI來取得資源,設定的方式之後介紹,或者您也可以先看看這篇文章有關於DBCP的介紹: http://www.caterpillar.onlyfun.net/phpBB2/viewforum.php?f=29
準備好這些檔案後,我們下一個主題將介紹一個快速入門的例子。
3。第一個Hibernate程式
這邊以一個簡單的單機程式來示範Hibernate的配置與功能,在這個例子中的一些操作,實際上會使用一些自動化工具來完成,而不一定親自手動操作設定,這邊完全手動的原因,在於讓您可以知道Hibernate實際上會作那些動作,在進行範例之前,請先確定前一個主題中的相關jar檔案都已經設定在CLASSPATH中。
我們先作資料庫的準備工作,在MySQL中新增一個HibernateTest資料庫,並建立USER表格: 代碼: CREATE TABLE USER ( user_id CHAR(32) NOT NULL PRIMARY KEY, name VARCHAR(16) NOT NULL, sex CHAR(1), age INT );
我們先撰寫一個純Java物件,它純綷表示一個資料集合,待會我們會將之映射至資料庫的表格上,程式如下: 代碼: package onlyfun.caterpillar;
public class User { private String id; private String name; private char sex; private int age;
public int getAge() { return age; }
public String getId() { return id; }
public String getName() { return name; }
public char getSex() { return sex; }
public void setAge(int i) { age = i; }
public void setId(String string) { id = string; }
public void setName(String string) { name = string; }
public void setSex(char c) { sex = c; } }
其中id是個特殊的屬性,Hibernate會使用它來作為主鍵識別,我們可以定義主鍵產生的方式,這是在XML映射文件中完成,為了告訴Hibernate物件如何映射至資料庫表格,我們撰寫一個XML映射文件檔名是User.hbm.xml,如下所示: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
這個XML文件定義了物件屬性映射至資料庫表格的關係,您可以很簡單的瞭解對映的方法,像是User物件對應至USER表格,其中我們使用uuid.hex來定義主鍵的產生算法,UUID算法使用IP地址、JVM的啟動時間、系統時間和一個計數值來產生主鍵。除了使用uuid.hex之外,我們還可以使用其它的方式來產生主鍵,像是increment等,這可以在Hibernate參考手冊中找到相關資料。
標籤用於定義Java物件的屬性,而當中的標籤用於定義與資料庫的對應,如果您是手工建立Java物件與資料庫表格,則在最簡單的情況下,可以只定義這樣的方式,而由Hibernate自動判斷Java物件屬性與資料庫表格名稱對應關係,在與標籤上的額外設定(像是not null、sql-type等),則可以用於自動產生Java物件與資料庫表格的工具上。
接下來我們定義Hibernate配置文件,主要是進行SessionFactory配置,Hibernate可以使用XML或屬性文件來進行配置,我們這邊先介紹如何使用XML配置,這也是Hibernate所建議的配置方式,我們的檔名是hibernate.cfg.xml,如下: 代碼: <?xml version='1.0' encoding='big5'?> BR>PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
true
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
jdbc:mysql://localhost/HibernateTest
caterpillar
123456
接下來我們撰寫一個測試的程式,這個程式將直接以Java程式設計人員熟悉的語法方式來操作物件,而實際上也直接完成對資料庫的操作,程式將會將一筆資料存入表格之中: 代碼: import onlyfun.caterpillar.*; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*;
public class HibernateTest { public static void main(String[] args) throws HibernateException { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
User user = new User(); user.setName("caterpillar"); user.setSex('M'); user.setAge(28);
Session session = sessionFactory.openSession(); Transaction tx= session.beginTransaction(); session.save(user); tx.commit(); session.close(); sessionFactory.close();
System.out.println("新增資料OK!請先用MySQL觀看結果!"); } }
Configuration代表了Java物件至資料庫的映射設定,這個設定是從我們上面的XML而來,接下來我們從Configuration取得SessionFactory物件,並由它來開啟一個Session,它代表物件與表格的一次會話操作,而Transaction則表示一組會話操作,我們只需要直接操作User物件,並進行Session與Transaction的相關操作,Hibernate就會自動完成對資料庫的操作。這邊對程式先只作簡單的介紹,之後再詳加說明。
將所有的.java檔案編譯,並將兩個XML檔案放置在與HibernateTest相同的目錄中,也就是檔案位置如下: 代碼: / |--HibernateTest.class |--User.hbm.xml |--hibernate.cfg.xml /onlyfun /caterpillar |--User.class
OK!現在您可以執行HibernateTest,程式將會出現以下的訊息: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: insert into USER (name, sex, age, user_id) values (?, ?, ?, ?) 新增資料OK!請先用MySQL觀看結果!
這邊只先進行資料的存入,要觀看資料存入的結果的話,請進入MySQL觀看,以下是資料庫存入的結果: 代碼: mysql> SELECT * FROM USER; +----------------------------------+-------------+------+------+ | user_id | name | sex | age | +----------------------------------+-------------+------+------+ | 297e3dbdfea6023d00fea60241000001 | caterpillar | M | 28 | +----------------------------------+-------------+------+------+ 1 rows in set (0.00 sec)
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
4。配置文件
Hibernate可以使用XML或屬性檔案來配置SessionFactory,預設的配置文件名稱為hibernate.cfg.xml或hibernate.properties。
上一個主題中所示範的為使用XML文件的方式,一個XML文件的例子如下: 代碼: <?xml version='1.0' encoding='big5'?> BR>PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
true
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
jdbc:mysql://localhost/HibernateTest
caterpillar
123456
使用XML文件進行配置時,可以在當中指定物件與資料庫表格的映射文件位置,XML配置文件的位置必須在CLASSPATH的設定中,例如單機執行時主程式的位置,或是Web程式的WEB-INF/classes中,我們使用下面的方式來讀入XML文件以配置Hibernate: 代碼: SessionFactory sf = new Configuration().configure().buildSessionFactory();
Configuration表示Java物件與資料庫表格映射的集合,並用於之後建立SessionFactory,之後Configuration就不再有作用。預設的XML文件名稱是hibernate.cfg.xml,您也可以指定文件的名稱,例如: 代碼: SessionFactory sf = new Configuration() .configure("db.cfg.xml") .buildSessionFactory();
除了使用XML文件進行配置,我們也可以使用屬性檔案進行配置,檔案名稱是hibernate.properties,一個例子如下: 代碼: hibernate.show_sql = true hibernate.dialect = net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class = com.mysql.jdbc.Driver hibernate.connection.url = jdbc:mysql://localhost/HibernateTest hibernate.connection.username = caterpillar hibernate.connection.password = 123456
hibernate.properties的位置必須在CLASSPATH的設定中,例如單機執行時主程式的位置,或是Web程式的WEB-INF/classes中,而為了要取得物件至資料庫表格的映射文件,我們必須在程式中如下載入: 代碼: Configuration cfg = new Configuration() .addClass(onlyfun.caterpillar.User.class) .addClass(onlyfun.caterpillar.Item.class);
這麼一來,程式會自動載入onlyfun/caterpillar/User.hbm.xml與onlyfun/caterpillar/Item.hbm.xml,完成Hibernate配置之後,我們可以如下取得SessionFactory: 代碼: SessionFactory sessions = cfg.buildSessionFactory();
其它更多有關Hibernate配置的細節,您可以查看Hibernate參考手冊。
5。提供JDBC連接
如果需要的話,您可以自行提供JDBC連接物件給Hibernate使用,而無需透過配置文件設定JDBC來源,一個最簡單的例子如下: 代碼: Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/HibernateTest?user=root&password="; java.sql.Connection conn = DriverManager.getConnection(url); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(conn);
當然您也可以透過屬性文件hibernate.properties來配置JDBC來源,例如: 代碼: hibernate.show_sql = true hibernate.dialect = net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class = com.mysql.jdbc.Driver hibernate.connection.url = jdbc:mysql://localhost/HibernateTest hibernate.connection.username = caterpillar hibernate.connection.password = 123456
如果是透過XML文件hibernate.cfg.xml則是如下進行配置: 代碼: <?xml version='1.0' encoding='big5'?> BR>PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
true
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
jdbc:mysql://localhost/HibernateTest
caterpillar
123456
Hibernate在資料庫連接池的使用上是可選的,您可以使用C3P0連接池,當您的屬性文件中含有hibernate.c3p0.*的配置時,就會自動啟用C3P0連接池,而您的CLASSPATH中必須包括c3p0-0.8.4.5.jar,屬性文件hibernate.properties的配置範例如下: 代碼: hibernate.show_sql = true hibernate.dialect = net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class = com.mysql.jdbc.Driver hibernate.connection.url = jdbc:mysql://localhost/HibernateTest hibernate.connection.username = root hibernate.connection.password = hibernate.c3p0.min_size=5 hibernate.c3p0.max_size=20 hibernate.c3p0.timeout=1800 hibernate.c3p0.max_statements=50
如果是使用hibernate.cfg.xml配置C3P0連接池的例子如下: 代碼: <?xml version='1.0' encoding='big5'?> BR>PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
true
net.sf.hibernate.dialect.MySQLDialect
com.mysql.jdbc.Driver
jdbc:mysql://localhost/HibernateTest
root
5 20 1800 50
您也可以使用Proxool或DBCP連接池,只要在配置文件中配置hibernate.proxool.*或hibernate.dbcp.*等相關選項,這可以在hibernate的etc目錄中找hibernate.properties中的配置例子來參考,當然要記得在CLASSPATH中加入相關的jar檔案。
如果您使用Tomcat的話,您也可以透過它提供的DBCP連接池來取得連接,您可以先參考這邊的文章來設定Tomcat的DBCP連接池: http://www.caterpillar.onlyfun.net/phpBB2/viewtopic.php?t=1354
設定好容器提供的DBCP連接池之後,您只要在配置文件中加入connection.datasource屬性,例如在hibernate.cfg.xml中加入: 代碼: java:comp/env/jdbc/dbname
如果是在hibernate.properties中的話,則加入: 代碼: hibernate.connection.datasource = java:comp/env/jdbc/dbname
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
6。基本資料查詢
使用Hibernate進行資料查詢是一件簡單的事,Java程式設計人員可以使用物件操作的方式來進行資料查詢,查詢時使用一種類似SQL的HQL(Hibernate Query Language)來設定查詢的條件,與SQL不同的是,HQL是具備物件導向的繼承、多型等特性的語言。
直接使用範例來看看如何使用Hibernate進行資料庫查詢,在這之前,請先照之前介紹過的主題在資料庫中新增幾筆資料: http://www.caterpillar.onlyfun.net/phpBB2/viewtopic.php?t=1375
查詢資料時,我們所使用的是Session的find()方法,並在當中指定HQL設定查詢條件,查詢的結果會裝載在List物件中傳回,您所需要的是將它們一一取出,一個最簡單的例子如下: 代碼: import onlyfun.caterpillar.*; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; import java.util.*;
public class HibernateTest { public static void main(String[] args) throws HibernateException { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory(); Session session = sessionFactory.openSession();
List users = session.find("from User");
session.close(); sessionFactory.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); } } }
find()中的"from User"即HQL,User指的是User類別,藉由映射文件,它將會查詢USER表格中的資料,相當於SQL中的SELECT * FROM USER,實際上我們的User類別是位於onlyfun.caterpillar下,Hibernate會自動看看import中的package名稱與類別名稱是否符合,您也可以直接指定package名稱,例如: 代碼: session.find("from onlyfun.caterpillar.User");
這個程式的運行結果可能是這樣的: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ caterpillar Age: 28 Sex: M momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35 Sex: F
上面所介紹的查詢是最簡單的,只是從資料表中查詢所有的資料,Hibernate所查詢得回的資料,是以物件的方式傳回,以符合程式中操作的需要,我們也可以限定一些查詢條件,並只傳回我們指定的欄位,例如: 代碼:
List names = session.find("select user.name from User as user where age = 25"); for (ListIterator iterator = names.listIterator(); iterator.hasNext(); ) { String name = (String) iterator.next(); System.out.println("name: " + name); }
在find()中的HQL示範了條件限定的查詢,User as user為User類別取了別名,所以我們就可以使用user.name來指定表格傳回欄位,where相當於SQL中的WHERE子句,我們限定查詢age等於25的資料,這次查詢的資料只有一個欄位,而型態是String,所以傳回的List內容都是String物件,一個運行的例子如下: 代碼:
log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.name as x0_0_ from USER user0_ where (age=25 ) name: momor name: Bush
如果要傳回兩個以上的欄位,也不是什麼問題,直接來看個例子: 代碼: List results = session.find("select user.name, user.age from User as user where sex = 'F'"); for (ListIterator iterator = results.listIterator(); iterator.hasNext(); ) { Object[] rows = (Object[]) iterator.next(); String name = (String) rows[0]; Integer age = (Integer) rows[1]; System.out.println("name: " + name + "\n\t" + age); }
從上面的程式中不難看出,傳回兩個以上欄位時,每一次ListIterator會以Object陣列的方式傳回一筆資料,我們只要指定陣列索引,並轉換為適當的型態,即可取得資料,一個查詢的結果如下: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.name as x0_0_, user0_.age as x1_0_ from USER user0_ where (sex='F' ) name: momor 25 name: Becky 35
您也可以在HQL中使用一些函數來進行結果統計,例如: 代碼: List results = session.find("select count(*), avg(user.age) from User as user"); ListIterator iterator = results.listIterator(); Object[] rows = (Object[]) iterator.next(); System.out.println("資料筆數: " + rows[0] + "\n平均年齡: " + rows[1]);
一個查詢的結果如下所示: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select count(*) as x0_0_, avg(user0_.age) as x1_0_ from USER user0_ 資料筆數: 4 平均年齡: 28.25
這邊我們先介紹的是一些簡單的查詢動作,將來有機會的話,再介紹一些進階的查詢,如果您有想要先認識一些HQL,可以看看參考手冊的第11章,當中對於HQL有詳細的說明。
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
7。Query介面
除了直接使用find()方法並配合HQL來進行查詢之外,我們還可以透過net.sf.hibernate.Query介面的實例來進行查詢,透過Query介面,您可以先設定查詢參數,之後透過setXXX()等方法,將指定的參數值填入,而不用每次都撰寫完整的HQL,直接來看個例子: 代碼: Query query = session.createQuery("select user.name from User as user where user.age = ? and user.sex = ?"); query.setInteger(0, 25); query.setCharacter(1, 'M');
List names = query.list(); for (ListIterator iterator = names.listIterator(); iterator.hasNext(); ) { String name = (String) iterator.next(); System.out.println("name: " + name); }
在設定參數值時,必須依照 ? 所設定的順序,並使用對應型態的setXXX()方法,一個執行的例子如下: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.name as x0_0_ from USER user0_ where (user0_.age=? )and(user0_.sex=? ) name: Bush
您可以使用命名參數(Named Parameter)來取代這個方法,這可以不用依照特定的順序來設定參數值,並擁有較好的可讀性,直接來看個例子: 代碼: Query query = session.createQuery("select user.name from User as user where user.age = :age and user.sex = :sex"); query.setInteger("age", 25); query.setCharacter("sex", 'M');
List names = query.list(); for (ListIterator iterator = names.listIterator(); iterator.hasNext(); ) { String name = (String) iterator.next(); System.out.println("name: " + name); }
設定命名參數時,在建立Query時先使用:後跟著參數名,之後我們就可以在setXXX()方法中直接指定參數名來設定參數值,而不用依照特定的順序。
我們也可以將HQL撰寫在程式之外,以避免硬編碼(hard code)在程式之中,在需要修改HQL時就很方便,在*.hbm.xml中使用標籤,並在之間撰寫HQL,撰寫的位置是在之前,例如: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
select user.name from User as user where user.age = :age and user.sex = :sex ]]>
的name屬性用來設定查詢外部HQL時的名稱依據,使用的例子如下: 代碼:
Query query = session.getNamedQuery("onlyfun.caterpillar.queryUser"); query.setInteger("age", 25); query.setCharacter("sex", 'M');
List names = query.list(); for (ListIterator iterator = names.listIterator(); iterator.hasNext(); ) { String name = (String) iterator.next(); System.out.println("name: " + name); }
更多有關於Hibernate查詢的操作,您可以查看參考手冊的第九章內容。
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
8。 更新、刪除資料
如果您是在同一個Session中取出資料並想要馬上進行更新,則只要先查詢並取出物件,透過setXXX()方法設定好新的值,然後呼叫session.flush()即可在同一個Session中更新指定的資料,例如: 代碼: import onlyfun.caterpillar.*; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; import java.util.*;
public class HibernateTest { public static void main(String[] args) throws HibernateException { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession(); List users = session.find("from User");
User updated = null;
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); if(updated == null) updated = user;
System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); }
updated.setName("justin"); session.flush();
users = session.find("from User");
session.close(); sessionFactory.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); } } }
這個程式會顯示資料表中的所有資料,並將資料表中的第一筆資料更新,一個執行的結果如下: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ caterpillar Age: 28 Sex: M momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35 Sex: F Hibernate: update USER set name=?, sex=?, age=? where user_id=? Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ justin Age: 28 Sex: M momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35 Sex: F
如果您開啟了一個Session,從資料表中取出資料顯示到使用者介面上,之後關閉Session,當使用者在介面上操作完畢並按下儲存時,這時您要重新開啟一個Session,使用update()方法將物件中的資料更新至對應的資料表中,一個例子如下: 代碼: import onlyfun.caterpillar.*; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; import java.util.*;
public class HibernateTest { public static void main(String[] args) throws HibernateException { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession(); List users = session.find("from User"); // 關閉這個Session session.close();
User updated = null;
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); if(updated == null) updated = user;
System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); }
// 使用者作一些操作,之後儲存 updated.setName("caterpillar");
// 開啟一個新的Session session = sessionFactory.openSession(); // 更新資料 session.update(updated); users = session.find("from User"); session.close(); sessionFactory.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); } } }
這個程式執行的結果範例如下,您可以看看實際上執行了哪些SQL: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ justin Age: 28 Sex: M momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35
Sex: F Hibernate: update USER set name=?, sex=?, age=? where user_id=? Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ caterpillar Age: 28 Sex: M momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35 Sex: F
Hibernate提供了一個saveOrUpdate()方法,為資料的儲存或更新提供了一個統一的操作介面,藉由定義映射文件時,設定標籤的unsaved-value來決定什麼是新的值必需,什麼是已有的值必須更新: 代碼:
unsaved-value可以設定的值包括: *any - 總是儲存 *none - 總是更新 *null - id為null時儲存(預設) *valid - id為null或是指定值時儲存
這樣設定之後,您可以使用session.saveOrUpdate(updated);來取代上一個程式的session.update(updated);方法。
如果要刪除資料,只要使用delete()方法即可,直接看個例子。 代碼: import onlyfun.caterpillar.*; import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; import java.util.*;
public class HibernateTest { public static void main(String[] args) throws HibernateException { SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession(); List users = session.find("from User"); User updated = null;
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); if(updated == null) updated = user;
System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); }
session.delete(updated); users = session.find("from User"); session.close(); sessionFactory.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); } } }
一個執行的結果範例如下: 代碼: log4j:WARN No appenders could be found for logger (net.sf.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ justin Age: 28 Sex: M momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35 Sex: F Hibernate: delete from USER where user_id=? Hibernate: select user0_.user_id as user_id, user0_.name as name, user0_.sex as sex, user0_.age as age from USER user0_ momor Age: 25 Sex: F Bush Age: 25 Sex: M Becky Age: 35 Sex: F
Hibernate對於資料的更新、刪除等動作,是依懶id值來判定,如果您已知id值,則可以使用load()方法來載入資料,例如: 代碼: User user = (User) session.load(User.class, id);
更多有關於Hibernate資料更新操作的說明,您可以看看參考手冊的第九章內容
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
9. Hibernate Gossip - 繼承映射(1)
如果應用程式中的物件有繼承的關係,我們可以有三種策略將這種關係映射至資料表上。
最簡單的方式就是給每個物件一個表格,如果父類別User中有field1、field2兩個屬性,其表格USER有FIELD1、FIELD2與之對應,而子類別SubUser若繼承了父類別的field1、field2屬性,表格中SUBUSER中也要擁有FIELD1、FIELD2與之對應,這種方法的好處只有映射上的方便,很顯然的,父類與子類共有的屬性,會變成在資料庫表格中重複的欄位,而且很難實現多型操作,建議只有在不需要多型操作時使用,要執行這種映射,為每一個子類別撰寫一個映射文件就是了,沒什麼特別的設定。
第二種方式是將所有繼承同一父類別的物件儲存在同一個表格中,表格中使用識別欄位來表示某一列(row)是屬於某個子類別或父類別,這種方式方便執行多型操作,而且兼具效能上的考量,在這個主題中我們將先說明這個方法。
我們先來看看我們撰寫的類別與繼承關係,首先是父類別: 代碼: package onlyfun.caterpillar;
public class User { private String id; private String name; private String password;
public String getId() { return id; }
public String getName() { return name; }
public String getPassword() { return password; }
public void setId(String string) { id = string; }
public void setName(String string) { name = string; }
public void setPassword(String password) { this.password = password; } }
再來是繼承User類別的兩個子類別,首先是PowerUser類別: 代碼: package onlyfun.caterpillar;
public class PowerUser extends User { private int level; private String otherOfPower;
public int getLevel() { return level; }
public String getOtherOfPower() { return otherOfPower; }
public void setLevel(int level) { this.level = level; }
public void setOtherOfPower(String otherOfPower) { this.otherOfPower = otherOfPower; } }
下面是繼承User類別的GuestUser類別: 代碼: package onlyfun.caterpillar;
public class GuestUser extends User { private String otherOfGuest;
public String getOtherOfGuest() { return otherOfGuest; }
public void setOtherOfGuest(String otherOfGuest) { this.otherOfGuest = otherOfGuest; } }
映射文件中該如何撰寫,由於這些類別將映射至同一個表格,我們使用discriminator作為每個類別記錄在表格中的識別,先直接看看映射文件如何撰寫: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
在表格中,我們增加一個欄位DISCRIMINATOR_USERTYPE來記錄儲存的類別是屬於User、PowerUser或是GuestUser的記錄,如果該欄位是ParentUser,則表示該筆資料是User類別,如果是POWER,表示是PowerUser的記錄,如果是GUEST,表示是GuestUser的記錄,在映射子類別時,使用指明映射的子類別以及其discriminator-value。
我們可以在資料庫中建立資料表格如下: 代碼: create table USER ( ID char(32) not null, DISCRIMINATOR_USERTYPE varchar(255) not null, NAME varchar(16) not null, PASSWORD varchar(16) not null, POWERUSER_LEVEL integer, POWER_OTHER varchar(255), GUEST_OTHER varchar(255), primary key (ID) );
您可以將資料表的建立工作,透過SchemaExportTask來自動建立,您可以參考這篇介紹: http://www.caterpillar.onlyfun.net/phpBB2/viewtopic.php?t=1383
假設我們在程式中如下儲存資料的話: 代碼: PowerUser pu = new PowerUser(); pu.setName("caterpillar"); pu.setPassword("123456"); pu.setLevel(1); pu.setOtherOfPower("PowerUser's field");
GuestUser gu = new GuestUser(); gu.setName("momor"); gu.setPassword("654321"); gu.setOtherOfGuest("GuestUser's field");
Session session = sessionFactory.openSession(); Transaction tx= session.beginTransaction(); session.save(pu); session.save(gu);
tx.commit(); session.close();
則資料表中將會有以下的內容(沒有顯示ID欄位): 代碼: +------------------------+-------------+----------+-----------------+-------------------+-------------------+ | DISCRIMINATOR_USERTYPE | NAME | PASSWORD | POWERUSER_LEVEL | POWER_OTHER | GUEST_OTHER | +------------------------+-------------+----------+-----------------+-------------------+-------------------+ | POWER | caterpillar | 123456 | 1 | PowerUser's field | NULL | | GUEST | momor | 654321 | NULL | NULL | GuestUser's field | +------------------------+-------------+----------+-----------------+-------------------+-------------------+
您可以觀察實際的儲存方式,注意DISCRIMINATOR_USERTYPE欄位,它用以標示該列屬於哪一個類別的資料,如果要查詢資料的話,例如查詢所有PowerUser的資料,我們只要如下進行: 代碼: Session session = sessionFactory.openSession();
List users = session.find("from PowerUser"); session.close(); for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { PowerUser user = (PowerUser) iterator.next(); System.out.println(user.getName() + "\n\tPassword: " + user.getPassword());
System.out.println("\tPower: " + user.getOtherOfPower() + "\n\tLevel: " + user.getLevel()); }
您可以觀察Hibernate真正所執行的SQL的內容,看看where就知道它如何查詢PowerUser的資料: 代碼: select poweruser0_.ID as ID, poweruser0_.POWERUSER_LEVEL as POWERUSE5_, poweruser0_.POWER_OTHER as POWER_OT6_, poweruser0_.NAME as NAME, poweruser0_.PASSWORD as PASSWORD from USER poweruser0_ where poweruser0_.DISCRIMINATOR_USERTYPE='POWER';
使用session.find("from GuestUser");就可以查詢GuestUser的資料,您也可以取回所有User型態的資料,例如: 代碼: Session session = sessionFactory.openSession();
List users = session.find("from User"); session.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName() + "\n\tPassword: " + user.getPassword()); if(user instanceof PowerUser) System.out.println("\tPower: " + ((PowerUser)user).getOtherOfPower() + "\n\tLevel: " + ((PowerUser)user).getLevel()); else System.out.println("\tGuest: " + ((GuestUser)user).getOtherOfGuest()); }
Hibernate可以使用父類別來取得所有的子類別資料,我們知道所有的Java類別都繼承自Object,所以如果您使用session.find("from java.lang.Object");,就將會取回資料庫中所有表格的資料。
有關於繼承關係映射的第三種作法,將留待下一個主題說明。
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
10 .Hibernate Gossip - 繼承映射(2)
接續上一個主題,我們來看看繼承關係映射的第三種方式,我們給予父類別與每個子類別一個表格,與第一個方法不同的是,父類別映射的表格與子類別映射的表格共享相同的主鍵值,父類別表格只記錄本身的屬性,如果要查詢的是子類別,則透過外鍵參考從父類別表格中取得繼承而來的屬性資料。
直接以圖片說明會比較容易理解,我們使用前一個主題中的User、PowerUser與GuestUser類別作說明,類別繼承圖如下:
其映射至資料庫表格的關係如下:
其中POWER_USER與GUEST_USER表格的主鍵值將與USER表格的主鍵值相同,POWER_USER_ID與GUEST_USER_ID作為一個外鍵參考,以取得父類別映射表格的NAME與PASSWORD資料。
在映射文件中要實現這種映射,我們使用標籤,並使用標籤指定子類別表格與父類別表格共享的主鍵值,映射文件的撰寫方式如下: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
您可以自行建立資料庫與表格,當然使用SchemaExportTask幫您自動建立表格是更方便的,下面是SchemaExportTask自動建立表格時所使用的SQL: 代碼: [schemaexport] alter table GUEST_USER drop constraint FKB62739F25ED19688; [schemaexport] alter table POWER_USER drop constraint FK38F5D2E586CA74B5; [schemaexport] drop table if exists USER; [schemaexport] drop table if exists GUEST_USER; [schemaexport] drop table if exists POWER_USER; [schemaexport] create table USER ( [schemaexport] USER_ID varchar(255) not null, [schemaexport] NAME varchar(16) not null, [schemaexport] PASSWORD varchar(16) not null, [schemaexport] primary key (USER_ID) [schemaexport] ); [schemaexport] create table GUEST_USER ( [schemaexport] GUEST_USER_ID varchar(255) not null, [schemaexport] GUEST_OTHER varchar(255), [schemaexport] primary key (GUEST_USER_ID) [schemaexport] ); [schemaexport] create table POWER_USER ( [schemaexport] POWER_USER_ID varchar(255) not null, [schemaexport] POWER_USER_LEVEL integer, [schemaexport] POWER_OTHER varchar(255), [schemaexport] primary key (POWER_USER_ID) [schemaexport] ); [schemaexport] alter table GUEST_USER add index FKB62739F25ED19688 (GUEST_USER_ID), add constraint FKB62739F25ED19688 foreign key (GUEST_USER_ID) references USER (USER_ID); [schemaexport] alter table POWER_USER add index FK38F5D2E586CA74B5 (POWER_USER_ID), add constraint FK38F5D2E586CA74B5 foreign key (POWER_USER_ID) references USER (USER_ID);
至於在Java程式的撰寫方面,您可以直接使用前一個主題中所寫的測試程式(您可以看到,Hibernate將程式撰寫與資料庫處理的細節分開了),假設我們使用上一個主題的測試程式新增了兩筆資料,則資料庫表格的結果如下: 代碼: mysql> select * from user; +----------------------------------+-------------+----------+ | USER_ID | NAME | PASSWORD | +----------------------------------+-------------+----------+ | 297e3dbdff0af72900ff0af72d4b0001 | caterpillar | 123456 | | 297e3dbdff0af72900ff0af72d4b0002 | momor | 654321 | +----------------------------------+-------------+----------+ 2 rows in set (0.05 sec)
mysql> select * from power_user; +----------------------------------+------------------+-------------------+ | POWER_USER_ID | POWER_USER_LEVEL | POWER_OTHER | +----------------------------------+------------------+-------------------+ | 297e3dbdff0af72900ff0af72d4b0001 | 1 | PowerUser's field | +----------------------------------+------------------+-------------------+ 1 row in set (0.00 sec)
mysql> select * from guest_user; +----------------------------------+-------------------+ | GUEST_USER_ID | GUEST_OTHER | +----------------------------------+-------------------+ | 297e3dbdff0af72900ff0af72d4b0002 | GuestUser's field | +----------------------------------+-------------------+ 1 row in set (0.00 sec)
瞭解一下儲存資料至資料庫時所使用的SQL語句有助於您瞭解底層的運作方式,新增兩筆資料的SQL如下: 代碼: Hibernate: insert into USER (NAME, PASSWORD, USER_ID) values (?, ?, ?) Hibernate: insert into POWER_USER (POWER_USER_LEVEL, POWER_OTHER, POWER_USER_ID) values (?, ?, ?) Hibernate: insert into USER (NAME, PASSWORD, USER_ID) values (?, ?, ?) Hibernate: insert into GUEST_USER (GUEST_OTHER, GUEST_USER_ID) values (?, ?)
有興趣的話,也可以看一下查詢資料時的SQL語句,我們可以看到實際上使用inner join來查詢資料,以下是查詢PowerUser所使用的SQL: 代碼: select poweruser0_.POWER_USER_ID as USER_ID, poweruser0_.POWER_USER_LEVEL as POWER_US2_1_, poweruser0_.POWER_OTHER as POWER_OT3_1_, poweruser0__1_.NAME as NAME0_, poweruser0__1_.PASSWORD as PASSWORD0_ from POWER_USER poweruser0_ inner join USER poweruser0__1_ on poweruser0_.POWER_USER_ID=poweruser0__1_.USER_ID
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
11. Hibernate Gossip - Component 映射
考慮這麼一個物件類別: 代碼: package onlyfun.caterpillar;
public class User { private String id; private String name; private char sex; private int age; private String address;
........ }
我們建立一個資料庫表格與這個類別作對應: 代碼: create table USER ( ID char(32) not null, NAME varchar(16) not null, SEX char(1), AGE integer, ADDRESS varchar(255) not null, primary key (ID) );
這樣的對應,您可以依照之前所介紹過的主題來撰寫映射文件達成,然而現在我們想要將address屬性以一個自訂物件Email表示,以便我們將這個自訂物件當作輔助物件,在適當的時候,我們可以直接呼叫Email物件的sendMail()方法,也就是說,我們的User類別變成: 代碼: package onlyfun.caterpillar;
public class User { private String id; private String name; private char sex; private int age; private Email email;
public int getAge() { return age; }
public String getId() { return id; }
public String getName() { return name; }
public char getSex() { return sex; }
public Email getEmail() { return email; }
public void setAge(int i) { age = i; }
public void setId(String string) { id = string; }
public void setName(String string) { name = string; }
public void setSex(char c) { sex = c; }
public void setEmail(Email email) { this.email = email; } }
而我們新增的Email類別設計如下: 代碼: package onlyfun.caterpillar;
public class Email { private String address;
public void setAddress(String address) { this.address = address; }
public String getAddress() { return address; }
public void sendMail() { System.out.println("send mail to: " + address); } }
實際上,我們只是將使用者的Email資訊抽取出來,並獨立為一個輔助物件,這是為了在應用程式中操作方便而設計的,而實際上在資料庫表格中的資訊並沒有增加,我們不用改變資料表的內容。
顯然的,如果這麼設計,物件與資料表並不是一個對映一個,物件的數量將會比資料表來的多,物件的設計粒度比資料表來的細,為了完成物件與資料表的對應,在Hibernate中使用標籤來進行設定,我們的User.hbm.xml映射文件撰寫如下: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
這個映射文件與之前的映射文件撰寫相比,主要是多了紅色的部份,將Email類別當作User類別的一個組件(Component)。
接下來對物件的操作並沒有什麼太大的差別,例如下面的程式片段示範如何儲存資料: 代碼: User user = new User(); user.setName("caterpillar"); user.setSex('M'); user.setAge(28);
Email email = new Email(); email.setAddress("justin@caterpillar.onlyfun.net");
user.setEmail(email);
Session session = sessionFactory.openSession(); Transaction tx= session.beginTransaction();
session.save(user); tx.commit();
session.close();
下面的程式片段則示範如何查詢資料: 代碼: Session session = sessionFactory.openSession();
List users = session.find("from User");
session.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName() + "\n\tAge: " + user.getAge() + "\n\tSex: " + user.getSex()); user.getEmail().sendMail(); }
關於Component進一步的設定說明,可以看看參考手冊的12內容。
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
12 . Hibernate Gossip - Set 映射
這個主題介紹如果在物件中包括集合物件,像是使用HashSet來包括其它物件時,該如何進行物件與資料表的映射,像Set這樣的集合,可以包括所有的Java物件,這邊先介紹當Set中包括的物件沒有實體(Entiy)時的映射方式。
(簡單的說,也就是所包括的物件沒有物件識別(identity)值,沒有資料庫層次上的識別值之表格與之對應的物件,只是純綷的值型態(value type)物件,關於Entity與value type的說明,可以看看參考手冊5.2.1或是Hibernate in Action的第六章。)
假設我們有一個User類別,當中除了名稱屬性之外,另一個就是使用者的電子郵件位址,同一個使用者可能有多個不同的郵件地址,所以我們在User類別中使用Set物件來加以記錄,在這邊我們使用String來記錄每一筆郵件位址,為了不允許重複的郵件位址記錄,所以我們使用Set物件,我們的User類別如下: 代碼: package onlyfun.caterpillar;
import java.util.HashSet; import java.util.Set;
public class User { private long id; private String name; private Set addrs = new HashSet();
public Set getAddrs() { return addrs; } public void setAddrs(Set addrs) { this.addrs = addrs; } 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; }
public void addAddress(String addr) { addrs.add(addr); } }
addAddress()方法是為了加入一筆一筆的郵件位址而另外增加的,我們也可以在外部設定好Set物件,再使用setAddrs()方法設定給User物件,在映射文件上,為了進行Set的映射,我們使用標籤來進行設定,如下所示: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
從映射文件中我們可以看到,我們使用另一個表格ADDRS來記錄Set中真正記錄的物件,為了表明ADDRS中的每一筆資料是屬於哪一個USER的,我們透過ADDRS的外鍵USER_ID參考至USER的USER_ID,ADDRS的USER_ID與USER_ID的內容將會是相同的,而中設定Set所包括的物件之型態,以及它將記錄在哪一個欄位中。
假設我們使用下面的程式來儲存User的資料: 代碼: User user1 = new User(); user1.setName("caterpillar"); user1.addAddress("caterpillar@caterpillar.onlyfun.net"); user1.addAddress("justin@caterpillar.onlyfun.net"); user1.addAddress("justin@fake.com");
User user2 = new User(); user2.setName("momor"); user2.addAddress("momor@caterpillar.onlyfun.net"); user2.addAddress("momor@fake.com");
Session session = sessionFactory.openSession(); Transaction tx= session.beginTransaction(); session.save(user1); session.save(user2); tx.commit(); session.close();
實際上在資料庫中的USER與ADDRS表格的內容將如下: 代碼: mysql> select * from user; +---------+-------------+ | USER_ID | NAME | +---------+-------------+ | 1 | caterpillar | | 2 | momor | +---------+-------------+ 2 rows in set (0.00 sec)
mysql> select * from addrs; +---------+-------------------------------------+ | USER_ID | ADDRESS | +---------+-------------------------------------+ | 1 | caterpillar@caterpillar.onlyfun.net | | 1 | justin@caterpillar.onlyfun.net | | 1 | justin@fake.com | | 2 | momor@caterpillar.onlyfun.net | | 2 | momor@fake.com | +---------+-------------------------------------+ 5 rows in set (0.00 sec)
下面的程式則簡單的示範如何取出資料: 代碼: Session session = sessionFactory.openSession();
List users = session.find("from User");
session.close(); sessionFactory.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) { User user = (User) iterator.next(); System.out.println(user.getName()); Object[] addrs = user.getAddrs().toArray(); for(int i = 0; i System.out.println("\taddress " + (i+1) + ": " + addrs); } }
|
|
zaochen Newbie
注册日期: 2005-9-16 文章: 39 状态: 离线
|
13 . Hibernate Gossip - List 映射
這邊介紹如果物件中包括List型態的屬性時如何進行映射,首先我們假設我們要製作一個線上檔案管理,使用者上載的檔案名稱可能是重複的 、具有相同名稱,之前使用的Set不允許有重複的內容,所以這次我們改用List,我們的User類別撰寫如下: 代碼: package onlyfun.caterpillar;
import java.util.*;
public class User { private long id; private String name; private List files = new ArrayList();
public List getFiles() { return files; } public void setFiles(List files) { this.files = files; } 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; } public void addFiles(String name) { files.add(name); } public void addFiles(int i, String name) { files.add(i, name); } }
我們在程式中使用的是ArrayList,要在Hibernate中將之映射至資料庫,基本上與映射Set相同,我們使用標籤替代,而由於List中儲存的物件是具有索引值的,所以我們必須額外增加一個欄位來記錄物件在List中的位置,這我們可以使用標籤來指定,我們的映射文件撰寫如下: 代碼: <?xml version="1.0"?> BR>PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
與Set類似的是,記錄List的FILES表格中,使用與USER表格相同的USER_ID值,我們來看看資料儲存至資料表中是如何,假設我們的程式以下面的方式儲存資料: 代碼: User user1 = new User(); user1.setName("caterpillar"); user1.addFiles("hibernate2.jar"); user1.addFiles("jtx.jar");
User user2 = new User(); user2.setName("momor"); user2.addFiles("fan.jpg"); user2.addFiles("fan.jpg"); user2.addFiles("bush.jpg");
Session session = sessionFactory.openSession(); Transaction tx= session.beginTransaction(); session.save(user1); session.save(user2); tx.commit(); session.close();
那麼在資料庫中的USER與FILES表格將儲存以下的內容: 代碼: mysql> select * from user; +---------+-------------+ | USER_ID | NAME | +---------+-------------+ | 1 | caterpillar | | 2 | momor | +---------+-------------+ 2 rows in set (0.00 sec)
mysql> select * from files; +---------+----------------+----------+ | USER_ID | FILENAME | POSITION | +---------+----------------+----------+ | 1 | hibernate2.jar | 0 | | 1 | jtx.jar | 1 | | 2 | fan.jpg | 0 | | 2 | fan.jpg | 1 | | 2 | bush.jpg | 2 | +---------+----------------+----------+ 5 rows in set (0.00 sec)
|
|
|