4.5 SQL查詢
Hibernate還支持使用SQL查詢,使用SQL查詢可以利用某些數(shù)據(jù)庫(kù)的特性,或者用于將原有的JDBC應(yīng)用遷移到Hibernate應(yīng)用上。使用命名的SQL查詢還可以將SQL語(yǔ)句放在配置文件中配置,從而提高程序的解耦,命名SQL查詢還可以用于調(diào)用存儲(chǔ)過(guò)程。
如果是一個(gè)新的應(yīng)用,通常不要使用SQL查詢。
SQL查詢是通過(guò)SQLQuery接口來(lái)表示的,SQLQuery接口是Query接口的子接口,因此完全可以調(diào)用Query接口的方法:
?? ● setFirstResult(),設(shè)置返回結(jié)果集的起始點(diǎn)。
?? ● setMaxResults(),設(shè)置查詢獲取的最大記錄數(shù)。
?? ● list(),返回查詢到的結(jié)果集。
但SQLQuery比Query多了兩個(gè)重載的方法:
?? ● addEntity,將查詢到的記錄與特定的實(shí)體關(guān)聯(lián)。
?? ● addScalar,將查詢的記錄關(guān)聯(lián)成標(biāo)量值。
執(zhí)行SQL查詢的步驟如下:
(1)獲取Hibernate Session對(duì)象;
(2)編寫SQL語(yǔ)句;
(3)以SQL語(yǔ)句作為參數(shù),調(diào)用Session的createSQLQuery方法創(chuàng)建查詢對(duì)象;
(4)如果SQL語(yǔ)句包含參數(shù),調(diào)用Query的setXxx方法為參數(shù)賦值;
(5)調(diào)用SQLQuery對(duì)象的addEntity或addScalar方法將選出的結(jié)果與實(shí)體或標(biāo)量值關(guān)聯(lián);
(6)調(diào)用Query的list方法返回查詢的結(jié)果集。
看下面的SQL查詢示例:
private void test()
{
??? //獲取Hibernate Session對(duì)象
??? Session session = HibernateUtil.currentSession();
??? //開(kāi)始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //編寫SQL語(yǔ)句
??? String sqlString = "select {s.*} from student s where s.name like '馬軍'";
??? //以SQL語(yǔ)句創(chuàng)建SQLQuery對(duì)象
??? List l = session.createSQLQuery(sqlString)
??????????????????? //將查詢到的記錄與特定實(shí)體關(guān)聯(lián)起來(lái)
??????????????????? .addEntity("s",Student.class)
??????????????????? //返回全部的記錄集
??????????????????? .list();
??? //遍歷結(jié)果集
??? Iterator it = l.iterator();
??? while (it.hasNext())
??? {
??????? //因?yàn)閷⒉樵兘Y(jié)果與Student類關(guān)聯(lián),因此返回的是Student集合
??????? Student s = (Student)it.next();
??????? Set enrolments = s.getEnrolments();
??????? Iterator iter = enrolments.iterator();
??????? while(iter.hasNext())
??????? {
??????????? Enrolment e = (Enrolment)iter.next();
??????????? System.out.println(e.getCourse().getName());
??????? }
??? }
??? //提交事務(wù)
??? tx.commit();
??? //關(guān)閉Session
??? HibernateUtil.closeSession();
}
上面的示例顯示了將查詢記錄關(guān)聯(lián)成一個(gè)實(shí)體的示例。事實(shí)上,SQL查詢也支持將查詢結(jié)果轉(zhuǎn)換成標(biāo)量值,轉(zhuǎn)換成標(biāo)量值可以使用addScalar方法,如:
Double max = (Double) session.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
??????? .addScalar("maxWeight", Hibernate.DOUBLE);
??????? .uniqueResult();
使用SQL查詢,如果需要將查詢到的結(jié)果轉(zhuǎn)換成特定實(shí)體,就要求為選出的字段命名別名。這別名不是隨意命名的,而是以“/”實(shí)例名.屬性名“/”的格式命名,例如:
//依次將多個(gè)選出的字段命名別名,命名別名時(shí)都以ss作為前綴,ss是關(guān)聯(lián)實(shí)體的別名
String sqlStr = "select stu.studentId as {ss.studentNumber},"
??????? + "stu.name as {ss.name} from "
??????? + "student as stu where stu.name like '楊海華'";
List l = session.createSQLQuery(sqlStr)
??????????? //將查詢出的ss實(shí)例,關(guān)聯(lián)到Student類
??????????? .addEntity("ss",Student.class)
??????????? .list();
在第一個(gè)示例中,以{s.*}代表該表的全部字段,且關(guān)聯(lián)實(shí)例的別名也被指定為s。
注意:如果不使用{s.*}的形式,就可讓實(shí)體別名和表別名互不相同。關(guān)聯(lián)實(shí)體的類型時(shí),被關(guān)聯(lián)的類必須有對(duì)應(yīng)的setter方法。
4.5.1 命名SQL查詢
可以將SQL語(yǔ)句不放在程序中,而放在配置文件中,這種方式以松耦合的方式配置SQL語(yǔ)句,可以提高程序解耦。
在Hibernate的映射文件中定義查詢名,然后確定查詢所用的SQL語(yǔ)句,然后就可以直接調(diào)用該命名SQL查詢。在這種情況下,不需要調(diào)用addEntity()方法,因?yàn)樵谂渲妹鸖QL查詢時(shí),已經(jīng)完成了查詢結(jié)果與實(shí)體的關(guān)聯(lián)。
下面是命名SQL查詢的配置片段:
<!-- 每個(gè)sql-query元素定義一個(gè)命名SQL查詢 -->
<sql-query name="mySqlQuery">
??? <!-- 關(guān)聯(lián)返回的結(jié)果與實(shí)體類 -->
??? <return alias="s" class="Student"/>
??????? <!-- 定義命名SQL查詢的SQL語(yǔ)句 -->
???? ??? SELECT {s.*}
??????? from student s WHERE s.name like'楊海華'
</sql-query>
sql-query元素是hibernate-mapping元素的子元素。因此,sql-query定義的名可以直接通過(guò)Session訪問(wèn),上面定義的mySqlQuery查詢可以直接訪問(wèn),下面是使用該命名SQL查詢的示例代碼:
private void testNamedSQl()
{
??? //獲取Hibernate Session對(duì)象
??? Session session = HibernateUtil.currentSession();
??? //開(kāi)始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //調(diào)用命名查詢,直接返回結(jié)果
??? List l = session.getNamedQuery("mySqlQuery")
??????????????????? ???? .list();
??? //遍歷結(jié)果集
??? Iterator it = l.iterator();
??? while (it.hasNext())
??? {
??????? //在定義SQL查詢時(shí),已經(jīng)將結(jié)果集與Student類關(guān)聯(lián)起來(lái)
??????? //因此,集合里的每個(gè)元素都是Student實(shí)例
??????? Student s = (Student)it.next();
??????? Set enrolments = s.getEnrolments();
??????? Iterator iter = enrolments.iterator();
??????? while(iter.hasNext())
??????? {
??????????? Enrolment e = (Enrolment)iter.next();
??????????? System.out.println("=====================================");
??????????? System.out.println(e.getCourse().getName());
??????????? System.out.println("=====================================");
??????? }
??? }
??? tx.commit();
??? HibernateUtil.closeSession();
}
4.5.2 調(diào)用存儲(chǔ)過(guò)程
Hibernate 3增加了存儲(chǔ)過(guò)程的支持,該存儲(chǔ)過(guò)程只能返回一個(gè)結(jié)果集。
下面是Oracle 9i的存儲(chǔ)過(guò)程示例:
CREATE OR REPLACE FUNCTION selectAllEmployments
??? RETURN SYS_REFCURSOR
AS
??? st_cursor SYS_REFCURSOR;
BEGIN
??? OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
????? RETURN st_cursor;
END;
如果需要使用該存儲(chǔ)過(guò)程,可以先將其定義成命名SQL查詢,例如:
<!-- 定義命名SQL查詢,name屬性指定命名SQL查詢名 -->
<sql-query name="selectAllEmployees_SP" callable="true">
??? <!-- 定義返回列與關(guān)聯(lián)實(shí)體類屬性之間的映射 -->
??? <return alias="emp" class="Employment">
??????? <!-- 依次定義每列與實(shí)體類屬性的對(duì)應(yīng) -->
??? ??? <return-property name="employee" column="EMPLOYEE"/>
??? ??? <return-property name="employer" column="EMPLOYER"/>
??? ??? <return-property name="startDate" column="STARTDATE"/>
??? ??? <return-property name="endDate" column="ENDDATE"/>
??? ??? <return-property name="regionCode" column="REGIONCODE"/>
??? ??? <return-property name="id" column="EID"/>
??????? <!-- 將兩列值映射到一個(gè)關(guān)聯(lián)類的組件屬性 -->
??? ??? <return-property name="salary">
??????????? <!-- 映射列與組件屬性之間的關(guān)聯(lián) -->
??????? ??? <return-column name="VALUE"/>
??????? ??? <return-column name="CURRENCY"/>
??????? </return-property>
??? </return>
??? { ? = call selectAllEmployments() }
</sql-query>
調(diào)用存儲(chǔ)過(guò)程還有如下需要注意的地方:
?? ● 因?yàn)榇鎯?chǔ)過(guò)程本身完成了查詢的全部操作,所以調(diào)用存儲(chǔ)過(guò)程進(jìn)行的查詢無(wú)法使用setFirstResult()/setMaxResults()進(jìn)行分頁(yè)。
?? ● 存儲(chǔ)過(guò)程只能返回一個(gè)結(jié)果集,如果存儲(chǔ)過(guò)程返回多個(gè)結(jié)果集,Hibernate將僅處理第一個(gè)結(jié)果集,其他將被丟棄。
?? ● 如果在存儲(chǔ)過(guò)程里設(shè)定SET NOCOUNT ON,將有更好的性能表現(xiàn)。當(dāng)然也可以沒(méi)有該設(shè)定。
posted on 2009-07-19 09:02
jadmin 閱讀(1158)
評(píng)論(0) 編輯 收藏