hibernate sql查詢總結

轉自:http://blog.csdn.net/chenssy/article/details/7728431

 Hibernate除了支持HQL查詢外,還支持原生SQL查詢。

         對原生SQL查詢執行的控制是通過SQLQuery接口進行的,通過執行Session.createSQLQuery()獲取這個接口。該接口是Query接口的子接口。

         執行SQL查詢步驟如下:

         1、獲取Hibernate Session對象

         2、編寫SQL語句

         3、通過Session的createSQLQuery方法創建查詢對象

         4、調用SQLQuery對象的addScalar()或addEntity()方法將選出的結果與標量值或實體進行關聯,分別用於進行標量查詢或實體查詢

         5、如果SQL語句包含參數,調用Query的setXxxx方法爲參數賦值

         6、調用Query的list方法返回查詢的結果集

 

        一、標量查詢

         最基本的SQL查詢就是獲得一個標量的列表:

  1. session.createSQLQuery("select * from person_inf").list();  
  2.   
  3. session.createSQLQuery("select id,name,age from person_inf").list();  

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

         但是在JDBC中過多的使用ResultSetMetadata會降低程序的性能。所以爲了過多的避免使用ResultSetMetadata或者爲了指定更加明確的返回值類型,我們可以使用addScalar()方法:

  1. session.createSQLQuery("select * from person_inf")  
  2.   
  3. .addScalar("name",StandardBasicTypes.STRING)  
  4.   
  5. .addScalar("age",StandardBasicTypes.INT)  
  6.   
  7. .list();  

         這個查詢指定了:

         1SQL查詢字符串。

         2要返回的字段和類型。

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

         如果僅僅只需要選出某個字段的值,而不需要明確指定該字段的數據類型,則可以使用addScalar(String columnAlias)

實例如下:

  1. public void scalarQuery(){  
  2.         Session session = HibernateUtil.getSession();  
  3.         Transaction tx = session.beginTransaction();  
  4.         String sql = "select * from person_inf";  
  5.         List list = session.createSQLQuery(sql).  
  6.                     addScalar("person_id",StandardBasicTypes.INTEGER).  
  7.                     addScalar("name", StandardBasicTypes.STRING).  
  8.                     addScalar("age",StandardBasicTypes.INTEGER).list();  
  9.         for(Iterator iterator = list.iterator();iterator.hasNext();){  
  10.             //每個集合元素都是一個數組,數組元素師person_id,person_name,person_age三列值  
  11.             Object[] objects = (Object[]) iterator.next();  
  12.             System.out.println("id="+objects[0]);  
  13.             System.out.println("name="+objects[1]);  
  14.             System.out.println("age="+objects[2]);  
  15.             System.out.println("----------------------------");  
  16.         }  
  17.         tx.commit();  
  18.         session.close();  
  19.     }  


         從上面可以看出。標量查詢中addScalar()方法有兩個作用:

         1、指定查詢結果包含哪些數據列---沒有被addScalar選出的列將不會包含在查詢結果中。

         2、指定查詢結果中數據列的數據類型

 

        二、實體查詢

         上面的標量查詢返回的標量結果集,也就是從resultset中返回的數據。如果我們想要的結果是某個對象的實體,這是就可以通過addEntity()方法來實現。addEntity()方法可以講結果轉換爲實體。但是在轉換的過程中要注意幾個問題:

         1、查詢返回的是某個數據表的全部數據列

         2、該數據表有對應的持久化類映射

         這時纔可以通過addEntity()方法將查詢結果轉換成實體。

  1. session.createSQLQuery("select * from perons_inf").addEntity(Person.class).list;  
  2.   
  3. session.createSQLQuery("select id,name,age from person_inf").addEntity(Person.class).list();  

         這個查詢指定:

         1SQL查詢字符串

         2要返回的實體

         假設Person被映射爲擁有id,name和age三個字段的類,以上的兩個查詢都返回一個List,每個元素都是一個Person實體。

         假若實體在映射時有一個many-to-one的關聯指向另外一個實體,在查詢時必須也返回那個實體(獲取映射的外鍵列),否則會導致發生一個"column not found"的數據庫錯誤。這些附加的字段可以使用*標註來自動返回,但我們希望還是明確指明,看下面這個具有指向teacher的many-to-one的例子:   

  1. sess.createSQLQuery("select id, name, age, teacherID from person_inf").addEntity(Person.class).list();  

         這樣就可以通過person.getTeacher()獲得teacher了。

          實例:

  1. public void entityQuery(){  
  2.     Session session = HibernateUtil.getSession();  
  3.     Transaction tx = session.beginTransaction();  
  4.     String sql = "select * from person_inf";  
  5.     List list = session.createSQLQuery(sql).  
  6.                 addEntity(Person.class).    //指定將查詢的記錄行轉換成Person實體  
  7.                 list();       
  8.     for (Iterator iterator = list.iterator();iterator.hasNext();) {  
  9.         Person person = (Person) iterator.next();      //集合的每個元素都是一個Person對象  
  10.         System.out.println("name="+person.getName());  
  11.         System.out.println("age="+person.getAge());  
  12.   
  13.     }  
  14.     tx.commit();  
  15.     session.close();  
  16. }  

         上面的都是單表查詢,如果我們在SQL語句中使用了多表連接,則SQL語句可以選出多個數據表的數據。Hibernate支持將查詢結果轉換成多個實體。如果要將查詢結果轉換成多個實體,則SQL字符串中應該爲不同數據表指定不同別名,並且調用addEntity()方法將不同數據錶轉換成不同實體。如下

  1. public void multiEntityQuery(){  
  2.     Session session = HibernateUtil.getSession();  
  3.     Transaction tx = session.beginTransaction();  
  4.     String sql = "select p.*,e.* from person_inf as p inner join event_inf as e" +  
  5.                  " on p.person_id=e.person_id";  
  6.     List list = session.createSQLQuery(sql)  
  7.                 .addEntity("p",Person.class)  
  8.                 .addEntity("e", MyEvent.class)  
  9.                 .list();  
  10.     for(Iterator iterator = list.iterator();iterator.hasNext();){  
  11.         //每個集合元素都是Person、MyEvent所組成的數組  
  12.         Object[] objects = (Object[]) iterator.next();  
  13.         Person person = (Person) objects[0];  
  14.         MyEvent event = (MyEvent) objects[1];  
  15.         System.out.println("person_id="+person.getId()+" person_name="+person.getName()+" title="+event.getTitle());  
  16.           
  17.     }  
  18. }  


 

         三、處理關聯和繼承 

         通過提前抓取將Event連接獲得,而避免初始化proxy帶來的額外開銷也是可能的。這是通過addJoin()方法進行的,通過這個方法可以講實體的關聯實體轉換成查詢對象。如下:

  1. public void joinQuery(){  
  2.     Session session = HibernateUtil.getSession();  
  3.     Transaction tx = session.beginTransaction();  
  4.     String sql = "select p.*,e.* from person_inf as p,event_inf as e where e.person_id=p.person_id";  
  5.     List list = session.createSQLQuery(sql)  
  6.                 .addEntity("p",Person.class)  
  7.                 .addJoin("e","p.myEvents")  
  8.                 .list();  
  9.     for(Iterator iterator = list.iterator();iterator.hasNext();){  
  10.         //每個集合元素都是Person、MyEvent所組成的數組  
  11.         Object[] objects = (Object[]) iterator.next();  
  12.         Person person = (Person) objects[0];  
  13.         MyEvent event = (MyEvent) objects[1];  
  14.         System.out.println("person_id="+person.getId()+" person_name="+person.getName()+" title="+event.getTitle());  
  15.           
  16.     }  
  17. }  

         上面的程序返回的Person對象,其屬性myEvent屬性已經完全被初始化,不再需要數據庫的額外操作,同時將該屬性轉換成別名爲e的實體。也就是說返回的結果是PersonEvent對象數組的列表。

 

        四、命名查詢

        我們可以將SQL語句不放在程序中,而是放在配置文件中。這樣可以更好地提高程序解耦。

         Hibernate使用<sql-query.../>元素來配置命名SQL查詢,配置<sql-query.../>元素有一個必填的name屬性,該屬性用於指定該命名SQL查詢的名稱。

         使用<sql-query.../>元素定義命名查詢時,可以包含如下幾個元素:

            <return.../>:將查詢結果轉換成持久化實體

            <return-join.../>:預加載持久化實體的關聯實體

            <return-scalar.../>:將查詢的數據列轉換成標量值

         在使用命名SQL查詢時,不需要調用addEntity()addScalar()等方法。因爲在配置命名SQL查詢時,已經指定了查詢返回的結果信息。

  1. <!-- 命名SQL查詢 -->  
  2. <sql-query name="sqlquery">  
  3.     <!-- 將p別名轉換爲Person實體 -->  
  4.     <return alias="p" class="Person" />  
  5.     <!-- 將e別名轉換成Event實體 -->  
  6.     <return alias="e" class="MyEvent" />  
  7.     <!-- 指定將person_inf表的name屬性列作爲標量值返回-->  
  8.     <return-scalar column="p.name" type="string"/>  
  9.     select p.*,e.* from person_inf as p,event_inf as e where p.person_id = e.person_id and p.age=:age  
  10. </sql-query>  

         使用SessiongetNamedQuery即可獲得指定命名sql查詢。

  1. public void query(){  
  2.     Session session = HibernateUtil.getSession();  
  3.     //調用命名查詢,直接返回結果  
  4.     List list = session.getNamedQuery("sqlquery")  
  5.                 .setInteger("age"30).list();  
  6.     for(Iterator iterator = list.iterator();iterator.hasNext();){  
  7.         //每個集合元素都是Person、MyEvent所組成的數組  
  8.         Object[] objects = (Object[]) iterator.next();  
  9.         Person person = (Person) objects[0];  
  10.         MyEvent event = (MyEvent) objects[1];  
  11.         System.out.println("person_id="+person.getId()+" person_name="+person.getName()+" title="+event.getTitle());  
  12.     }  
  13.     session.close();  
  14. }  

         Hibernate允許吧把結果集的映射信息放在<resultset.../>元素定義,這樣就可讓多個命名查詢共有該結果集映射。

  1. <resultset name="person_resultSet">  
  2.     <return alias="p" class="Person" />  
  3.     <return-scalar column="p.age" type="int"/>  
  4. </resultset>  

         通過爲<sql-query.../>元素指定resultset-ref屬性,就可以讓命名SQL查詢使用一個已有的結果集映射了。

  1. <sql-query name="sqlquery" resultset-ref="person_resultSet">  
  2.     select p.* from person as p  
  3. </sql-query>  


 

        五、調用存儲過程

         Hibernate可以通過命名SQL查詢來調用存儲過程或者函數。對於函數,該函數必須返回一個結果集;對於存儲過程,該存儲過程的第一個參數必須傳出參數,且數據類型是結果集。

         下面是一個簡單的存儲過程:

  1. Create procedure select_person()  
  2.   
  3. ect * from person_inf;  

         如果需要使用該存儲過程,可以先將其定義成命名SQL查詢,然後在程序中使用。

         當使用原生SQL來調用存儲過程,應該爲<sql-query.../>元素指定callable="true"

  1. <sql-query name="callProcedure" callable="true">  
  2.     <return class="Person">  
  3.         <!-- 將查詢的數據列轉換成實體的屬性 -->  
  4.         <return-property name="name" column="name"/>  
  5.         <return-property name="age" column="age" />  
  6.         <return-property name="person_id" column="id" />  
  7.     </return>  
  8. </sql-query>  


         程序與上面相同。

         調用存儲過程需要注意以下幾個問題:

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

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

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

         對於Oracle有如下規則:

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

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

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章