- 概述:数据查询与检索是Hibernate中的一个亮点,相对其他ORM实现而言,Hibernate提供了灵活多样的查询机制。
- 标准化对象查询(Criteria Query):以对象的方式进行查询,将查询语句封闭为对象操作。优点:可主动性好,符合Java程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)
n 例:查询用户名以“J”开头的所有用户
Criteria c = session.createCreateria(User.class);
c.add(Expression.like(“name”,”J%”));
List users = c.list();
- Hibernate语句查询(Hibernate Query Language,HQL):它是完全面向对象的查询语句,查询功能非常强大,具备多态、关联待特性。
- Native SQL Queries(原生SQL查询):直接使用标准SQL语言或跟特定数据库相关的SQL进行查询。
- Ø 注:除了Java类与属性的名称外,查询语句对大小写并不敏感。 所以 SeLeCT 与 sELEct 以及 SELECT 是相同的,但是 org.hibernate.eg.FOO 并不等价于 org.hibernate.eg.Foo 并且 foo.barSet 也不等价于 foo.BARSET
- HQL用面向对象的方式生成SQL
n 以类和属性来代替表和数据列
n 支持多态
n 支持各种关联
n 减少了SQL的冗余
- HQL支持所有的关系数据库操作
n 连接(joins,包括inner/outer/full joins),笛卡尔积(Cartesian products)
n 投影(projection)
n 聚合(Aggregation,max,avg)和分组(group)
n 排序(ordering)
n 子查询(subqueries)
n SQL函数(SQL function calls)
HQL语言是一套中立语言:它与任何数据库都没有关系,通过hibernate的方言自动的把HQL语言翻译成各种数据库语言
027-1 HQL简单例子
例一:查询用户名以“J”开头的所有用户:
Query query = session.createSQLQuery("from User user where user.name like ‘J%‘"); List users = query.list(); |
注:User是指User实体类 并重新起别名为user
user.name:表示实体类的属性
HQL中不使用表名,字段名,全部使用实体类、实体类的属性
例二:复杂例子:从User和Group中查找属于“admin”组的所有用户
Query query = session.createQuery("from User user where user.group.name=‘admin‘"); |
注:user.group.name:表示实体类User中组(group)中的名称(name)
传统的SQL:
select user.userId as userId, user.name as name, user.groupId as groupid, user.idCardId as IdCardId from TBL_USER USER, TBL_GROUP group where (group.groupName=’admin’ and user.groupid=groupid.groupid) |
语法:HQL语言中的关键字不区分大小写,但是属性和类名区分大小写,from后面跟的是类名,不是表名。
HQL演示环境
学生与班级的关联映射
Classes实体类:
public class Classes { private int id; private String name; //一对多通常使用Set来映射,Set是不可重复内容。 //注意使用Set这个接口,不要使用HashSet,因为hibernate有延迟加载, private Set students; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set getStudents() { return students; } public void setStudents(Set students) { this.students = students; } } |
Student实体类:
public class Student { private int id; private String name; private Date createTime; private Classes classes; public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Classes getClasses() { return classes; } public void setClasses(Classes classes) { this.classes = classes; } } |
Classes实体类的映射文件:
<hibernate-mapping> <class name="com.wjt276.hibernate.Classes" table="t_classes"> <id name="id" column="id"> <generator class="native"/> </id> <property name="name" column="name"/> <!-- <set>标签 映射一对多(映射set集合),name="属性集合名称" 然后在用<key>标签,在多的一端加入一个外键(column属性指定列名称)指向一的一端 再采用<one-to-many>标签说明一对多,还指定<set>标签中name="students"这个集合中的类型 要使用完整的类路径(例如:class="com.wjt276.hibernate.Student") inverse="false":一的一端维护关系失效(反转) :false:可以从一的一端维护关系(默认);true:从一的一端维护关系失效 --> <set name="students" inverse="true"> <key column="classesid"/> <one-to-many class="com.wjt276.hibernate.Student"/> </set> </class> </hibernate-mapping> |
Student实体类映射文件:
<hibernate-mapping> <class name="com.wjt276.hibernate.Student" table="t_student"> <id name="id" column="id"> <generator class="native"/> </id> <property name="name" column="name"/> <property name="createTime"/> <many-to-one name="classes" column="classesid"/> </class> </hibernate-mapping> |
初始化演示需要的数据:
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 */ 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; } } |
org.hibernate.Query Session.createQuery返回执行HQL查询的Query实例。
Session.createSQLQuery返回执行本地SQL查询的Query实例。
- List org.hibernate.Query.list():返回结果集列表。注:不同的HQL语句,返回不同的结果集列表
1.简单属性的查询
01 单一属性查询
查询:所有学生称名:
query.list():返回是单一属性的集合,
元素类型和实体类中相应的属性类型一致,就是说List的类型取决于实体类属性的类型。
session = HibernateUtils.getSession(); tx = session.beginTransaction(); //单一属性查询,query.list()返回是单一属性的集合,元素类型和实体类中相应的属性类型一致,就是说List //的类型取决于实体类属性的类型 List student = session.createQuery("select name from Student").list(); for (Iterator iter = student.iterator(); iter.hasNext();){ String name = (String)iter.next();//实体类Student的属性name类型为String System.out.println(name); } tx.commit(); |
注:select name from Student
Student:不是数据库中的表,而是Student实体类,所以需要区分大小写
name:不是表中的字段名,而是Student实体类中的属性,所以需要区分大小写
02 多个属性查询
查询:所有学生的ID、姓名
Query.list():返回结果集合元素是对象数组
数组元素的类型和对应的属性在实体类中的类型一致
数组的长度取决与select中属性的个数
数组中元素的顺序,也取决与select中属性的顺序
session = HibernateUtils.getSession(); tx = session.beginTransaction(); //查询多个属性,其返回结果集合元素是对象数组 //数组元素的类型和对应的属性在实体类中的类型一致 //数组的长度取决与select中属性的个数 //数组中元素的顺序,也取决与select中属性的顺序 List student = session.createQuery("select id, name from Student").list(); for (Iterator iter = student.iterator(); iter.hasNext();){ //集合中的元素是2个长度对象的数组(因为select查询了id,name两个属性) Object[] obj = (Object[])iter.next(); //数据组中第一个元素是id,类型是int;第二个元素是name,类型是String(因为select中是id(int),name(String)顺序) System.out.println(obj[0] + "," + obj[1]); } tx.commit(); |
03 查询一个或多个属性,要求返回实体对象
Query.list():返回结果集合元素是实体类对象
* 查询一个或多个属性,要求返回实体对象,
* 可采用HQL动态实例化实体对象
* 实现条件:实体类中要一个构造方法,其参数是你需要查询的属性,还需要一个无参构造方法(这是实体类的必需条件之一)
* 然后在HQL语句中采用new 一个实体对象就可以了。
* 如果查询所有学生的的id、姓名,要求list返回Student对象:select new Student(id,name) from Student
* 这样session.createQuery().list()返回的就是Student实体对象
session = HibernateUtils.getSession(); tx = session.beginTransaction(); //如果认为返回数组不够对象化,可以采用hql动态实例化Student对象,此时list中为Student对象集合。 //当然Student实体类中要有以id、name为参数的构造方法,和一个无参构造方法(是实体类必要条件之一) 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()); } |
04 使用别名查询
HQL语句中,可以使用别名查询,可以使用空格或as进行命名别名
1、select s.id, s.name from Student s 2、select s.id, s.name from Student as s |
2.实体对象查询
2.1简单的实体对象查询
* 语法:from 实体类(如果:session.createQuery("from Student");
* 可以忽略select
* Query.list():返回实体对象集合(Student对象集合)
session = HibernateUtils.getSession(); tx = 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()); } tx.commit(); |
2.2 实体对象使用别名查询
实体类对象查询,也可以使用别名,同样可以使用空格或as
1、List students = session.createQuery("from Student s").list(); 2、List students = session.createQuery("from Student as s").list(); |
2.3使用select查询实体对象
如果使用select查询实体对象,必须采用别名,可以用空格或as关键字, select后直接跟别名就可以了。
1、List students = session.createQuery("select s from Student s").list(); 2、List students = session.createQuery("select s from Student as s").list(); |
2.4 HQL不支持select * from ……
HQL不支持select * from ……查询,否则会抛异常,但*可以用是特定的函数中。
2.5 query.iterate查询数据
* query.iterate()方式返回迭代查询
* 会开始发出一条语句:查询所有记录ID语句
* Hibernate: select student0_.id as col_0_0_ from t_student student0_
* 然后有多少条记录,会发出多少条查询语句。
* n + 1问题:n:有n条记录,发出n条查询语句;1 :发出一条查询所有记录ID语句。
* 出现n+1的原因:因为iterate(迭代查询)是使用缓存的,
第一次查询数据时发出查询语句加载数据并加入到缓存,以后再查询时hibernate会先到ession缓存(一级缓存)中查看数据是否存在,如果存在则直接取出使用,否则发出查询语句进行查询。
session = HibernateUtils.getSession(); tx = session.beginTransaction(); /** * 出现N+1问题 * 发出查询id列表的sql语句 * Hibernate: select student0_.id as col_0_0_ from t_student student0_ * * 再依次发出根据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 students = session.createQuery("from Student").iterate(); while (students.hasNext()){ Student student = (Student)students.next(); System.out.println(student.getName()); } tx.commit(); |
2.6 query.list()和query.iterate()的区别
先执行query.list(),再执行query.iterate,这样不会出现N+1问题,
* 因为list操作已经将Student对象放到了一级缓存中,所以再次使用iterate操作的时候
* 它首先发出一条查询id列表的sql,再根据id到缓存中取数据,只有在缓存中找不到相应的
* 数据时,才会发出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()); } System.out.println("---------------------------------------------------------"); // 不会出现N+1问题,因为list操作已经将数据加入到一级缓存。 Iterator iters = session.createQuery("from Student").iterate(); while (iters.hasNext()){ Student student = (Student)iters.next(); System.out.println(student.getName()); } |
2.7 两次query.list()
* 会再次发出查询sql
* 在默认情况下list每次都会向数据库发出查询对象的sql,除非配置了查询缓存
* 所以:虽然list操作已经将数据放到一级缓存,但list默认情况下不会利用缓存,而再次发出sql
* 默认情况下,list会向缓存中放入数据,但不会使用数据。
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只向缓存中放入数据,不会使用缓存中数据 students = session.createQuery("from Student").list(); for (Iterator iter = students.iterator(); iter.hasNext();){ Student student = (Student)iter.next(); System.out.println(student.getName()); } |