主要內(nèi)容:
- 簡(jiǎn)單屬性查詢
- 實(shí)體對(duì)象查詢
一奏候、概述
- 數(shù)據(jù)查詢與檢索是Hibernate中的一個(gè)兩點(diǎn)循集。相對(duì)其他ORM實(shí)現(xiàn)而言,Hibernate提供了靈活多樣的查詢機(jī)制蔗草。
- 標(biāo)準(zhǔn)化對(duì)象查詢(Criteria Query):以對(duì)象的方式進(jìn)行查詢咒彤,將查詢語句封裝為對(duì)象操作。優(yōu)點(diǎn):可讀性好咒精,符合java程序員的編碼習(xí)慣镶柱。缺點(diǎn):不夠成熟,不支持投影(projection)或統(tǒng)計(jì)函數(shù)(aggregation)狠轻。
- Hibernate語句查詢:是完全面向?qū)ο蟮牟樵冋Z句奸例,查詢功能非常強(qiáng)大,具備多態(tài)向楼、關(guān)聯(lián)等特性。Hibernate官方推薦使用HQL進(jìn)行查詢谐区。
- Native SQL Queries(原生SQL查詢):直接使用標(biāo)準(zhǔn)SQL語言或跟特定數(shù)據(jù)庫(kù)相關(guān)的SQL進(jìn)行查詢湖蜕。
注意:在HQL中關(guān)鍵字不區(qū)分大小寫,但是屬性宋列、實(shí)體類名是區(qū)分大小寫的昭抒。
二、相關(guān)示例(工程hibernate_hql
)
相關(guān)映射和實(shí)體:
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("班級(jí)"+i);
session.save(classes);
for(int j=0; j<10; j++){
Student student = new Student();
student.setName("班級(jí)"+i+"的學(xué)生"+j);
student.setCreateTime(randomDate("2008-01-01","2008-03-01"));
//在內(nèi)存中建立由student指向classes的引用
student.setClasses(classes);
session.save(student);
}
}
for(int i=0; i<5; i++){
Classes classes = new Classes();
classes.setName("無學(xué)生班級(jí)"+i);
session.save(classes);
}
for(int i=0; i<10; i++){
Student student = new Student();
student.setName("無業(yè)游民"+i);
session.save(student);
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
HibernateUtils.closeSession(session);
}
}
/**
* 獲取隨機(jī)日期
* @param beginDate 起始日期炼杖,格式為:yyyy-MM-dd
* @param endDate 結(jié)束日期灭返,格式為: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簡(jiǎn)單屬性查詢【重點(diǎn)】
單一屬性查詢,返回結(jié)果集屬性列表坤邪,元素類型和實(shí)體類中相應(yīng)的屬性類型一致熙含。
多個(gè)屬性查詢,返回的的集合元素是對(duì)象數(shù)組艇纺,數(shù)組元素的類型和相應(yīng)的屬性在實(shí)體中的類型一致怎静,數(shù)組的長(zhǎng)度取決于與select中屬性的個(gè)數(shù)邮弹。
如果認(rèn)為返回?cái)?shù)組不夠?qū)ο蠡梢圆捎肏QL動(dòng)態(tài)實(shí)例化student對(duì)象蚓聘。
測(cè)試:
SimplePropertyQueryTest.java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
/**
* 簡(jiǎn)單屬性查詢
* @author Administrator
*/
public class SimplePropertyQueryTest extends TestCase {
/**
* 單一屬性查詢
*/
public void testQuery1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//返回結(jié)果集屬性列表腌乡,元素類型和實(shí)體類中相應(yīng)的屬性類型一致
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);
}
}
}
多個(gè)屬性查詢:
//查詢多個(gè)屬性,其集合元素是對(duì)象數(shù)組
//數(shù)組元素的類型和對(duì)應(yīng)的屬性在實(shí)體類中的類型一致
//數(shù)組的長(zhǎng)度取決與select中屬性的個(gè)數(shù)
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實(shí)體對(duì)象
//如果認(rèn)為返回?cái)?shù)組不夠?qū)ο蠡鼓担梢圆捎胔ql動(dòng)態(tài)實(shí)例化Student對(duì)象
//此時(shí)list中為Student對(duì)象集合
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實(shí)體對(duì)象查詢【重點(diǎn)】
N+1問題与纽,在默認(rèn)情況下,使用
query.iterate
查詢塘装,有可能出現(xiàn)N+1問題所謂的N+1是在查詢的時(shí)候發(fā)出了N+1條sql語句
1:首先發(fā)出一條查詢語句去查詢對(duì)象id列表
N:根據(jù)id列表到緩存中查詢渣锦,如果緩存中不存在與之匹配的數(shù)據(jù),那么會(huì)根據(jù)id發(fā)出相應(yīng)的sql語句-
list和iterate的區(qū)別
- list默認(rèn)情況下氢哮,每次都會(huì)發(fā)出sql語句袋毙,list會(huì)向緩存中放數(shù)據(jù),但是默認(rèn)是不利用緩存中的數(shù)據(jù)
- iterate默認(rèn)情況下是會(huì)利用緩存冗尤,只有在緩存中沒有相應(yīng)的數(shù)據(jù)才會(huì)發(fā)出sql語句去數(shù)據(jù)庫(kù)中查詢听盖,即N+1問題。
測(cè)試:
SimpleObjectQueryTest1.java
public void testQuery1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//返回Student對(duì)象的集合
//可以忽略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對(duì)象的集合
//可以忽略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());
}
說明:第一種方式和第二種方式差不多裂七,最后一種注意必須使用別名(當(dāng)我么使用select的時(shí)候)皆看。最后注意,不支持
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;
/**
* 實(shí)體對(duì)象查詢
* @author Administrator
*/
public class SimpleObjectQueryTest2 extends TestCase {
public void testQuery1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/**
* 采用list查詢發(fā)出一條查詢語句,取得Student對(duì)象數(shù)據(jù)徙瓶、
* 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();
/**
* 出現(xiàn)N+1問題
*
* 1:發(fā)出查詢id列表的sql
* Hibernate: select student0_.id as col_0_0_ from t_student student0_
*
* N:在依次發(fā)出根據(jù)id查詢Student對(duì)象的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("---------------------------------------------");
/**
* 不會(huì)出現(xiàn)N+1問題
*
* 因?yàn)閘ist操作已經(jīng)將Student對(duì)象放到了一級(jí)緩存中毛雇,所以再次使用iterate操作的時(shí)候
* 它首先發(fā)出一條查詢id列表的sql,在根據(jù)id到緩存中去數(shù)據(jù)侦镇,只有在緩存中找不到相應(yīng)的
* 數(shù)據(jù)時(shí)灵疮,才會(huì)發(fā)出sql到數(shù)據(jù)庫(kù)中查詢
*
*/
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("---------------------------------------------");
/**
* 再次發(fā)出查詢sql
*
* 在默認(rèn)情況下list每次都會(huì)向數(shù)據(jù)庫(kù)發(fā)出查詢對(duì)象的sql,除非配置查詢緩存壳繁,所以下面的list操作
* 雖然在一級(jí)緩存中已經(jīng)有了對(duì)象數(shù)據(jù)震捣,但list默認(rèn)情況下不會(huì)利用緩存,而再次發(fā)出sql
*
* 默認(rèn)情況下闹炉,list會(huì)向緩存中放入數(shù)據(jù)蒿赢,但不會(huì)利用數(shù)據(jù)
*
*/
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.對(duì)于方法一,我們可以看到使用list方法發(fā)出一條sql語句將所有的對(duì)象都查詢出來渣触。
2.對(duì)于方法二羡棵,我們使用iterator方法進(jìn)行查詢,此時(shí)查詢就不一樣了昵观,先是發(fā)出一條sql語句將所有的id主鍵都查詢出來晾腔,然后根據(jù)主鍵去找相關(guān)的數(shù)據(jù)舌稀,首先在緩存中找,如果緩存中沒有對(duì)應(yīng)的數(shù)據(jù)灼擂,那么就會(huì)發(fā)出sql語句去數(shù)據(jù)庫(kù)中查詢壁查,于是就出現(xiàn)了N+1問題,因?yàn)樵诤竺鏁?huì)發(fā)出多條sql語句剔应,這樣對(duì)于數(shù)據(jù)庫(kù)的性能損耗是很大的睡腿。
3.從方法三中我們也可以看到當(dāng)我們先使用list查詢出對(duì)象之后再使用iterator方法查詢就不會(huì)再次發(fā)出sql語句,因?yàn)閕terator方法會(huì)首先在緩存中找峻贮,而list方法已經(jīng)將相關(guān)數(shù)據(jù)放在了緩存中席怪,所以iterator方法不會(huì)再次發(fā)出sql語句,但是如果我們?cè)诤竺孢€是使用list方法而不是iterator方法纤控,那么還是會(huì)發(fā)出查詢語句挂捻,從方法四中可以看到,這就說明船万,iterator方法可以利用緩存刻撒,而list方法不會(huì)利用緩存。