主要內容:
- 簡單屬性查詢
- 實體對象查詢
一、概述
- 數據查詢與檢索是Hibernate中的一個兩點。相對其他ORM實現而言,Hibernate提供了靈活多樣的查詢機制。
- 標準化對象查詢(Criteria Query):以對象的方式進行查詢,將查詢語句封裝為對象操作。優點:可讀性好,符合java程序員的編碼習慣。缺點:不夠成熟,不支持投影(projection)或統計函數(aggregation)。
- Hibernate語句查詢:是完全面向對象的查詢語句,查詢功能非常強大,具備多態、關聯等特性。Hibernate官方推薦使用HQL進行查詢。
- Native SQL Queries(原生SQL查詢):直接使用標準SQL語言或跟特定數據庫相關的SQL進行查詢。
注意:在HQL中關鍵字不區分大小寫,但是屬性、實體類名是區分大小寫的。
二、相關示例(工程hibernate_hql
)
相關映射和實體:
Student.java
private int id;
private String name;
private Date createTime;
private Classes classes;
Classes.java
private int id;
private String name;
private Set students;
Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.hibernate.Student" table="_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="createTime"/>
<many-to-one name="classes" column="classesid"/>
</class>
</hibernate-mapping>
Classes.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate">
<class name="Classes" table="_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true" cascade="all">
<key column="classesid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
初始化
InitData.java
package cn.itcast.hibernate;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.hibernate.Session;
public class InitData {
public static void main(String[] args) {
Session session = HibernateUtils.getSession();
try {
session.beginTransaction();
for(int i=0; i<10; i++){
Classes classes = new Classes();
classes.setName("班級"+i);
session.save(classes);
for(int j=0; j<10; j++){
Student student = new Student();
student.setName("班級"+i+"的學生"+j);
student.setCreateTime(randomDate("2008-01-01","2008-03-01"));
//在內存中建立由student指向classes的引用
student.setClasses(classes);
session.save(student);
}
}
for(int i=0; i<5; i++){
Classes classes = new Classes();
classes.setName("無學生班級"+i);
session.save(classes);
}
for(int i=0; i<10; i++){
Student student = new Student();
student.setName("無業游民"+i);
session.save(student);
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
HibernateUtils.closeSession(session);
}
}
/**
* 獲取隨機日期
* @param beginDate 起始日期,格式為:yyyy-MM-dd
* @param endDate 結束日期,格式為:yyyy-MM-dd
* @return
*/
private static Date randomDate(String beginDate,String endDate){
try {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date start = format.parse(beginDate);
Date end = format.parse(endDate);
if(start.getTime() >= end.getTime()){
return null;
}
long date = random(start.getTime(),end.getTime());
return new Date(date);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static long random(long begin,long end){
long rtn = begin + (long)(Math.random() * (end - begin));
if(rtn == begin || rtn == end){
return random(begin,end);
}
return rtn;
}
}
2.1簡單屬性查詢【重點】
單一屬性查詢,返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致。
多個屬性查詢,返回的的集合元素是對象數組,數組元素的類型和相應的屬性在實體中的類型一致,數組的長度取決于與select中屬性的個數。
如果認為返回數組不夠對象化,可以采用HQL動態實例化student對象。
測試:
SimplePropertyQueryTest.java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
/**
* 簡單屬性查詢
* @author Administrator
*/
public class SimplePropertyQueryTest extends TestCase {
/**
* 單一屬性查詢
*/
public void testQuery1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致
List students = session.createQuery("select name from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
String name = (String)iter.next();
System.out.println(name);
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
多個屬性查詢:
//查詢多個屬性,其集合元素是對象數組
//數組元素的類型和對應的屬性在實體類中的類型一致
//數組的長度取決與select中屬性的個數
List students = session.createQuery("select id, name from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Object[] obj = (Object[])iter.next();
System.out.println(obj[0] + "," + obj[1]);
}
返回Student實體對象
//如果認為返回數組不夠對象化,可以采用hql動態實例化Student對象
//此時list中為Student對象集合
List students = session.createQuery("select new Student(id, name) from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getId() + "," + student.getName());
}
使用別名
//可以使用別名1
List students = session.createQuery("select s.id, s.name from Student s").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Object[] obj = (Object[])iter.next();
System.out.println(obj[0] + "," + obj[1]);
}
----------------------------------------------------------
//可以使用as命名別名
List students = session.createQuery("select s.id, s.name from Student as s").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Object[] obj = (Object[])iter.next();
System.out.println(obj[0] + "," + obj[1]);
}
2.2實體對象查詢【重點】
N+1問題,在默認情況下,使用
query.iterate
查詢,有可能出現N+1問題所謂的N+1是在查詢的時候發出了N+1條sql語句
1:首先發出一條查詢語句去查詢對象id列表
N:根據id列表到緩存中查詢,如果緩存中不存在與之匹配的數據,那么會根據id發出相應的sql語句-
list和iterate的區別
- list默認情況下,每次都會發出sql語句,list會向緩存中放數據,但是默認是不利用緩存中的數據
- iterate默認情況下是會利用緩存,只有在緩存中沒有相應的數據才會發出sql語句去數據庫中查詢,即N+1問題。
測試:
SimpleObjectQueryTest1.java
public void testQuery1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//返回Student對象的集合
//可以忽略select
List students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
使用別名
//返回Student對象的集合
//可以忽略select
List students = session.createQuery("from Student s").list();
//List students = session.createQuery("from Student as s").list();
//List students = session.createQuery("select s from Student as s").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
說明:第一種方式和第二種方式差不多,最后一種注意必須使用別名(當我么使用select的時候)。最后注意,不支持
List students = session.createQuery("select * from Student").list();
這種方式,即不支持select * from ....
。
SimpleObjectQueryTest2.java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
/**
* 實體對象查詢
* @author Administrator
*/
public class SimpleObjectQueryTest2 extends TestCase {
public void testQuery1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/**
* 采用list查詢發出一條查詢語句,取得Student對象數據、
* Hibernate: select student0_.id as id1_, student0_.name as name1_,
* student0_.createTime as createTime1_, student0_.classesid as classesid1_
* from t_student student0_
*
*/
List students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testQuery2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/**
* 出現N+1問題
*
* 1:發出查詢id列表的sql
* Hibernate: select student0_.id as col_0_0_ from t_student student0_
*
* N:在依次發出根據id查詢Student對象的sql
* Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_,
* student0_.createTime as createTime1_0_, student0_.classesid as classesid1_0_
* from t_student student0_ where student0_.id=?
*
*/
Iterator iter = session.createQuery("from Student").iterate();
while(iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testQuery3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
System.out.println("---------------------------------------------");
/**
* 不會出現N+1問題
*
* 因為list操作已經將Student對象放到了一級緩存中,所以再次使用iterate操作的時候
* 它首先發出一條查詢id列表的sql,在根據id到緩存中去數據,只有在緩存中找不到相應的
* 數據時,才會發出sql到數據庫中查詢
*
*/
Iterator iter = session.createQuery("from Student").iterate();
while(iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testQuery4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
System.out.println("---------------------------------------------");
/**
* 再次發出查詢sql
*
* 在默認情況下list每次都會向數據庫發出查詢對象的sql,除非配置查詢緩存,所以下面的list操作
* 雖然在一級緩存中已經有了對象數據,但list默認情況下不會利用緩存,而再次發出sql
*
* 默認情況下,list會向緩存中放入數據,但不會利用數據
*
*/
students = session.createQuery("from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:
1.對于方法一,我們可以看到使用list方法發出一條sql語句將所有的對象都查詢出來。
2.對于方法二,我們使用iterator方法進行查詢,此時查詢就不一樣了,先是發出一條sql語句將所有的id主鍵都查詢出來,然后根據主鍵去找相關的數據,首先在緩存中找,如果緩存中沒有對應的數據,那么就會發出sql語句去數據庫中查詢,于是就出現了N+1問題,因為在后面會發出多條sql語句,這樣對于數據庫的性能損耗是很大的。
3.從方法三中我們也可以看到當我們先使用list查詢出對象之后再使用iterator方法查詢就不會再次發出sql語句,因為iterator方法會首先在緩存中找,而list方法已經將相關數據放在了緩存中,所以iterator方法不會再次發出sql語句,但是如果我們在后面還是使用list方法而不是iterator方法,那么還是會發出查詢語句,從方法四中可以看到,這就說明,iterator方法可以利用緩存,而list方法不會利用緩存。