天天看點

08-Hibernate3.6.2 查詢02 by SQL QBC QBE

在前面總結了Hibernate的查詢方式和HQL查詢(07-Hibernate3.6.2 查詢 by HQL),今天來總結剩下三種方式,SQL 、QBC、QBE

一、SQL

其實在上一篇總結中已經提到了NativeSQL查詢,隻是簡單提了一下,這裡還想單獨提出來總結一下。

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

執行SQL查詢的步驟如下:

             1、  擷取Hibernate Session對象;

             2、  編寫SQL語句;

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

             4、  調用SQLQuery對象的addScalar()和addEntity()方法将選出的結果和标量值或實體進行關聯,分别用于标量查詢或實體查詢;

             5、  如果SQL語句包含參數,調用Query的setXxx()方法為參數指派;

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

1、  标量查詢

最基本的SQL查詢就是獲得一個标量的清單:

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()方法:

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

這時仍然會傳回Object數組,但是此時不再使用ResultSetMetdata,而是明确的将name和age按照String和int類型從resultset中取出。同時,也指明了就算query是使用*來查詢的,可能獲得超過列出的這三個字段,也僅僅會傳回這三個字段。如果僅僅隻需要選出某個字段的值,而不需要明确指定該字段的資料類型,則可以使用addScalar(String columnAlias)。

    是以addScalar()方法有兩個作用:

           1、指定查詢結果包含哪些資料列---沒有被addScalar選出的列将不會包含在查詢結果中;

           2、指定查詢結果中資料列的資料類型;

2、實體查詢

上面的标量查詢傳回的标量結果集,也就是從resultset中傳回的“裸”資料。如果我們想要的結果是某個對象的實體,這是就可以通過addEntity()方法來實作。addEntity()方法可以講結果轉換為實體。但是在轉換的過程中要注意幾個問題:

1、查詢傳回的是某個資料表的全部資料列

2、該資料表有對應的持久化類映射

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

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

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

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

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

上面說的是查詢單個對象的方式,但是如果我們需要查詢多表關聯,則SQL語句可以選出多個資料表的資料。Hibernate支援将查詢結果轉換成多個實體。如果要将查詢結果轉換成多個實體,則SQL字元串應該為不同的資料表指定不同别名,并且調用addEntity()方法将不同資料表轉換成不同實體。

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());  
	   }  
           

注意在傳回多個實體的時候,有需求需要解決字段名重複的問題:

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

3、處理關聯和繼承

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

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());  
	    }  
           

4、命名查詢

我們可以将SQL語句不放在程式中,而是放在配置檔案中。這樣可以更好地提高程式解耦。

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

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

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

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

        <return-scalar.../>:将查詢的資料列轉換成标量值

在使用命名SQL查詢時,不需要調用addEntity()、addScalar()等方法。因為在配置命名SQL查詢時,已經指定了查詢傳回的結果資訊。

<!-- 命名SQL查詢 -->  
<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查詢。

//調用命名查詢,直接傳回結果  
	    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());  
	   }  
           

Hibernate允許将結果集映射資訊放在<resultset.../>元素定義,這樣就可讓多個命名查詢共有該結果集映射

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

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

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

其實這裡已經和MyBatis很像了,可以參看一下我對MyBatis的總結。

二、QBC

QBC主要由Criteria接口、Criterion接口和Expression類組成,它支援在運作時動态生成查詢語句。

         QBC檢索步驟:

               1、  調用Session的createCriteria()方法建立一個Criteria對象;

               2、  設定查詢條件。Expression類提供了一系列用于設定查詢條件的靜态方法,這些靜态方法都傳回Criterion執行個體,每一個Criterion執行個體代表一個查詢條件。Criterion的add()方法用于加入查詢條件;

               3、  調用Criteria的list()方法執行查詢語句,該方法傳回list類型的查詢結果,在List集合中存放了符合查詢條件的持久化對象。

//建立一個Criteria對象   
	Criteria criteria=session.createCriteria(Customer.class);   
	//設定查詢條件,然後把查詢條件加入到Criteria中   
	Criterion criterion1= Expression.like("name", "T%") ;   
	Criterion criterion2= Expression.eq("age", new Integer(21)) ;   
	criteria=criteria.add(criterion1);   
	criteria=criteria.add(criterion2);   
	//執行查詢語句,傳回查詢結果   
	List result=criteria.list();  

           

注:Expression;類是org.hibernate.criterion.Restrictions的子類,是以也可以換成該類。Criteria也是采用的方法連的程式設計風格,因為add方法傳回值也是Criteria

Criteria c = session.createCriteria(Topic.class) // from Topic
				.add(Restrictions.gt("id", 2)) // greater than = id > 2
				.add(Restrictions.lt("id", 8)) // little than = id < 8
				.add(Restrictions.like("title", "t_")).createCriteria(
				// category.id >= 3 and category.id <=5
						"category").add(Restrictions.between("id", 3, 5));
		// DetachedCriterea
		for (Object o : c.list()) {
			Topic t = (Topic) o;
			System.out.println(t.getId() + "-" + t.getTitle());
		}
           

Query和Criteria接口都提供了用于分頁顯示查詢結果的方法:

setFirstResult(int firstResult):設定從哪一個對象開始檢索,參數firstResult表示這個對象在查詢結果中的索引位置,索引位置的起始值為0。預設情況下,Query和Criteria接口從查詢結果中的第一個對象,也就是索引位置為0的對象開始檢索。

        setMaxResult(int maxResults):設定一次最多檢索出的對象數目。預設情況下,Query和Criteria接口檢索出查詢結果中所有的對象。

三、QBE

Topic topic = new Topic();
		topic.setTitle("T_");

		//ignoreCase忽略大小寫,enableLike:like
		Example e = Example.create(topic).ignoreCase().enableLike();
		Criteria c = session.createCriteria(Topic.class).add(
				Restrictions.gt("id", 2)).add(Restrictions.lt("id", 8)).add(e);

		for (Object o : c.list()) {
			Topic t = (Topic) o;
			System.out.println(t.getId() + "-" + t.getTitle());
		}