Hibernate SQL查詢 addScalar()或addEntity()

Hibernate除了支持HQL查詢外,還支持原生SQL查詢。
         對原生SQL查詢執行的控制是經過SQLQuery接口進行的,經過執行Session.createSQLQuery()獲取這個接口。該接口是Query接口的子接口。
         執行SQL查詢步驟以下:
         一、獲取Hibernate Session對象
         二、編寫SQL語句
         三、經過Session的createSQLQuery方法建立查詢對象
         四、調用SQLQuery對象的addScalar()或addEntity()方法將選出的結果與標量值或實體進行關聯,分別用於進行標量查詢或實體查詢
         五、若是SQL語句包含參數,調用Query的setXxxx方法爲參數賦值
         六、調用Query的list方法返回查詢的結果集

        1、標量查詢
         最基本的SQL查詢就是得到一個標量的列表:
html

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

 
         它們都將返回一個Object數組組成的List,數組每一個元素都是person_inf表的一個字段值。Hibernate會使用ResultSetMetadata來斷定返回的標量值的實際順序和類型。
         可是在JDBC中過多的使用ResultSetMetadata會下降程序的性能。因此爲了過多的避免使用ResultSetMetadata或者爲了指定更加明確的返回值類型,咱們可使用addScalar()方法:
java

session.createSQLQuery("select * from person_inf")  
  
.addScalar("name",StandardBasicTypes.STRING)  
  
.addScalar("age",StandardBasicTypes.INT)  
  
.list();


         這個查詢指定了:
         一、SQL查詢字符串。
         二、要返回的字段和類型。
         它仍然會返回Object數組,可是此時再也不使用ResultSetMetdata,而是明確的將name和age按照String和int類型從 resultset中取出。同時,也指明瞭就算query是使用*來查詢的,可能得到超過列出的這三個字段,也僅僅會返回這三個字段。
         若是僅僅只須要選出某個字段的值,而不須要明確指定該字段的數據類型,則可使用addScalar(String columnAlias)。
實例以下:
sql

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

 

         從上面能夠看出。標量查詢中addScalar()方法有兩個做用:
         一、指定查詢結果包含哪些數據列---沒有被addScalar選出的列將不會包含在查詢結果中。
         二、指定查詢結果中數據列的數據類型

        2、實體查詢
         上面的標量查詢返回的標量結果集,也就是從resultset中返回的「裸」數據。若是咱們想要的結果是某個對象的實體,這是就能夠經過 addEntity()方法來實現。addEntity()方法能夠講結果轉換爲實體。可是在轉換的過程當中要注意幾個問題:
         一、查詢返回的是某個數據表的所有數據列
         二、該數據表有對應的持久化類映射
         這時才能夠經過addEntity()方法將查詢結果轉換成實體。
數據庫

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

 
         這個查詢指定:
         一、SQL查詢字符串
         二、要返回的實體
         假設Person被映射爲擁有id,name和age三個字段的類,以上的兩個查詢都返回一個List,每一個元素都是一個Person實體。
         倘若實體在映射時有一個many-to-one的關聯指向另一個實體,在查詢時必須也返回那個實體(獲取映射的外鍵列),不然會致使發生一 個"column not found"的數據庫錯誤。這些附加的字段可使用*標註來自動返回,但咱們但願仍是明確指明,看下面這個具備指向teacher的many-to- one的例子:  
數組

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


         這樣就能夠經過person.getTeacher()得到teacher了。
          實例:
session

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


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

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


         3、處理關聯和繼承
         經過提早抓取將Event鏈接得到,而避免初始化proxy帶來的額外開銷也是可能的。這是經過addJoin()方法進行的,經過這個方法能夠講實體的關聯實體轉換成查詢對象。以下:
性能

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

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

        4、命名查詢
        咱們能夠將SQL語句不放在程序中,而是放在配置文件中。這樣能夠更好地提升程序解耦。
         Hibernate使用<sql-query.../>元素來配置命名SQL查詢,配置<sql-query.../>元素有一個必填的name屬性,該屬性用於指定該命名SQL查詢的名稱。
         使用<sql-query.../>元素定義命名查詢時,能夠包含以下幾個元素:
            <return.../>:將查詢結果轉換成持久化實體
            <return-join.../>:預加載持久化實體的關聯實體
            <return-scalar.../>:將查詢的數據列轉換成標量值
         在使用命名SQL查詢時,不須要調用addEntity()、addScalar()等方法。由於在配置命名SQL查詢時,已經指定了查詢返回的結果信息。
[html] view plaincopyprint?
<!-- 命名SQL查詢 --> 
.net

<sql-query name="sqlquery">  
    <!-- 將p別名轉換爲Person實體 -->  
    <return alias="p" class="Person" />  
    <!-- 將e別名轉換成Event實體 -->  
    <return alias="e" class="MyEvent" />  
    <!-- 指定將person_inf表的name屬性列做爲標量值返回-->  
    <return-scalar column="p.name" type="string"/>  
    select p.*,e.* from person_inf as p,event_inf as e where p.person_id = e.person_id and p.age=:age  
</sql-query>  
         使用Session的getNamedQuery便可得到指定命名sql查詢。
[java] view plaincopyprint? 
public void query(){  
    Session session = HibernateUtil.getSession();  
    //調用命名查詢,直接返回結果  
    List list = session.getNamedQuery("sqlquery")  
                .setInteger("age", 30).list();  
    for(Iterator iterator = list.iterator();iterator.hasNext();){  
        //每一個集合元素都是Person、MyEvent所組成的數組  
        Object[] objects = (Object[]) iterator.next();  
        Person person = (Person) objects[0];  
        MyEvent event = (MyEvent) objects[1];  
        System.out.println("person_id="+person.getId()+" person_name="+person.getName()+" title="+event.getTitle());  
    }  
    session.close();  
}  
         Hibernate容許吧把結果集的映射信息放在<resultset.../>元素定義,這樣就可以讓多個命名查詢共有該結果集映射。
[html] view plaincopyprint? 
<resultset name="person_resultSet">  
    <return alias="p" class="Person" />  
    <return-scalar column="p.age" type="int"/>  
</resultset>  
         經過爲<sql-query.../>元素指定resultset-ref屬性,就可讓命名SQL查詢使用一個已有的結果集映射了。
[html] view plaincopyprint? 
<sql-query name="sqlquery" resultset-ref="person_resultSet">  
    select p.* from person as p  
</sql-query>


        5、調用存儲過程
         Hibernate能夠經過命名SQL查詢來調用存儲過程或者函數。對於函數,該函數必須返回一個結果集;對於存儲過程,該存儲過程的第一個參數必須傳出參數,且數據類型是結果集。
         下面是一個簡單的存儲過程:
scala

Create procedure select_person()  
  
ect * from person_inf;

        
若是須要使用該存儲過程,能夠先將其定義成命名SQL查詢,而後在程序中使用。
         當使用原生SQL來調用存儲過程,應該爲<sql-query.../>元素指定callable="true"。

<sql-query name="callProcedure" callable="true">  
    <return class="Person">  
        <!-- 將查詢的數據列轉換成實體的屬性 -->  
        <return-property name="name" column="name"/>  
        <return-property name="age" column="age" />  
        <return-property name="person_id" column="id" />  
    </return>  
</sql-query>

           程序與上面相同。          調用存儲過程須要注意如下幾個問題:          爲了在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,這可能會效率更高,但這不是必需的。 轉載:http://blog.csdn.net/vacblog/article/details/7769976                    

相關文章
相關標籤/搜索