在前面總結了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());
}