hql查詢~
本講要點:
l Hibernate數據查詢
l 利用關聯關係操縱對象
l Hibernate事務
l Hibernate的Cache管理
9.1 Hibernate數據查詢
數據查詢與檢索是Hibernate的一個亮點。Hibernate的數據查詢方式主要有3種,它們是:
l Hibernate Query Language(HQL)
l Criteria Query
l Native SQL
下面對這3種查詢方式分別進行講解。
9.1.1 Hibernate Query Language(HQL)
Hibernate Query Language(HQL)提供了十分強大的功能,推薦大家使用這種查詢方式。HQL具有與SQL語言類似的語法規範,只不過SQL針對表中字段進行查詢,而HQL針對持久化對象,它用來取得對象,而不進行update、delete和insert等操作。而且HQL是完全面向對象的,具備繼承、多態和關聯等特性。
1.from子句
from字句是最簡單的HQL語句,例如 from Student,也可以寫成select s from Student s。它簡單地返回Student類的所有實例。
除了Java類和屬性的名稱外,HQL語句對大小寫並不敏感,所以在上一句HQL語句中,from與FROM是相同的,但是Student與student就不同了,所以上述語句寫成from student就會報錯。下列程序演示如何通過執行from語句取得所有的Student對象。
Query query = session.createQuery(“from Student”);
List list = query.list();
for (int i=0;i<list.size(); i++)
{
Student stu = (Student)list.get(i);
System.out.println(stu.getName());
}
如果執行HQL語句“from Student, Course”,並不簡單地返回兩個對象,而是返回這兩個對象的的笛卡兒積,這類似於SQL語句中字段的全外連接。在實際應用中,像“from Student, Course”這樣的語句幾乎不會出現。
2.select子句
有時並不需要得到對象的所有屬性,這時可以使用select子句進行屬性查詢,例如,select s.name from Student s。下面程序演示如何執行這個語句:
Query query = session.createQuery(“select s.name from Student s”);
List list = query.list();
for (int i=0;i<list.size(); i++) {
String name = (String)list.get(i);
System.out.println(ame());
}
如果要查詢兩個以上的屬性,查詢結果會以數組的方式返回,如下所示:
Query query = session.createQuery(“select s.name, s.sex from Student as s”);
List list = query.list();
for (int i=0;i<list.size(); i++) {
Object obj[] = (Object[])list.get(i);
System.out.println(ame(obj[0] + “的性別是:” +obj[1]));
}
在使用屬性查詢時,由於使用對象數組,操作和理解都不太方便,如果將一個object[]中所有成員封裝成一個對象就方便多了。下面的程序將查詢結果進行了實例化:
Query query = session.createQuery(“select new Student(s.name, s.sex) from Student s”);
List list = query.list();
for (int i=0;i<list.size(); i++) {
Student stu = (Student)list.get(i);
System.out.println(stu.getName());
}
要正確運行以上程序,還需要在Student類中加入一個如下的構造函數:
public Student(String name, String sex)
{
this.name = name;
this.sex = sex;
}
3.統計函數查詢
可以在HQL中使用函數,經常使用的函數有:
l count():統計記錄條數
l min():求最小值
l max():求最大值
l sum():求和
l age():求平均值
例如,要取得Student實例的數量,可以編寫如下HQL語句:
select count(*) from Student
取得Student的平均年齡的HQL語句如下:
select avg(s.age) from Student as s
可以使用distinct去除重複數據:
select distinct s.age from Student as s
4.where子句
HQL也支持子查詢,它通過where子句實現這一機制。where子句讓用戶縮小要返回的實例的列表範圍,例如下面語句會返回所有名字爲“Bill”的Student實例:
Query query = session.createQuery("from Student as s where s.name='Bill' ");
where子句允許出現的表達式包括了SQL中可以使用的大多數情況:
l 數學操作:+,-,*,/
l 真假比較操作:=,>=,<=,<>,!=,like
l 邏輯操作:and,or, not
l 字符串連接:||
l SQL標量函數:例如upper()和lower()
如果子查詢返回多條記錄,可以用以下的關鍵字來量化:
l all:表示所有的記錄。
l any:表示所有記錄中的任意一條。
l some:與any用法相同。
l in:與any等價。
l exists:表示子查詢至少要返回一條記錄。
例如,下面語句返回所有學生的年齡都大於22的班級對象:
from Group g where 22<all (select s.age from g.students s)
下述語句返回在所有學生中有一個學生的年齡等於22的班級:
from Group g where 22=any (select s.age from g.students s)
或者
from Group g where 22=some (select s.age from g.students s)
或者
from Group g where 22 in (select s.age from g.students s)
5.order by 子句
查詢返回的列表可以按照任何返回的類或者組件的屬性排序:
from Student s order by s.name asc
asc和desc是可選的,分別代表升序或者降序。
6.連接查詢
與SQL查詢一樣, HQL也支持連接查詢,如內連接、外連接和交叉連接。
l inner join:內連接
l left outer join:左外連接
l right outer join:右外連接
l full join:全連接,但不常用
下面重點講解內連接查詢,左外連接和右外連接查詢和內連接大同小異,而全連接幾乎不怎麼使用。
inner join可以簡寫爲join,例如在查詢得到Group對象時,內連接取得對應的Student對象,實現的程序如下。
……//打開Session,開啓事務
Student stu = null; //聲明Student實例
Group group = null; //聲明Group實例
Query query = session.createQuery("from Group g join g.students");
List list = query.list();
Object obj[] = null; //聲明對象數組
for(int i=0;i<list.size();i++) {
obj = (Object[])list.get(i); //取得集合中的第i個數組
group = (Group)obj[0]; //group是數組中第一個對象
stu = (Student)obj[1]; //stu是數組中第二個對象
System.out.println(stu.getName() + "屬於:" +group.getName() );
}
……//提交事務,關閉Session
9.1.2 Criteria Query方式
當查詢數據時,人們往往需要設置查詢條件。在SQL或HQL語句中,查詢條件常常放在where子句中。此外,Hibernate還支持Criteria查詢(Criteria Query),這種查詢方式把查詢條件封裝爲一個Criteria對象。在實際應用中,使用Session的createCriteria()方法構建一個org.hibernate.Criteria實例,然後把具體的查詢條件通過Criteria的add()方法加入到Criteria實例中。這樣,程序員可以不使用SQL甚至HQL的情況下進行數據查詢,如例程9-1所示。
例程9-1 Criteria應用實例
------------------------------------------------------------------------------------------
Criteria cr = session.createCriteria(Student.class); //生成一個Criteria對象
cr.add(Restrictions.eq("name", "Bill"));//等價於where name=’Bill’
List list = cr.list();
Student stu = (Student)list.get(0);
System.out.println(stu.getName());
1.常用的查詢限制方法
在例程9-1中,Restrictions.eq()方法表示equal,即等於的情況。Restrictions類提供了查詢限制機制。它提供了許多方法,以實現查詢限制。這些方法及其他一些criteria常用查詢限制方法列於表9-1中。
表9-1 Criteria Query常用的查詢限制方法
方 法 |
說 明 |
Restrictions.eq() |
equal,= |
Restrictions.allEq() |
參數爲Map對象,使用key/value進行多個等於的對比,相當於多個Restrictions.eq()的效果 |
Restrictions.gt() |
greater-than, > |
Restrictions.lt() |
less-than, < |
Restrictions.le() |
less-equal, <= |
Restrictions.between() |
對應SQL的between子句 |
Restrictions.like() |
對應SQL的like子句 |
Restrictions.in() |
對應SQL的in子句 |
Restrictions.and() |
and關係 |
Restrictions.or() |
or關係 |
Restrictions.isNull() |
判斷屬性是否爲空,爲空返回true,否則返回false |
Restrictions.isNotNull() |
與Restrictions.isNull()相反 |
Order.asc() |
根據傳入的字段進行升序排序 |
Order.desc() |
根據傳入的字段進行降序排序 |
MatchMode.EXACT |
字符串精確匹配,相當於“like 'value'” |
MatchMode.ANYWHERE |
字符串在中間位置,相當於“like '%value%'” |
MatchMode.START |
字符串在最前面的位置,相當於“like 'value%'” |
MatchMode.END |
字符串在最後面的位置,相當於“like '%value'” |
例1:查詢學生名字以t開頭的所有Student對象。
Criteria cr = session.createCriteria(Student.class);
cr.add(Restrictions.like(“name”, “t%”))
List list = cr.list();
Student stu = (Student)list.get(0);
或者使用另一種方式:
Criteria cr = session.createCriteria(Student.class);
cr.add(Restrictions.like(“name”, “t”, MatchMode.START))
List list = cr.list();
Student stu = (Student)list.get(0);
例2:查詢學生姓名在Bill, Jack和Tom之間的所有Student對象。
String[] names = {“Bill”, “Jack”, “Tom”}
Criteria cr = session.createCriteria(Student.class);
cr.add(Restrictions.in(“name”, names))
List list = cr.list();
Student stu = (Student)list.get(0);
例3:查詢學生的年齡age等於22或age爲空(null)的所有Student對象。
Criteria cr = session.createCriteria(Student.class);
cr.add(Restrictions.eq(“age”, new Integer(22));
cr.add(Restrictions.isNull(“age”));
List list = cr.list();
Student stu = (Student)list.get(0);
例4:查詢學生姓名以字母F開頭的所有Student對象,並按姓名升序排序。
Criteria cr = session.createCriteria(Student.class);
cr.add(Restrictions.like(“name”, “F%”);
cr.addOrder(Order.asc(“name”));
List list = cr.list();
Student stu = (Student)list.get(0);
|
調用Order.asc的方法應是Criteria的addOrder()方法。
2.連接限制
在Criteria 查詢中使用FetchMode來實現連接限制。在HQL語句中,可以通過fetch關鍵字來表示預先抓取(Eager fetching),如下所示:
from Group g
left join fetch g.students s
where g.name like '%2005'
可以使用Criteria的API完成同樣的功能,如下所示:
Criteria cr = session.createCriteria(Group.class);
cr.setFetchMode(“students”, FetchMode.EAGER);
cr.add(Restrictions.like(“name”, “2005”, MatchMode.END))
List list = cr.list();
以上兩種方式編寫的代碼,都使用相同的SQL語句完成它們的功能,如下所示:
select g.*, s.* from Group g
left outer join Student s
on g.id = s.group_id
where g.name like '%2005'
9.1.3 Native SQL查詢
本地SQL查詢(Native SQL Query)指的是直接使用本地數據庫(如Oracle)的SQL語言進行查詢。它能夠掃清你把原來直接使用SQL/JDBC 的程序遷移到基於 Hibernate應用的道路上的障礙。
Hibernate3允許你使用手寫的SQL來完成所有的create、update、delete和load操作(包括存儲過程)。
1.創建一個基於SQL的Query
Native SQL查詢是通過SQLQuery接口來控制的,它是通過調用Session.createSQLQuery()方法來獲得的,例如:
String sql = "select {s.*} from t_student s where s.age>22";
SQLQuery slqQuery = session.createSQLQuery(sql);
sqlQuery.addEntity("s", Student.class);
List list = sqlQuery.list();
for (int i=0;list.size();i++) {
Student stu = (Student)list.get(i);
System.out.println(stu.getAge() +" "+ stu.getName());
}
createSQLQuery(String sql)利用傳入的SQL參數構造一個SQLQuery實例(SQLQuery是Query的子接口)。使用這個方法時,還需要傳入查詢的實體類,因此要配合SQLQuery的addEntity()方法一起使用。
addEntity()方法是將實體類別與別名聯繫在一起的方法,此方法的定義如下:
public SQLQuery addEntity(String alias, Class entityClass)
{}號用來引用數據表的別名,例如以上代碼中{s.*}表示使用s來作爲t_student表的別名。
2.命名SQL查詢
與HQL的命名查詢相似,也可以將本地的SQL查詢語句定義在映射文件中,然後像調用一個命名HQL查詢一樣直接調用命名SQL查詢。
例如在Student.hbm.xml中定義一個命名SQL查詢,如下所示:
<hibernate-mapping>
<class name="Student" table="student" lazy="false">
……
</class>
<sql-query name="QueryStudents">
<![CDATA[
select {s.*} from t_student s where s.age>22
]]>
<return alias="s" class="Student"/>
</sql-query>
</hibernate-mapping>
<sql-query>元素是<hibernate-mapping>元素的一個子元素。利用<sql-query>元素的子元素<return>指定別名與實體類相關聯。配合映射文件中的定義,編寫如下代碼來調用這個命名SQL查詢:
Query query = session.getNamedQuery(“QueryStudents”);
List list = query.list();
for (int i=0;list.size();i++) {
Student stu = (Student)list.get(i);
System.out.println(stu.getAge() + “ ”+ stu.getName());
}
也可以在命名查詢中設定查詢參數,如下所示:
……
<sql-query name=”QueryStudents”>
<![CDATA[
select {s.*} from t_student s where s.age>:age
]]>
<return alias=”s” class=”Student”/>
</sql-query>
…..
編寫如下代碼來調用這個命名SQL查詢,並且把查詢中定義的參數傳入:
Query query = session.getNamedQuery(“QueryStudents”);
query.setInteger(“age”,22);
List list = query.list();
for (int i=0;list.size();i++) {
Student stu = (Student)list.get(i);
System.out.println(stu.getAge() + “ ”+ stu.getName());
}
3.自定義insert、update和delete語句
Hibernate3.x的映射文件中新添了<sql-insert>、<sql-update> 和<sql-delete>3個標記。可以使用這3個標記自定義自己的insert、update和delete語句,例如在Student.hbm.xml中定義這些語句如下:
<hibernate-mapping>
<class name="Student" table="student" lazy="false">
<id name="id" unsaved-value="null" type="string" column="id">
<generator class="uuid.hex"/>
<property name="name" type="string" />
<property name="age" type="int" />
<sql-insert> <!--insert語句-->
insert into t_student(name, age, id) values(?,?,?)
</sql-insert>
<sql-update> <!--update語句-->
update t_student set name=?, age=? where id=?
</sql-update>
<sql-delete> <!--delete語句-->
delete from t_student where id=?
</sql-delete>
</class>
</hibernate-mapping>
對於上述文件中自定義的SQL語句,要注意以下幾點。
l insert和update語句中定義的字段必須和映射文件聲明的屬性相對應,一個都不能少。
l 在insert和update語句中,屬性出現的順序必須和映射文件中聲明的順序一致。
l 在insert語句中,主鍵id總是放在最後。
在程序中實現以上自定義的insert語句如下:
……
Student stu = new Student();
stu.setName(“Bill”);
stu.setAge(22);
session.save(stu);
…
運行上述程序,控制檯顯示的信息如下:
Hibernate: insert into t_student(name,age,id) values(?,?,?)
如果不想在insert或update語句中包括所有屬性,則可以在屬性定義時,加上insert="false"或update="false",如下所示:
<property name=”name” type=”string” insert=”false” update=”false” />
<sql-insert> insert into t_student(age, id) values(?,?) </sql-insert>
<sql-update> update t_student set age=? where id=? </sql-update>
實例:
9.2 利用關聯關係操縱對象
數據對象之間關聯關係有一對一、一對多及多對多關聯關係。在數據庫操作中,數據對象之間的關聯關係使用JDBC處理很困難。本節講解如何在Hibernate中處理這些對象之間的關聯關係。本節使用到4個類,它們分別是Student(學生)、Card(學生證)、Group(班級)和Course(課程),它們之間的關聯關係如圖9-1所示。這些實體存在級聯(cascade)問題。例如,當刪除一個班級的信息時,還要刪除該班的所有學生的基本信息。如果直接使用JDBC執行這種級聯操作,會非常煩瑣。Hibernate通過把實體對象之間關聯關係及級聯關係在映射文件中聲明,比較簡便地解決了這類級聯操作問題。
圖9-1 對象關聯圖
9.2.1 一對一關聯關係的使用
一對一關係在實際生活中是比較常見的,例如學生與學生證的關係,通過學生證可以找到學生。一對一關係在Hibernate中的實現有兩種方式,分別是主鍵關聯和外鍵關聯。
1.以主鍵關聯
主鍵關聯的重點是,關聯的兩個實體共享一個主鍵值。例如,Student與Card是一對一關係,它們在數據庫中對應的表分別是t_student和t_card。它們共用一個主鍵值id,這個主鍵可由t_student表或t_card表生成。問題是如何讓另一張表引用已經生成的主鍵值呢?例如,t-student表填入了主鍵id的值,t_card表如何引用它?這需要在Hibernate的映射文件中使用主鍵的foreign生成機制。
爲了表示Student與Card之間的一對一關聯關係,在Student和Card的映射文件Student.hbm.xml和Card.hbm.xml中都要使用<one-to-one>標記,如例程9-2所示。
例程9-2 Student.hbm.xml
-----------------------------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="test.Student" table="T_STUDENT" lazy="true"><!-- 把類與表關聯起來-->
<id name="id" column="id" type="int">
<generator class="increment" />
</id>
<property name="name" column="NAME" type="string" />
<!--property name="card_id" column="CARD_ID" type="int" /--> <!--映射學生證號-->
<property name="sex" column="SEX" type="string" />
<property name="age" column="AGE" type="int" />
<one-to-one name="card" class="test.Card"
fetch="join" cascade="all" />
</class>
</hibernate-mapping>
<class>元素的lazy屬性設定爲true,表示延遲加載,如果lazy的值設置爲false,則表示立即加載。下面對立即加載和延遲加載這兩個概念進行說明。
l 立即加載:表示Hibernate在從數據庫中取得數據,組裝好一個對象(比如學生1)後,會立即再從數據庫取得數據,組裝此對象所關聯的對象(例如學生證1)。
l 延遲加載:表示Hibernate在從數據庫中取得數據,組裝好一個對象(比如學生1)後,不會立即再從數據庫取得數據,組裝此對象所關聯的對象(例如學生證1),而是等到需要時,纔會從數據庫取得數據,組裝此關聯對象。
<one-to-one>元素的cascade屬性表明操作是否從父對象級聯到被關聯的對象,它的取值如下。
l none:在保存、刪除或修改對象時,不對其附屬對象(關聯對象)進行級聯操作。這是默認設置。
l save-update:在保存、更新當前對象時,級聯保存、更新附屬對象(臨時對象、遊離對象)。
l delete:在刪除當前對象時,級聯刪除附屬對象。
l all:在所有情況下均進行級聯操作,即包含save-update和delete操作。
l delete-orphan:刪除和當前對象解除關係的附屬對象。
<one-to-one>元素的fetch屬性的可選值是join和select,默認值是select。當fetch屬性設定爲join時,表示連接抓取(Join fetching) : Hibernate通過 在SELECT語句使用OUTER JOIN(外連接)來獲得對象的關聯實例或者關聯集合。 當fetch屬性設定爲select時,表示查詢抓取(Select fetching):需要另外發送一條 SELECT 語句抓取當前對象的關聯實體或集合。
例程9-3中<one-to-one>元素的cascade屬性設置爲“all”,表示增加、刪除及修改Student對象時,都會級聯增加、刪除和修改Card對象。
例程9-3 Card.hbm.xml
-----------------------------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="test.Card" table="t_card" lazy="true"><!-- 把類與表關聯起來-->
<id name="id" column="id">
<generator class="foreign" >
<param name="property">student</param>
</generator>
</id>
<one-to-one name="student" class="test.Student" constrained="true"/>
<property name="name" column="name" type="string" />
<!-- one-to-one name="student" class="test.Student" constrained="true"/-->
</class>
</hibernate-mapping>
在例程9-3中,Card.hbm.xml的主鍵id使用外鍵(foreign)生成機制,引用代號爲“student”對象的主鍵作爲Card表的主鍵和外鍵。student在該映射文件的<one-to-one>元素中進行了定義,它是Student對象的代號。<one-to-one>元素的屬性Constrained="true"表示Card引用了student的主鍵作爲外鍵。
需要特別注意的是,Student類中要相應地加入一對get/set方法:
public Card getCard() {
return this.card;
}
public void setCard(Card card) {
this.card = card;
}
在Card類中也要相應地加入一對get/set方法:
public Student getStudent() {
return this.stu;
}
public void setStudent(Student stu) {
this.stu = stu;
}
在客戶端測試程序中操縱Student和Card對象的方法如例程9-4所示。
例程9-4 客戶端測試程序
package test;
import org.hibernate.*;
import org.hibernate.cfg.*;
import java.io.File;
import java.util.List;
public class Test {
public static void main(String[] args) {
File file = new File("D:\\eclipse3.2\\workspace\\HibernateTest\\hibernate.cfg.xml");
Configuration conf = new Configuration().configure(file);
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
//新建Student對象
Student stu = new Student();
stu.setName("Walker");
stu.setSex("male");
stu.setAge(22);
//新建Card對象
Card card = new Card();
card.setName("Walker");
//設置Student對象與Card對象之間的關聯
stu.setCard(card);
card.setStudent(stu); //此句不能省略,否則card將不知從何處取得主鍵值
try {
session.save(stu);
tx.commit();
session.close();
System.out.println("Data have been inserted into DB.");
} catch (HibernateException e) {
e.printStackTrace();
tx.rollback();
session.close();
}
}
}
運行以上代碼後,將會在t_student表和t_card表中插入相應的數據。
2.以外鍵關聯
以外鍵關聯的要點是:兩個實體各自有不同的主鍵,但其中一個實體有一個外鍵引用另一個實體的主鍵。例如,假如Student和Card是外鍵關聯的一對一關係,它們在數據庫中相應的表分別是t_student表和t_card表,t_student表有一個主鍵id,t_card表有一個主鍵id和一個外鍵stu_id,此外鍵對應student表的主鍵id。
Student的映射文件Student.hmb.xml見例程9-2。但Card的映射文件Card.hbm.xml要做相應變動,如例程9-5所示。
例程9-5 Card.hbm.xml
----------------------------------------------------------------------------------------------------------------------
<?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="test.Card" table="T_CARD" lazy= "true"><!--把類與表關聯起來-->
<id name="id" >
<generator class="increment" ><!--不再是foreign了-->
</generator>
</id>
<property name="name" column="NAME" type="string" />
<many-to-one name="student" class="Student" column="stu_id"
unique="true"/> <!--唯一的多對一,實際上變成一對一關係了-->
</class>
</hibernate-mapping>
在例程9-5中,<many-to-one>元素的name屬性聲明外鍵關聯對象的代號,class屬性聲明該外鍵關聯對象的類,column屬性聲明該外鍵在數據表中對應的字段名,unique屬性表示使用DDL爲外鍵字段生成一個唯一約束。
以外鍵關聯對象的一對一關係,其實本質上變成了一對多的雙向關聯了,應直接按照一對多和多對一的要求編寫它們的映射文件。當<many-to-one>元素的unique屬性設定爲true,多對一的關係實際上變成了一對一的關係。
在客戶端程序中操縱外鍵關聯一對一關係的對象的方法見例程9-4。
9.2.2 一對多關聯關係的使用
一對多關係很常見,例如父親和孩子、班級與學生的關係就是很好的一對多的關係。在實際編寫程序時,一對多關係有兩種實現方式:單向關聯和雙向關聯。單向的一對多關係只需在一方進行映射配置,而雙向的一對多需要在關聯的雙方進行映射配置。下面以Group(班級)和Student(學生)爲例講解如何配置一對多的關係。
1.單向關聯
單向的一對多關係只需在一方進行映射配置,所以我們只配置Group(班級)的映射文件Group.hbm.xml,如例程9-6所示。
例程9-6 Group.hbm.xml
<?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="test.Group" table="T_GROUP" lazy="true"><!--把類與表關聯起來-->
<id name="id" column="ID"type="int">
<generator class="increment" >
</generator>
</id>
<property name="name" column="NAME" type="string"
update="true" insert="true" />
<set name="students"
table="T_STUDENT"
lazy="false"
inverse="false"
cascade="all"
sort="unsorted"
>
<key column="ID"/>
<one-to-many class="test.Student"/>
</set>
</class>
</hibernate-mapping>
在以上映射文件中,<property>元素的insert屬性表示被映射的字段是否出現在SQL的 INSERT語句中;update屬性表示被映射的字段是否出現在SQL的 UPDATE語句中。
<set>元素描述的字段(本例中爲students)對應的類型爲java.util.Set,它的各個屬性的含義如下。
l name:字段名,本例的字段名爲students,它屬於java.util.Set類型。
l table:關聯表名,本例中,students的關聯數據表名是t_student。
l lazy:是否延遲加載,lazy=false表示立即加載。
l inverse:用於表示雙向關聯中的被動方的一端,inverse的值爲false的一方負責維護關聯關係。默認值爲false。本例中Group將負責維護它與Student之間的關聯關係。
l cascade:級聯關係;cascade=all表示所有情況下均進行級聯操作,即包含save-update和delete操作。
l sort:排序關係,其可選取值爲unsorted(不排序)、natural(自然排序)、comparatorClass(由某個實現了java.util.comparator接口的類型指定排序算法)。
<key>子元素的column屬性指定關聯表(本例中t_student表)的外鍵,<one-to-many>子元素的class屬性指定了關聯類的名字。
此外,在Group類中增加如下get/set方法:
private Set students;
public Set getStudents() {
return this.students;
}
public void setStudents(Set stu) {
this.students = stu;
}
假如我們想爲一個班級添加一個學生對象,實現的代碼如下:
Transaction tx = session.beginTransaction();
Student stu = new Student();
stu.setName("Walker");
stu.setSex("male");
stu.setAge(22);
group.getStudents().add(stu);
session.save(group);
tx.commit();
2.雙向關聯
如果要設置一對多雙向關聯,那麼還需要在“多”方的映射文件中使用<many-to-one>標記。例如,在Group與Student一對多的雙向關聯中,除了Group的映射文件Group.hbm.xml和Group類進行設置和修改外,還需要在Student的映射文件Student.hbm.xm中加入:
<many-to-one
name="group"
class="test.Group"
cascade="none"
outer-join="auto"
update="true"
insert="true"
column="ID"
/>
name、class等屬性前面已經解釋過了,這裏只說明insert和update屬性。insert和update設定是否對column屬性指定的關聯字段進行insert和update操作。在Student類還要相應添加一對get/set方法:
public Group getGroup() {
return this.group;
}
public void setGroup(Group g) {
this.group = g;
}
此外,把Group.hbm.xml(如例程9-6所示)中的<set>元素的inverse屬性的值設定爲true,如下所示。
<set name="students" table="T_STUDENT" lazy="false"
inverse="true" cascade="all" sort="unsorted">
<key column="ID"/>
<one-to-many class="Student"/>
</set>
當Group.hmb.xml中<set>元素的inverse屬性的值設定爲false時,Group和Student之間的關聯關係由Group維護,Group負責將自己的id告訴Student,然後Hibernate發送update語句去更新記錄。但現在inverse的值設定爲true後,Group和Student之間的關聯關係轉由Student來維護,由Student自動去取得Group的id,而這個Student取得Group的id的動作,其實就是完成一個“學生添加到班級”的動作。
9.2.3 多對多關聯關係的使用
Student(學生)和Course(課程)的關係就是多對多的關係。在映射多對多關係時,需要另外使用一個連接表(例如,Student_Course)。Student_Course表包含2個字段:CourseId和StuId。此外,在它們的映射文件中使用<many-to-many>標記。
Student的映射文件Student.hbm.xml中加入以下描述信息:
<set name="courses" table=" Student_Course" lazy="false"
inverse="false" cascade="save-update" >
<key column="StuId"/>
<many-to-many class="test.Course" column="CourseId" />
</set>
相應地,Course的映射文件Course.hbm.xml加入以下描述信息:
<set name="students" table=" Student_Course" lazy="false"
inverse="true" cascade="save-update" >
<key column="CourseId"/>
<many-to-many class="test.Student" column="StuId" />
</set>
1.添加關聯關係
首先讓我們編一個程序來看看一個名爲Bill的學生選擇了什麼課程:
……
//獲得包含Bill的Student對象
Student stu = (Student) session.createQuery(“from Student s where s.name =
‘Bill’ ”) .uniqueResult();
List ls = new ArrayList(stu.getCourses());
for(int i=0; i<ls.size(); i++) {
Course course = (Course)ls.get(i); //獲得Course對象
System.out.println(course.getName()); //打印Bill所選課程的清單
}
…..
現在Bill還想選修business課程,這對於程序員來說只是爲Bill添加了一個到business的關聯,也就是說在student_course表中新添一條記錄,而T_Student 和T_Course表都不用變更。
……
Student stu = (Student) session.createQuery(“from Student s where s.name = ‘Bill’ ”) .uniqueResult();
Course course = (Course) session.createQuery(“from Course c where c.name =
‘business’ ”) .uniqueResult();
//設置stu與course的相互關係
stu.getCourses().add(course);
course.getStudents().add(stu);
…..
2.刪除關聯關係
刪除關聯關係比較簡單,直接調用對象集合的remove()方法刪除不要的對象即可。例如,要從學生Bill的選修課清單中刪除politics和chemistry兩門課,程序如下:
…….
Student stu = (Student) session.createQuery("from Student s where s.name = 'Bill' ") .uniqueResult();
Course course1 = (Course) session.createQuery("from Course c where c.name =
'politics' ") .uniqueResult();
Course course2 = (Course) session.createQuery("from Course c where c.name =
'chemistry' ") .uniqueResult();
stu.getCourse().remove(course1); //刪除politics課程
stu.getCourse().remove(course2); //刪除chemisty課程
…….
運行以上語句將從student_course表中刪除這兩條記錄,但T_Student和T_Course表沒有任何變化。