主要内容:
- 简单属性查询
- 实体对象查询
一、概述
- 数据查询与检索是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
Classes.hbm.xml
初始化
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方法不会利用缓存。