使用SQLQuery 在Hibernate中使用sql語句

session.createSQLQuery.轉載

對原生SQL查詢執行的控制是經過SQLQuery接口進行的,經過執行Session.createSQLQuery()獲取這個接口。下面來描述如何使用這個API進行查詢。 java

1.標量查詢(Scalar queries)

最基本的SQL查詢就是得到一個標量(數值)的列表。 sql

sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();

它們都將返回一個Object數組(Object[])組成的List,數組每一個元素都是CATS表的一個字段值。Hibernate會使用ResultSetMetadata來斷定返回的標量值的實際順序和類型。 數據庫

若是要避免過多的使用ResultSetMetadata,或者只是爲了更加明確的指名返回值,可使用addScalar()數組

sess.createSQLQuery("SELECT * FROM CATS").addScalar("ID", Hibernate.LONG).addScalar("NAME", Hibernate.STRING).addScalar("BIRTHDATE", Hibernate.DATE)

這個查詢指定了: session

  • SQL查詢字符串 app

  • 要返回的字段和類型 函數

它仍然會返回Object數組,可是此時再也不使用ResultSetMetdata,而是明確的將ID,NAME和BIRTHDATE按照Long,String和Short類型從resultset中取出。同時,也指明瞭就算query是使用*來查詢的,可能得到超過列出的這三個字段,也僅僅會返回這三個字段ui

對所有或者部分的標量值不設置類型信息也是能夠的。 spa

sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME") .addScalar("BIRTHDATE")

基本上這和前面一個查詢相同,只是此時使用ResultSetMetaData來決定NAME和BIRTHDATE的類型,而ID的類型是明確指出的。 scala

關於從ResultSetMetaData返回的java.sql.Types是如何映射到Hibernate類型,是由方言(Dialect)控制的。倘若某個指定的類型沒有被映射,或者不是你所預期的類型,你能夠經過Dialet的registerHibernateType調用自行定義。

2.實體查詢(Entity queries)

上面的查詢都是返回標量值的,也就是從resultset中返回的「」數據。下面展現如何經過addEntity()讓原生查詢返回實體對象。

sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class); 
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);

這個查詢指定:

  • SQL查詢字符串

  • 要返回的實體

假設Cat被映射爲擁有ID,NAME和BIRTHDATE三個字段的,以上的兩個查詢都返回一個List,每一個元素都是一個Cat實體

倘若實體在映射時有一個many-to-one的關聯指向另一個實體,在查詢時必須也返回那個實體,不然會致使發生一個"column not found"的數據庫錯誤。這些附加的字段可使用*標註來自動返回,但咱們但願仍是明確指明,看下面這個具備指向Dogmany-to-one的例子:

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);

這樣cat.getDog()就能正常運做。

 

下面的暫時沒需求

16.1.3. 處理關聯和集合類(Handling associations and collections)

經過提早抓取將Dog鏈接得到,而避免初始化proxy帶來的額外開銷也是可能的。這是經過addJoin()方法進行的,這個方法可讓你將關聯或集合鏈接進來。

sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID").addEntity("cat", Cat.class).addJoin("cat.dog");

上面這個例子中,返回的Cat對象,其dog屬性被徹底初始化了,再也不須要數據庫的額外操做。注意,咱們加了一個別名("cat"),以便指明join的目標屬性路徑。經過一樣的提早鏈接也能夠做用於集合類,例如,倘若Cat有一個指向Dog的一對多關聯。

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID") .addEntity("cat", Cat.class) .addJoin("cat.dogs");
到此爲止,咱們碰到了天花板:若不對SQL查詢進行加強,這些已是在Hibernate中使用原生SQL查詢所能作到的最大可能了。
下面的問題即將出現:返回多個一樣類型的實體怎麼辦?或者默認的別名/字段不夠又怎麼辦?

16.1.4. 返回多個實體(Returning multiple entities)

到目前爲止,結果集字段名被假定爲和映射文件中指定的的字段名是一致的。倘若SQL查詢鏈接了多個表,同一個字段名可能在多個表中出現屢次,這就會形成問題。

下面的查詢中須要使用字段別名注射(這個例子自己會失敗):

sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID") .addEntity("cat", Cat.class) .addEntity("mother", Cat.class)

這個查詢的本意是但願每行返回兩個Cat實例,一個是cat,另外一個是它的媽媽。可是由於它們的字段名被映射爲相同的,並且在某些數據庫中,返回的字段別名是「c.ID」,"c.NAME"這樣的形式,而它們和在映射文件中的名字("ID"和"NAME")不匹配,這就會形成失敗。

下面的形式能夠解決字段名重複:

sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID") .addEntity("cat", Cat.class) .addEntity("mother", Cat.class)

這個查詢指明:

  • SQL查詢語句,其中包含佔位附來讓Hibernate注射字段別名

  • 查詢返回的實體

上面使用的{cat.*}和{mother.*}標記是做爲「全部屬性」的簡寫形式出現的。固然你也能夠明確地羅列出字段名,但在這個例子裏面咱們讓Hibernate來爲每一個屬性注射SQL字段別名。字段別名的佔位符是屬性名加上表別名的前綴。在下面的例子中,咱們從另一個表(cat_log)中經過映射元數據中的指定獲取Cat和它的媽媽。注意,要是咱們願意,咱們甚至能夠在where子句中使用屬性別名。

String sql = "SELECT ID as {c.id}, NAME as {c.name}, " + "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " + "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID"; List loggedCats = sess.createSQLQuery(sql) .addEntity("cat", Cat.class) .addEntity("mother", Cat.class).list()

16.1.4.1. 別名和屬性引用(Alias and property references)

大多數狀況下,都須要上面的屬性注射,但在使用更加複雜的映射,好比複合屬性、經過標識符構造繼承樹,以及集合類等等狀況下,也有一些特別的別名,來容許Hibernate注射合適的別名。

下表列出了使用別名注射參數的不一樣可能性。注意:下面結果中的別名只是示例,實用時每一個別名須要惟一而且不一樣的名字。

表 16.1. 別名注射(alias injection names)

描述 語法 示例
簡單屬性 {[aliasname].[propertyname] A_NAME as {item.name}
複合屬性 {[aliasname].[componentname].[propertyname]} CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}
實體辨別器(Discriminator of an entity) {[aliasname].class} DISC as {item.class}
實體的全部屬性 {[aliasname].*} {item.*}
集合鍵(collection key) {[aliasname].key} ORGID as {coll.key}
集合id {[aliasname].id} EMPID as {coll.id}
集合元素 {[aliasname].element} XID as {coll.element}  
集合元素的屬性 {[aliasname].element.[propertyname]} NAME as {coll.element.name}  
集合元素的全部屬性 {[aliasname].element.*} {coll.element.*}  
集合的全部屬性 {[aliasname].*} {coll.*}  

16.1.5. 返回非受管實體(Returning non-managed entities)

能夠對原生sql 查詢使用ResultTransformer。這會返回不受Hibernate管理的實體。

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS") .setResultTransformer(Transformers.aliasToBean(CatDTO.class))

這個查詢指定:

  • SQL查詢字符串

  • 結果轉換器(result transformer)

上面的查詢將會返回CatDTO的列表,它將被實例化而且將NAME和BIRTHDAY的值注射入對應的屬性或者字段。

16.1.6. 處理繼承(Handling inheritance)

原生SQL查詢倘若其查詢結果實體是繼承樹中的一部分,它必須包含基類和全部子類的全部屬性。

16.1.7. 參數(Parameters)

原生查詢支持位置參數和命名參數:

Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class); List pusList = query.setString(0, "Pus%").list(); query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class); List pusList = query.setString("name", "Pus%").list();

16.2. 命名SQL查詢

能夠在映射文檔中定義查詢的名字,而後就能夠象調用一個命名的HQL查詢同樣直接調用命名SQL查詢.在這種狀況下,咱們addEntity()方法.

<sql-query > <return alias="person" class="eg.Person"/> SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex} FROM PERSON person WHERE person.NAME LIKE :namePattern </sql-query>
List people = sess.getNamedQuery("persons") .setString("namePattern", namePattern) .setMaxResults(50) .list();

<return-join>和 <load-collection> 元素是用來鏈接關聯以及將查詢定義爲預先初始化各個集合的。

<sql-query > <return alias="person" class="eg.Person"/> <return-join alias="address" property="person.mailingAddress"/> SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex}, adddress.STREET AS {address.street}, adddress.CITY AS {address.city}, adddress.STATE AS {address.state}, adddress.ZIP AS {address.zip} FROM PERSON person JOIN ADDRESS adddress ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' WHERE person.NAME LIKE :namePattern </sql-query>

一個命名查詢可能會返回一個標量值.你必須使用<return-scalar>元素來指定字段的別名和 Hibernate類型

<sql-query > <return-scalar column="name" type="string"/> <return-scalar column="age" type="long"/> SELECT p.NAME AS name, p.AGE AS age, FROM PERSON p WHERE p.NAME LIKE 'Hiber%' </sql-query>

你能夠把結果集映射的信息放在外部的<resultset>元素中,這樣就能夠在多個命名查詢間,或者經過setResultSetMapping()API來訪問。(此處原文即存疑。原文爲:You can externalize the resultset mapping informations in a <resultset>element to either reuse them accross several named queries or through the setResultSetMapping() API.)

<resultset > <return alias="person" class="eg.Person"/> <return-join alias="address" property="person.mailingAddress"/> </resultset> <sql-query name="personsWith" resultset-ref="personAddress"> SELECT person.NAME AS {person.name}, person.AGE AS {person.age}, person.SEX AS {person.sex}, adddress.STREET AS {address.street}, adddress.CITY AS {address.city}, adddress.STATE AS {address.state}, adddress.ZIP AS {address.zip} FROM PERSON person JOIN ADDRESS adddress ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' WHERE person.NAME LIKE :namePattern </sql-query>

另外,你能夠在java代碼中直接使用hbm文件中的結果集定義信息。

List cats = sess.createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" ) .setResultSetMapping("catAndKitten") .list();

16.2.1. 使用return-property來明確地指定字段/別名

使用<return-property>你能夠明確的告訴Hibernate使用哪些字段別名,這取代了使用{}-語法 來讓Hibernate注入它本身的別名.

<sql-query > <return alias="person" class="eg.Person"> <return-property name="name" column="myName"/> <return-property name="age" column="myAge"/> <return-property name="sex" column="mySex"/> </return> SELECT person.NAME AS myName, person.AGE AS myAge, person.SEX AS mySex, FROM PERSON person WHERE person.NAME LIKE :name </sql-query>
<return-property>也可用於多個字段,它解決了使用 {}-語法不能細粒度控制多個字段的限制
<sql-query > <return alias="emp" class="Employment"> <return-property name="salary"> <return-column name="VALUE"/> <return-column name="CURRENCY"/> </return-property> <return-property name="endDate" column="myEndDate"/> </return> SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}, STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate}, REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY FROM EMPLOYMENT WHERE EMPLOYER = :id AND ENDDATE IS NULL ORDER BY STARTDATE ASC </sql-query>

注意在這個例子中,咱們使用了<return-property>結合{}的注入語法. 容許用戶來選擇如何引用字段以及屬性.

若是你映射一個識別器(discriminator),你必須使用<return-discriminator> 來指定識別器字段

16.2.2. 使用存儲過程來查詢

Hibernate 3引入了對存儲過程查詢(stored procedure)和函數(function)的支持.如下的說明中,這兩者通常都適用。 存儲過程/函數必須返回一個結果集,做爲Hibernate可以使用的第一個外部參數. 下面是一個Oracle9和更高版本的存儲過程例子.

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;

在Hibernate裏要要使用這個查詢,你須要經過命名查詢來映射它.

<sql-query callable="true"> <return alias="emp" class="Employment"> <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"/> <return-property name="salary"> <return-column name="VALUE"/> <return-column name="CURRENCY"/> </return-property> </return> { ? = call selectAllEmployments() } </sql-query>

注意存儲過程當前僅僅返回標量和實體.如今不支持<return-join><load-collection>

16.2.2.1. 使用存儲過程的規則和限制

爲了在Hibernate中使用存儲過程,你必須遵循一些規則.不遵循這些規則的存儲過程將不可用.若是你仍然想要使用他們, 你必須經過session.connection()來執行他們.這些規則針對於不一樣的數據庫.由於數據庫 提供商有各類不一樣的存儲過程語法和語義.

對存儲過程進行的查詢沒法使用setFirstResult()/setMaxResults()進行分頁。

建議採用的調用方式是標準SQL92: { ? = call functionName(<parameters>) } 或者 { ? = call procedureName(<parameters>}.原生調用語法不被支持。

對於Oracle有以下規則:

  • 函數必須返回一個結果集。存儲過程的第一個參數必須是OUT,它返回一個結果集。這是經過Oracle 9或10的SYS_REFCURSOR類型來完成的。在Oracle中你須要定義一個REF CURSOR類型,參見Oracle的手冊。

對於Sybase或者MS SQL server有以下規則:

  • 存儲過程必須返回一個結果集。.注意這些servers可能返回多個結果集以及更新的數目.Hibernate將取出第一條結果集做爲它的返回值, 其餘將被丟棄。

  • 若是你可以在存儲過程裏設定SET NOCOUNT ON,這可能會效率更高,但這不是必需的。

16.3. 定製SQL用來create,update和delete

Hibernate3可以使用定製的SQL語句來執行create,update和delete操做。在Hibernate中,持久化的類和集合已經 包含了一套配置期產生的語句(insertsql, deletesql, updatesql等等),這些映射標記 <sql-insert><sql-delete>, and <sql-update>重載了 這些語句。

<class > <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert> <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update> <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete> </class>

這些SQL直接在你的數據庫裏執行,因此你能夠自由的使用你喜歡的任意語法。但若是你使用數據庫特定的語法, 這固然會下降你映射的可移植性。

若是設定callable,則可以支持存儲過程了。

<class > <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert> <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete> <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update> </class>

參數的位置順序是很是重要的,他們必須和Hibernate所期待的順序相同。

你可以經過設定日誌調試級別爲org.hiberante.persister.entity,來查看Hibernate所期待的順序。在這個級別下, Hibernate將會打印出create,update和delete實體的靜態SQL。(若是想看到預計的順序。記得不要將定製SQL包含在映射文件裏, 由於他們會重載Hibernate生成的靜態SQL。)

在大多數狀況下(最好這麼作),存儲過程須要返回插入/更新/刪除的行數,由於Hibernate對語句的成功執行有些運行時的檢查。 Hibernate常會把進行CUD操做的語句的第一個參數註冊爲一個數值型輸出參數。

CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2) RETURN NUMBER IS BEGIN update PERSON set NAME = uname, where ID = uid; return SQL%ROWCOUNT; END updatePerson;

16.4. 定製裝載SQL

你可能須要聲明你本身的SQL(或HQL)來裝載實體

<sql-query > <return alias="pers" class="Person" lock-mode="upgrade"/> SELECT NAME AS {pers.name}, ID AS {pers.id} FROM PERSON WHERE ID=? FOR UPDATE </sql-query>

這只是一個前面討論過的命名查詢聲明,你能夠在類映射裏引用這個命名查詢。

<class > <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <loader query-ref="person"/> </class>

這也能夠用於存儲過程

你甚至能夠定一個用於集合裝載的查詢:

<set inverse="true"> <key/> <one-to-many class="Employment"/> <loader query-ref="employments"/> </set>
<sql-query > <load-collection alias="emp" role="Person.employments"/> SELECT {emp.*} FROM EMPLOYMENT emp WHERE EMPLOYER = :id ORDER BY STARTDATE ASC, EMPLOYEE ASC </sql-query>

你甚至還能夠定義一個實體裝載器,它經過鏈接抓取裝載一個集合:

<sql-query > <return alias="pers" class="Person"/> <return-join alias="emp" property="pers.employments"/> SELECT NAME AS {pers.*}, {emp.*} FROM PERSON pers LEFT OUTER JOIN EMPLOYMENT emp ON pers.ID = emp.PERSON_ID WHERE ID=? </sql-query>
相關文章
相關標籤/搜索