Hibernate查询
| ? | ? |
9.1? Hibernate數(shù)據(jù)查詢(xún)
數(shù)據(jù)查詢(xún)與檢索是Hibernate的一個(gè)亮點(diǎn)。Hibernate的數(shù)據(jù)查詢(xún)方式主要有3種,它們是:
l???????? Hibernate Query Language(HQL)
l???????? Criteria Query
l???????? Native SQL
下面對(duì)這3種查詢(xún)方式分別進(jìn)行講解。
9.1.1? Hibernate Query Language(HQL)
Hibernate Query Language(HQL)提供了十分強(qiáng)大的功能,推薦大家使用這種查詢(xún)方式。HQL具有與SQL語(yǔ)言類(lèi)似的語(yǔ)法規(guī)范,只不過(guò)SQL針對(duì)表中字段進(jìn)行查 詢(xún),而HQL針對(duì)持久化對(duì)象,它用來(lái)取得對(duì)象,而不進(jìn)行update、delete和insert等操作。而且HQL是完全面向?qū)ο蟮?#xff0c;具備繼承、多態(tài)和 關(guān)聯(lián)等特性。
1.from子句
from字句是最簡(jiǎn)單的HQL語(yǔ)句,例如 from Student,也可以寫(xiě)成select s from Student s。它簡(jiǎn)單地返回Student類(lèi)的所有實(shí)例。
除了Java類(lèi)和屬性的名稱(chēng)外,HQL語(yǔ)句對(duì)大小寫(xiě)并不敏感,所以在上一句HQL語(yǔ)句中,from與FROM是相同的,但是Student與student 就不同了,所以上述語(yǔ)句寫(xiě)成from student就會(huì)報(bào)錯(cuò)。下列程序演示如何通過(guò)執(zhí)行from語(yǔ)句取得所有的Student對(duì)象。
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());
}
如果執(zhí)行HQL語(yǔ)句“from Student, Course”,并不簡(jiǎn)單地返回兩個(gè)對(duì)象,而是返回這兩個(gè)對(duì)象的的笛卡兒積,這類(lèi)似于SQL語(yǔ)句中字段的全外連接。在實(shí)際應(yīng)用中,像“from Student, Course”這樣的語(yǔ)句幾乎不會(huì)出現(xiàn)。
2.select子句
有時(shí)并不需要得到對(duì)象的所有屬性,這時(shí)可以使用select子句進(jìn)行屬性查詢(xún),例如,select s.name from Student s。下面程序演示如何執(zhí)行這個(gè)語(yǔ)句:
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());
}
如果要查詢(xún)兩個(gè)以上的屬性,查詢(xún)結(jié)果會(huì)以數(shù)組的方式返回,如下所示:
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]));
}
在使用屬性查詢(xún)時(shí),由于使用對(duì)象數(shù)組,操作和理解都不太方便,如果將一個(gè)object[]中所有成員封裝成一個(gè)對(duì)象就方便多了。下面的程序?qū)⒉樵?xún)結(jié)果進(jìn)行了實(shí)例化:
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());
}
要正確運(yùn)行以上程序,還需要在Student類(lèi)中加入一個(gè)如下的構(gòu)造函數(shù):
public Student(String name, String sex)
{
??? this.name = name;
??? this.sex = sex;
}
3.統(tǒng)計(jì)函數(shù)查詢(xún)
可以在HQL中使用函數(shù),經(jīng)常使用的函數(shù)有:
l???????? count():統(tǒng)計(jì)記錄條數(shù)
l???????? min():求最小值
l???????? max():求最大值
l???????? sum():求和
l???????? age():求平均值
例如,要取得Student實(shí)例的數(shù)量,可以編寫(xiě)如下HQL語(yǔ)句:
select count(*) from Student
取得Student的平均年齡的HQL語(yǔ)句如下:
select avg(s.age) from Student as s
可以使用distinct去除重復(fù)數(shù)據(jù):
select distinct s.age from Student as s
4.where子句
HQL也支持子查詢(xún),它通過(guò)where子句實(shí)現(xiàn)這一機(jī)制。where子句讓用戶(hù)縮小要返回的實(shí)例的列表范圍,例如下面語(yǔ)句會(huì)返回所有名字為“Bill”的Student實(shí)例:
Query query = session.createQuery("from Student as s where s.name='Bill' ");
where子句允許出現(xiàn)的表達(dá)式包括了SQL中可以使用的大多數(shù)情況:
l???????? 數(shù)學(xué)操作:+,-,*,/
l???????? 真假比較操作:=,>=,<=,<>,!=,like
l???????? 邏輯操作:and,or, not
l???????? 字符串連接:||
l???????? SQL標(biāo)量函數(shù):例如upper()和lower()
如果子查詢(xún)返回多條記錄,可以用以下的關(guān)鍵字來(lái)量化:
l???????? all:表示所有的記錄。
l???????? any:表示所有記錄中的任意一條。
l???????? some:與any用法相同。
l???????? in:與any等價(jià)。
l???????? exists:表示子查詢(xún)至少要返回一條記錄。
例如,下面語(yǔ)句返回所有學(xué)生的年齡都大于22的班級(jí)對(duì)象:
from Group g where 22<all (select s.age from g.students s)
下述語(yǔ)句返回在所有學(xué)生中有一個(gè)學(xué)生的年齡等于22的班級(jí):
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 子句
查詢(xún)返回的列表可以按照任何返回的類(lèi)或者組件的屬性排序:
from Student s order by s.name asc
asc和desc是可選的,分別代表升序或者降序。
6.連接查詢(xún)
與SQL查詢(xún)一樣, HQL也支持連接查詢(xún),如內(nèi)連接、外連接和交叉連接。
l???????? inner join:內(nèi)連接
l???????? left outer join:左外連接
l???????? right outer join:右外連接
l???????? full join:全連接,但不常用
下面重點(diǎn)講解內(nèi)連接查詢(xún),左外連接和右外連接查詢(xún)和內(nèi)連接大同小異,而全連接幾乎不怎么使用。
inner join可以簡(jiǎn)寫(xiě)為join,例如在查詢(xún)得到Group對(duì)象時(shí),內(nèi)連接取得對(duì)應(yīng)的Student對(duì)象,實(shí)現(xiàn)的程序如下。
……//打開(kāi)Session,開(kāi)啟事務(wù)
Student? stu = null;? //聲明Student實(shí)例
Group? group = null; //聲明Group實(shí)例
Query query = session.createQuery("from Group g join g.students");
List list = query.list();
Object obj[] = null;? //聲明對(duì)象數(shù)組
for(int i=0;i<list.size();i++)? {
??? obj = (Object[])list.get(i); //取得集合中的第i個(gè)數(shù)組
??? group = (Group)obj[0];? //group是數(shù)組中第一個(gè)對(duì)象
??? stu = (Student)obj[1];?? //stu是數(shù)組中第二個(gè)對(duì)象
??? System.out.println(stu.getName() + "屬于:" +group.getName() );
}
??? ……//提交事務(wù),關(guān)閉Session
9.1.2? Criteria Query方式
當(dāng)查詢(xún)數(shù)據(jù)時(shí),人們往往需要設(shè)置查詢(xún)條件。在SQL或HQL語(yǔ)句中,查詢(xún)條件常常放在where子句中。此外,Hibernate還支持Criteria 查詢(xún)(Criteria Query),這種查詢(xún)方式把查詢(xún)條件封裝為一個(gè)Criteria對(duì)象。在實(shí)際應(yīng)用中,使用Session的createCriteria()方法構(gòu)建一 個(gè)org.hibernate.Criteria實(shí)例,然后把具體的查詢(xún)條件通過(guò)Criteria的add()方法加入到Criteria實(shí)例中。這樣, 程序員可以不使用SQL甚至HQL的情況下進(jìn)行數(shù)據(jù)查詢(xún),如例程9-1所示。
例程9-1? Criteria應(yīng)用實(shí)例
------------------------------------------------------------------------------------------
Criteria cr = session.createCriteria(Student.class); //生成一個(gè)Criteria對(duì)象
cr.add(Restrictions.eq("name", "Bill"));//等價(jià)于where name=’Bill’
List list = cr.list();
Student stu = (Student)list.get(0);
System.out.println(stu.getName());
1.常用的查詢(xún)限制方法
在例程9-1中,Restrictions.eq()方法表示equal,即等于的情況。Restrictions類(lèi)提供了查詢(xún)限制機(jī)制。它提供了許多方法,以實(shí)現(xiàn)查詢(xún)限制。這些方法及其他一些criteria常用查詢(xún)限制方法列于表9-1中。
表9-1? Criteria Query常用的查詢(xún)限制方法
| 方??? 法 | 說(shuō)??? 明 |
| Restrictions.eq() | equal,= |
| Restrictions.allEq() | 參數(shù)為Map對(duì)象,使用key/value進(jìn)行多個(gè)等于的對(duì)比,相當(dāng)于多個(gè)Restrictions.eq()的效果 |
| Restrictions.gt() | greater-than, > |
| Restrictions.lt() | less-than, < |
| Restrictions.le() | less-equal, <= |
| Restrictions.between() | 對(duì)應(yīng)SQL的between子句 |
| Restrictions.like() | 對(duì)應(yīng)SQL的like子句 |
| Restrictions.in() | 對(duì)應(yīng)SQL的in子句 |
| Restrictions.and() | and關(guān)系 |
| Restrictions.or() | or關(guān)系 |
| Restrictions.isNull() | 判斷屬性是否為空,為空返回true,否則返回false |
| Restrictions.isNotNull() | 與Restrictions.isNull()相反 |
| Order.asc() | 根據(jù)傳入的字段進(jìn)行升序排序 |
| Order.desc() | 根據(jù)傳入的字段進(jìn)行降序排序 |
| MatchMode.EXACT | 字符串精確匹配,相當(dāng)于“l(fā)ike 'value'” |
| MatchMode.ANYWHERE | 字符串在中間位置,相當(dāng)于“l(fā)ike '%value%'” |
| MatchMode.START | 字符串在最前面的位置,相當(dāng)于“l(fā)ike 'value%'” |
| MatchMode.END | 字符串在最后面的位置,相當(dāng)于“l(fā)ike '%value'” |
例1:查詢(xún)學(xué)生名字以t開(kāi)頭的所有Student對(duì)象。
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:查詢(xún)學(xué)生姓名在Bill, Jack和Tom之間的所有Student對(duì)象。
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:查詢(xún)學(xué)生的年齡age等于22或age為空(null)的所有Student對(duì)象。
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:查詢(xún)學(xué)生姓名以字母F開(kāi)頭的所有Student對(duì)象,并按姓名升序排序。
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);
|
調(diào)用Order.asc的方法應(yīng)是Criteria的addOrder()方法。
2.連接限制
在Criteria 查詢(xún)中使用FetchMode來(lái)實(shí)現(xiàn)連接限制。在HQL語(yǔ)句中,可以通過(guò)fetch關(guān)鍵字來(lái)表示預(yù)先抓取(Eager fetching),如下所示:
from Group g
left join fetch g.students s
where g.name like ' 05'
可以使用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();
以上兩種方式編寫(xiě)的代碼,都使用相同的SQL語(yǔ)句完成它們的功能,如下所示:
select g.*, s.* from Group g
left outer join Student s
on g.id = s.group_id
where g.name like ' 05'
9.1.3? Native SQL查詢(xún)
本地SQL查詢(xún)(Native SQL Query)指的是直接使用本地?cái)?shù)據(jù)庫(kù)(如Oracle)的SQL語(yǔ)言進(jìn)行查詢(xún)。它能夠掃清你把原來(lái)直接使用SQL/JDBC 的程序遷移到基于 Hibernate應(yīng)用的道路上的障礙。
Hibernate3允許你使用手寫(xiě)的SQL來(lái)完成所有的create、update、delete和load操作(包括存儲(chǔ)過(guò)程)。
1.創(chuàng)建一個(gè)基于SQL的Query
Native SQL查詢(xún)是通過(guò)SQLQuery接口來(lái)控制的,它是通過(guò)調(diào)用Session.createSQLQuery()方法來(lái)獲得的,例如:
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參數(shù)構(gòu)造一個(gè)SQLQuery實(shí)例(SQLQuery是Query的子接口)。使用這個(gè)方法時(shí),還需要傳入查詢(xún)的實(shí)體類(lèi),因此要配合SQLQuery的addEntity()方法一起使用。
addEntity()方法是將實(shí)體類(lèi)別與別名聯(lián)系在一起的方法,此方法的定義如下:
public SQLQuery addEntity(String alias, Class entityClass)
{}號(hào)用來(lái)引用數(shù)據(jù)表的別名,例如以上代碼中{s.*}表示使用s來(lái)作為t_student表的別名。
2.命名SQL查詢(xún)
與HQL的命名查詢(xún)相似,也可以將本地的SQL查詢(xún)語(yǔ)句定義在映射文件中,然后像調(diào)用一個(gè)命名HQL查詢(xún)一樣直接調(diào)用命名SQL查詢(xún)。
例如在Student.hbm.xml中定義一個(gè)命名SQL查詢(xún),如下所示:
<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>元素的一個(gè)子元素。利用<sql-query>元素的子元素<return>指定別名與實(shí)體類(lèi)相關(guān)聯(lián)。配合映射文件中的定義,編寫(xiě)如下代碼來(lái)調(diào)用這個(gè)命名SQL查詢(xún):
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());
}
也可以在命名查詢(xún)中設(shè)定查詢(xún)參數(shù),如下所示:
……
<sql-query name=”QueryStudents”>
??? <![CDATA[
??????? select {s.*} from t_student s where s.age>:age
??? ]]>
??? <return alias=”s” class=”Student”/>
</sql-query>
…..
編寫(xiě)如下代碼來(lái)調(diào)用這個(gè)命名SQL查詢(xún),并且把查詢(xún)中定義的參數(shù)傳入:
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語(yǔ)句
Hibernate3.x 的映射文件中新添了<sql-insert>、<sql-update> 和<sql-delete>3個(gè)標(biāo)記。可以使用這3個(gè)標(biāo)記自定義自己的insert、update和delete語(yǔ)句,例如在 Student.hbm.xml中定義這些語(yǔ)句如下:
<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語(yǔ)句-->
??? insert into t_student(name, age, id) values(?,?,?)
??? </sql-insert>
??? <sql-update> <!--update語(yǔ)句-->
??? update t_student set name=?, age=? where id=?
??? </sql-update>
??? <sql-delete> <!--delete語(yǔ)句-->
??????? delete from t_student where id=?
</sql-delete>
</class>
</hibernate-mapping>
對(duì)于上述文件中自定義的SQL語(yǔ)句,要注意以下幾點(diǎn)。
l???????? insert和update語(yǔ)句中定義的字段必須和映射文件聲明的屬性相對(duì)應(yīng),一個(gè)都不能少。
l???????? 在insert和update語(yǔ)句中,屬性出現(xiàn)的順序必須和映射文件中聲明的順序一致。
l???????? 在insert語(yǔ)句中,主鍵id總是放在最后。
在程序中實(shí)現(xiàn)以上自定義的insert語(yǔ)句如下:
……
Student stu = new Student();
stu.setName(“Bill”);
stu.setAge(22);
session.save(stu);
…
運(yùn)行上述程序,控制臺(tái)顯示的信息如下:
Hibernate: insert into t_student(name,age,id) values(?,?,?)
如果不想在insert或update語(yǔ)句中包括所有屬性,則可以在屬性定義時(shí),加上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>
實(shí)例:
9.2? 利用關(guān)聯(lián)關(guān)系操縱對(duì)象
數(shù)據(jù)對(duì)象之間關(guān)聯(lián)關(guān)系有一對(duì)一、一對(duì)多及多對(duì)多關(guān)聯(lián)關(guān)系。在數(shù)據(jù)庫(kù)操作中,數(shù)據(jù)對(duì)象之間的關(guān)聯(lián)關(guān)系使用JDBC處理很困難。本節(jié)講解如何在 Hibernate中處理這些對(duì)象之間的關(guān)聯(lián)關(guān)系。本節(jié)使用到4個(gè)類(lèi),它們分別是Student(學(xué)生)、Card(學(xué)生證)、Group(班級(jí))和 Course(課程),它們之間的關(guān)聯(lián)關(guān)系如圖9-1所示。這些實(shí)體存在級(jí)聯(lián)(cascade)問(wèn)題。例如,當(dāng)刪除一個(gè)班級(jí)的信息時(shí),還要?jiǎng)h除該班的所有 學(xué)生的基本信息。如果直接使用JDBC執(zhí)行這種級(jí)聯(lián)操作,會(huì)非常煩瑣。Hibernate通過(guò)把實(shí)體對(duì)象之間關(guān)聯(lián)關(guān)系及級(jí)聯(lián)關(guān)系在映射文件中聲明,比較簡(jiǎn) 便地解決了這類(lèi)級(jí)聯(lián)操作問(wèn)題。
圖9-1? 對(duì)象關(guān)聯(lián)圖
9.2.1? 一對(duì)一關(guān)聯(lián)關(guān)系的使用
一對(duì)一關(guān)系在實(shí)際生活中是比較常見(jiàn)的,例如學(xué)生與學(xué)生證的關(guān)系,通過(guò)學(xué)生證可以找到學(xué)生。一對(duì)一關(guān)系在Hibernate中的實(shí)現(xiàn)有兩種方式,分別是主鍵關(guān)聯(lián)和外鍵關(guān)聯(lián)。
1.以主鍵關(guān)聯(lián)
主鍵關(guān)聯(lián)的重點(diǎn)是,關(guān)聯(lián)的兩個(gè)實(shí)體共享一個(gè)主鍵值。例如,Student與Card是一對(duì)一關(guān)系,它們?cè)跀?shù)據(jù)庫(kù)中對(duì)應(yīng)的表分別是t_student和 t_card。它們共用一個(gè)主鍵值id,這個(gè)主鍵可由t_student表或t_card表生成。問(wèn)題是如何讓另一張表引用已經(jīng)生成的主鍵值呢?例 如,t-student表填入了主鍵id的值,t_card表如何引用它?這需要在Hibernate的映射文件中使用主鍵的foreign生成機(jī)制。
為了表示Student與Card之間的一對(duì)一關(guān)聯(lián)關(guān)系,在Student和Card的映射文件Student.hbm.xml和Card.hbm.xml中都要使用<one-to-one>標(biāo)記,如例程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"><!-- 把類(lèi)與表關(guān)聯(lián)起來(lái)-->
<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" /--> <!--映射學(xué)生證號(hào)-->
<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屬性設(shè)定為true,表示延遲加載,如果lazy的值設(shè)置為false,則表示立即加載。下面對(duì)立即加載和延遲加載這兩個(gè)概念進(jìn)行說(shuō)明。
l???????? 立即加載:表示Hibernate在從數(shù)據(jù)庫(kù)中取得數(shù)據(jù),組裝好一個(gè)對(duì)象(比如學(xué)生1)后,會(huì)立即再?gòu)臄?shù)據(jù)庫(kù)取得數(shù)據(jù),組裝此對(duì)象所關(guān)聯(lián)的對(duì)象(例如學(xué)生證1)。
l???????? 延遲加載:表示Hibernate在從數(shù)據(jù)庫(kù)中取得數(shù)據(jù),組裝好一個(gè)對(duì)象(比如學(xué)生1)后,不會(huì)立即再?gòu)臄?shù)據(jù)庫(kù)取得數(shù)據(jù),組裝此對(duì)象所關(guān)聯(lián)的對(duì)象(例如學(xué)生證1),而是等到需要時(shí),才會(huì)從數(shù)據(jù)庫(kù)取得數(shù)據(jù),組裝此關(guān)聯(lián)對(duì)象。
<one-to-one>元素的cascade屬性表明操作是否從父對(duì)象級(jí)聯(lián)到被關(guān)聯(lián)的對(duì)象,它的取值如下。
l???????? none:在保存、刪除或修改對(duì)象時(shí),不對(duì)其附屬對(duì)象(關(guān)聯(lián)對(duì)象)進(jìn)行級(jí)聯(lián)操作。這是默認(rèn)設(shè)置。
l???????? save-update:在保存、更新當(dāng)前對(duì)象時(shí),級(jí)聯(lián)保存、更新附屬對(duì)象(臨時(shí)對(duì)象、游離對(duì)象)。
l???????? delete:在刪除當(dāng)前對(duì)象時(shí),級(jí)聯(lián)刪除附屬對(duì)象。
l???????? all:在所有情況下均進(jìn)行級(jí)聯(lián)操作,即包含save-update和delete操作。
l???????? delete-orphan:刪除和當(dāng)前對(duì)象解除關(guān)系的附屬對(duì)象。
<one-to-one>元素的fetch屬性的可選值是join和select,默認(rèn)值是select。當(dāng)fetch屬性設(shè)定為join時(shí),表示連接抓取(Join fetching) : Hibernate通過(guò) 在SELECT語(yǔ)句使用OUTER JOIN(外連接)來(lái)獲得對(duì)象的關(guān)聯(lián)實(shí)例或者關(guān)聯(lián)集合。 當(dāng)fetch屬性設(shè)定為select時(shí),表示查詢(xún)抓取(Select fetching):需要另外發(fā)送一條 SELECT 語(yǔ)句抓取當(dāng)前對(duì)象的關(guān)聯(lián)實(shí)體或集合。
例程9-3中<one-to-one>元素的cascade屬性設(shè)置為“all”,表示增加、刪除及修改Student對(duì)象時(shí),都會(huì)級(jí)聯(lián)增加、刪除和修改Card對(duì)象。
例程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"><!-- 把類(lèi)與表關(guān)聯(lián)起來(lái)-->
<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)生成機(jī)制,引用代號(hào)為“student”對(duì)象的主鍵作為Card表的主鍵 和外鍵。student在該映射文件的<one-to-one>元素中進(jìn)行了定義,它是Student對(duì)象的代號(hào)。<one-to- one>元素的屬性Constrained="true"表示Card引用了student的主鍵作為外鍵。
需要特別注意的是,Student類(lèi)中要相應(yīng)地加入一對(duì)get/set方法:
public Card getCard() {
??? return this.card;???
??? }
??? public void setCard(Card card) {
this.card = card;
}
在Card類(lèi)中也要相應(yīng)地加入一對(duì)get/set方法:
public Student getStudent() {
??? return this.stu;
??? }
public void setStudent(Student stu) {
??? this.stu = stu;
}
在客戶(hù)端測(cè)試程序中操縱Student和Card對(duì)象的方法如例程9-4所示。
例程9-4? 客戶(hù)端測(cè)試程序
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對(duì)象
??????? Student stu = new Student();
??????????? stu.setName("Walker");
??????????? stu.setSex("male");
??????????? stu.setAge(22);
??????????? //新建Card對(duì)象
??????????? Card card = new Card();
??????????? card.setName("Walker");
???????????????????????????
??????? //設(shè)置Student對(duì)象與Card對(duì)象之間的關(guān)聯(lián)
??????? 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();
??????? }???
??? }
}
運(yùn)行以上代碼后,將會(huì)在t_student表和t_card表中插入相應(yīng)的數(shù)據(jù)。
2.以外鍵關(guān)聯(lián)
以外鍵關(guān)聯(lián)的要點(diǎn)是:兩個(gè)實(shí)體各自有不同的主鍵,但其中一個(gè)實(shí)體有一個(gè)外鍵引用另一個(gè)實(shí)體的主鍵。例如,假如Student和Card是外鍵關(guān)聯(lián)的一對(duì)一 關(guān)系,它們?cè)跀?shù)據(jù)庫(kù)中相應(yīng)的表分別是t_student表和t_card表,t_student表有一個(gè)主鍵id,t_card表有一個(gè)主鍵id和一個(gè)外 鍵 stu_id,此外鍵對(duì)應(yīng)student表的主鍵id。
Student的映射文件Student.hmb.xml見(jiàn)例程9-2。但Card的映射文件Card.hbm.xml要做相應(yīng)變動(dòng),如例程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"><!--把類(lèi)與表關(guān)聯(lián)起來(lái)-->
<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"/> <!--唯一的多對(duì)一,實(shí)際上變成一對(duì)一關(guān)系了-->
</class>
</hibernate-mapping>
在例程9-5中,<many-to-one>元素的name屬性聲明外鍵關(guān)聯(lián)對(duì)象的代號(hào),class屬性聲明該外鍵關(guān)聯(lián)對(duì)象的類(lèi),column屬性聲明該外鍵在數(shù)據(jù)表中對(duì)應(yīng)的字段名,unique屬性表示使用DDL為外鍵字段生成一個(gè)唯一約束。
以外鍵關(guān)聯(lián)對(duì)象的一對(duì)一關(guān)系,其實(shí)本質(zhì)上變成了一對(duì)多的雙向關(guān)聯(lián)了,應(yīng)直接按照一對(duì)多和多對(duì)一的要求編寫(xiě)它們的映射文件。當(dāng)<many-to-one>元素的unique屬性設(shè)定為true,多對(duì)一的關(guān)系實(shí)際上變成了一對(duì)一的關(guān)系。
在客戶(hù)端程序中操縱外鍵關(guān)聯(lián)一對(duì)一關(guān)系的對(duì)象的方法見(jiàn)例程9-4。
9.2.2? 一對(duì)多關(guān)聯(lián)關(guān)系的使用
一對(duì)多關(guān)系很常見(jiàn),例如父親和孩子、班級(jí)與學(xué)生的關(guān)系就是很好的一對(duì)多的關(guān)系。在實(shí)際編寫(xiě)程序時(shí),一對(duì)多關(guān)系有兩種實(shí)現(xiàn)方式:單向關(guān)聯(lián)和雙向關(guān)聯(lián)。單向的 一對(duì)多關(guān)系只需在一方進(jìn)行映射配置,而雙向的一對(duì)多需要在關(guān)聯(lián)的雙方進(jìn)行映射配置。下面以Group(班級(jí))和Student(學(xué)生)為例講解如何配置一 對(duì)多的關(guān)系。
1.單向關(guān)聯(lián)
單向的一對(duì)多關(guān)系只需在一方進(jìn)行映射配置,所以我們只配置Group(班級(jí))的映射文件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"><!--把類(lèi)與表關(guān)聯(lián)起來(lái)-->
<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屬性表示被映射的字段是否出現(xiàn)在SQL的 INSERT語(yǔ)句中;update屬性表示被映射的字段是否出現(xiàn)在SQL的 UPDATE語(yǔ)句中。
<set>元素描述的字段(本例中為students)對(duì)應(yīng)的類(lèi)型為java.util.Set,它的各個(gè)屬性的含義如下。
l???????? name:字段名,本例的字段名為students,它屬于java.util.Set類(lèi)型。
l???????? table:關(guān)聯(lián)表名,本例中,students的關(guān)聯(lián)數(shù)據(jù)表名是t_student。
l???????? lazy:是否延遲加載,lazy=false表示立即加載。
l???????? inverse:用于表示雙向關(guān)聯(lián)中的被動(dòng)方的一端,inverse的值為false的一方負(fù)責(zé)維護(hù)關(guān)聯(lián)關(guān)系。默認(rèn)值為false。本例中Group將負(fù)責(zé)維護(hù)它與Student之間的關(guān)聯(lián)關(guān)系。
l???????? cascade:級(jí)聯(lián)關(guān)系;cascade=all表示所有情況下均進(jìn)行級(jí)聯(lián)操作,即包含save-update和delete操作。
l???????? sort:排序關(guān)系,其可選取值為unsorted(不排序)、natural(自然排序)、comparatorClass(由某個(gè)實(shí)現(xiàn)了java.util.comparator接口的類(lèi)型指定排序算法)。
<key>子元素的column屬性指定關(guān)聯(lián)表(本例中t_student表)的外鍵,<one-to-many>子元素的class屬性指定了關(guān)聯(lián)類(lèi)的名字。
此外,在Group類(lèi)中增加如下get/set方法:
private Set students;
??? public Set getStudents() {
??????? return this.students;
??? }
??? public void setStudents(Set stu) {
??????? this.students = stu;
}
假如我們想為一個(gè)班級(jí)添加一個(gè)學(xué)生對(duì)象,實(shí)現(xiàn)的代碼如下:
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.雙向關(guān)聯(lián)
如果要設(shè)置一對(duì)多雙向關(guān)聯(lián),那么還需要在“多”方的映射文件中使用<many-to-one>標(biāo)記。例如,在Group與Student一對(duì) 多的雙向關(guān)聯(lián)中,除了Group的映射文件Group.hbm.xml和Group類(lèi)進(jìn)行設(shè)置和修改外,還需要在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等屬性前面已經(jīng)解釋過(guò)了,這里只說(shuō)明insert和update屬性。insert和update設(shè)定是否對(duì)column屬性指定的關(guān)聯(lián)字段進(jìn)行insert和update操作。在Student類(lèi)還要相應(yīng)添加一對(duì)get/set方法:
public Group getGroup() {
??? return this.group;
??? }
??? public void setGroup(Group g) {
??????? this.group = g;
??? }
此外,把Group.hbm.xml(如例程9-6所示)中的<set>元素的inverse屬性的值設(shè)定為true,如下所示。
<set? name="students" table="T_STUDENT" lazy="false"
??? inverse="true" cascade="all" sort="unsorted">
<key column="ID"/>
<one-to-many class="Student"/>
</set>
當(dāng) Group.hmb.xml中<set>元素的inverse屬性的值設(shè)定為false時(shí),Group和Student之間的關(guān)聯(lián)關(guān)系由 Group維護(hù),Group負(fù)責(zé)將自己的id告訴Student,然后Hibernate發(fā)送update語(yǔ)句去更新記錄。但現(xiàn)在inverse的值設(shè)定 為true后,Group和Student之間的關(guān)聯(lián)關(guān)系轉(zhuǎn)由Student來(lái)維護(hù),由Student自動(dòng)去取得Group的id,而這個(gè)Student 取得Group的id的動(dòng)作,其實(shí)就是完成一個(gè)“學(xué)生添加到班級(jí)”的動(dòng)作。
9.2.3? 多對(duì)多關(guān)聯(lián)關(guān)系的使用
Student(學(xué)生)和Course(課程)的關(guān)系就是多對(duì)多的關(guān)系。在映射多對(duì)多關(guān)系時(shí),需要另外使用一個(gè)連接表(例如,Student_Course)。 Student_Course表包含2個(gè)字段:CourseId和StuId。此外,在它們的映射文件中使用<many-to-many>標(biāo)記。
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>
相應(yīng)地,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.添加關(guān)聯(lián)關(guān)系
首先讓我們編一個(gè)程序來(lái)看看一個(gè)名為Bill的學(xué)生選擇了什么課程:
……
//獲得包含Bill的Student對(duì)象
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對(duì)象
??? System.out.println(course.getName()); //打印Bill所選課程的清單
}
…..
現(xiàn)在Bill還想選修business課程,這對(duì)于程序員來(lái)說(shuō)只是為Bill添加了一個(gè)到business的關(guān)聯(lián),也就是說(shuō)在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();
//設(shè)置stu與course的相互關(guān)系
stu.getCourses().add(course);
course.getStudents().add(stu);
…..
2.刪除關(guān)聯(lián)關(guān)系
刪除關(guān)聯(lián)關(guān)系比較簡(jiǎn)單,直接調(diào)用對(duì)象集合的remove()方法刪除不要的對(duì)象即可。例如,要從學(xué)生Bill的選修課清單中刪除politics和chemistry兩門(mén)課,程序如下:
…….
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課程
…….
運(yùn)行以上語(yǔ)句將從student_course表中刪除這兩條記錄,但T_Student和T_Course表沒(méi)有任何變化。
分享:0
喜歡
0
贈(zèng)金筆
閱讀(1407)┊ 評(píng)論 (1)┊ 收藏(0) ┊轉(zhuǎn)載(2) ┊ 喜歡▼ ┊打印┊舉報(bào)| 排行榜 |
-
新浪網(wǎng)友
文章很好,就是配色太接近了,讀著累。。。2012-7-1 ?18:48回復(fù)(0)
登錄名: 密碼:找回密碼 注冊(cè) 記住登錄狀態(tài)
分享到微博 ???評(píng)論并轉(zhuǎn)載此博文
驗(yàn)證碼:請(qǐng)點(diǎn)擊后輸入驗(yàn)證碼 收聽(tīng)驗(yàn)證碼
發(fā)評(píng)論以上網(wǎng)友發(fā)言只代表其個(gè)人觀點(diǎn),不代表新浪網(wǎng)的觀點(diǎn)或立場(chǎng)。
<?前一篇云空間 后一篇?>Oracle?存儲(chǔ)過(guò)程新浪BLOG意見(jiàn)反饋留言板 不良信息反饋 電話(huà):4006900000 提示音后按1鍵(按當(dāng)?shù)厥性?huà)標(biāo)準(zhǔn)計(jì)費(fèi)) 歡迎批評(píng)指正
新浪簡(jiǎn)介 | About Sina | 廣告服務(wù) | 聯(lián)系我們 | 招聘信息 | 網(wǎng)站律師 | SINA English | 會(huì)員注冊(cè) | 產(chǎn)品答疑
Copyright ? 1996 - 2015 SINA Corporation, All Rights Reserved
新浪公司 版權(quán)所有
幻燈播放轉(zhuǎn)載于:https://www.cnblogs.com/CooderIsCool/p/4745774.html
總結(jié)
以上是生活随笔為你收集整理的Hibernate查询的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux 复制文件或者文件
- 下一篇: Webstorm克隆Git后npm in