Hibernate查询:
标准化对象查询(Criteria Query):以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)
Hibernate语言查询(Hibernate Query Language,HQL):它是完全面向对象的查询语句,查询功能非常强大,具备多态、关联等特性。Hibernate官方推荐使用HQL进行查询。
Native SQL Queries(原生SQL查询):直接使用标准SQL语言或跟特定数据库相关的SQL进行查询。
Hibernate语言查询(Hibernate Query Language,HQL):
HQL用面向对象的方式生成SQL语句:
以类和属性来代替表和数据列;
支持多态;
支持各种关联;
减少了SQL的冗余;
HQL支持所有的关系数据库操作:
连接(joins,包括Inner/outer/full joins),笛卡尔积(cartesian products);
投影(projection)--查询一到多个属性;
聚合(Aggregation,max, avg)和分组(group);
排序(Ordering);
子查询(Subqueries);
SQL函数(SQL function calls)
简单示例:查询用户名以“王”开头的所有用户。
复杂示例:从User和Group中查找属于“admin”组的所有用户。
如果用传统的SQL则查询语句如下:
select user.userId as userId, user.name as name, user.groupId as groupId, user.idCardId as idCardId
HQL语言-简单属性查询:
(1)单一属性查询:
说明:
(2)多属性查询:
说明:
(3)采用HQL动态实例化对象方式返回所要查询的属性:
说明:
User对象必须提供一个构造函数,形式为:
否则将不能创建User对象,因为创建对象时调用的是带参数的构造方法。
(4)采用别名的方式查询属性值:
不采用as关键指定别名
采用as关键字指定别名
说明:当指定别名后,在查询的属性名需要使用前缀时必须使用别名,而不能再使用原实体对象的名称,即: u.id, u.name。
【小结】
单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致;
多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致;
数组的长度取决与select中属性的个数;
如果认为返回数组不够对象化,可以采用HQL动态实例化对象的方式:
HQL语言-实体对象查询:
查询实体对象
说明:
如:
if(user.getId() == 1){
user.setName("AAAA");
//会发出update语句,因为此时的user对象是实体对象,由于名字发生了变化,所以此时在提交事务后会发出相应的update语句与数据库同步
session.getTransaction().commit();
}
使用别名查询实体对象-忽略select关键字
不采用as关键指定别名
采用as关键字指定别名
使用别名查询实体对象-不省略select关键字
不采用as关键指定别名
采用as关键字指定别名
注:不能使用select
(1)使用list()方法查询数据:
如:
List users = session.createQuery("from User").list();
说明:list()方法只会把查询出来的数据放入Session(一级)缓存中,而不使用一级缓存中的数据(缓存中存在相同数据时,则不会再放)。
(2)使用iterate()方法查询数据:
如:
Iterator iter =
session.createQuery("from User").iterate();
说明: iterate()方法总会首先发出一条查询对象id列表的sql 语句,然后根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的SQL语句查询数据。这说明iterate()方法使用缓存。
【小结】
N + 1问题,在默认情况下,使用query.iterate()查询,有可以能出现N+1问题。
所谓的N+1是在查询的时候发出了N+1条SQL语句:
1: 首先发出一条查询对象id列表的SQL语句;
N: 根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的SQL语句。
list()方法和iterate()方法的区别?
list()方法:每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据;
iterate()方法:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题。
HQL语言-条件查询:
(1)采用拼接字符串形式查询
如:
String hql =
String key = "'%张%'";
hql = hql + key;
List user = session.createQuery(hql).list();
(2)采用占位符的形式查询
如:
Query query = session.createQuery("select u.id, u.name from User u where u.name like ?");
query.setParameter(0, “%王%");
List users = query.list();
或使用方法链编程:
List users = session.createQuery("select u.id, u.name from User u where u.name like ?")
注:在Hibernate中第1个占位符的下标位置从0开始
(3)采用参数命名的方式查询
单一条件
List users = session.createQuery("select u.id, u.name from User u where u.name like :myname")
多条件
List users = session.createQuery("select u.id, u.name from User u where u.name like :myname and u.id=:myid")
说明:定义命名参数固定格式::+参数名称(即:myid )
赋值时,直接写参数名即可: setParameter("myid", 1)
setParameter方法用于为参数赋单一值
(4)采用in关键字(即包含)查询
List users = session.createQuery("select u.id, u.name from User u where u.id in(:myids)")
说明:由于in中的条件是集合的形式,所以赋值是需要采用setParameterList这种方法,赋的值则采用对象数组的形式。
(5)采用数据库函数查询——不建议使用
如:
List students = session.createQuery("select u.id, u.name from User u where CONVERT(varchar(10), DATEPART(mm,u.create_time))=?")
(6) 查询某一时间段的数据
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//查询2010-06-10到2010-07-31入职的员工
List users = session.createQuery("select u.id, u.name from User u where u.create_time between ? and ?")
.setParameter(0, sdf.parse("2010-06-10 00:00:00"))
.setParameter(1, sdf.parse("2010-07-31 23:59:59"))
.list();
说明:在实际项目开发中,关于日期段的查询一般都是从起始日期的00:00:00时间开始,结束于结束日期的23:59:59,这样对于日期类型的数据库表的字段更为精确。
【小结】
可以采用拼字符串的方式传递参数
可以采用 ?来传递参数(索引从0开始)
可以采用 :参数名 来传递参数
如果传递多个参数,可以采用setParamterList方法
在HQL语言中可以使用数据库的函数,如:CONVERT、DATEPART
HQL语言-原生SQL语句查询:
采用原生SQL语句查询
List users = session.createSQLQuery("select * from t_user").list();
说明:
使用原生SQL语句,需要使用:SQLQuery接口;
使用原生SQL语句时,不会往Session(一级)缓存中放入数据,即SQLQuery接口中的list()方法不会往Session(一级)缓存中放入数据;
SQLQuery接口的list()方法返回的List集合中存储的是数组对象
HQL语言-外置命名查询:
在实际开发中这并不是决对的,因为当把所有的HQL语句放入到一个配置文件中在调试时,会增加一定的工作量,有时显得会不太方便,当项目追求低耦合度时,可以考虑采用这种方式。
实体映射文件:
在的标签后面加入如下代码:
说明: 配置文件中之所以使用… 是因为在HQL语句中含有”<”号,该符合是XML语言的组成部分,如果文本中含有XML语言特定的符号时可以将文本放在 中,如果不使用… ,则这些特定符号需要进行转义,否则会引起一些问题。
使用:
List users = session.getNamedQuery("searchUsers")
.setParameter(0, 6)
.list();
说明:
在映射文件中采用标签来定义HQL语句
在程序中采用session.getNamedQuery()方法得到HQL查询串
HQL语言-查询过滤器:
启用查询过滤器后,当Session没有关闭的时候,只要在Session中执行的所有HQL语句都会自动加上查询过滤器中的条件(即实现过滤功能)。
(1)定义查询过滤器
在的标签后面加入如下代码:
说明:
filtertest:查询过滤器名称
myid:过滤条件中的参数名称
type:指定参数的类型,本例为小写的integer表示这是Hibernate中的类型,也可以写成Java中的java.lang.Integer
(2)为实体映射文件指定使用查询过滤器
在标签中的最后加入如下代码:
说明:
name:指定要使用的查询过滤器名称
condition:指定查询过滤器的条件,将来过滤器将使用该条件进行过滤操作。其中“< ”表示的是“<”号,因为该符号是XML语言的特殊符号,所以在这里需要进行转义。
(3)在程序中启动查询过滤器
//启用查询过滤器,只有启动以后查询过滤器才起作用
session.enableFilter("filtertest")
【小结】
在映射文件中定义过滤器参数
在类的映射中使用这些参数
在程序中启用过滤器
查询过滤器中的条件在Hibernate生成具体的SQL语句时会自动加上该条件,所以条件中必须使用表中的字段名
HQL语言-分页查询:
Hibernate分页查询
List users = session.createQuery("from User")
//指定从第几条记录开始,下标从0开始,本例为从第2条记录开始查询
.setFirstResult(1)
//每页显示多少条记录
.setMaxResults(2)
.list();
HQL语言-对象导航查询:
对象导航查询:
List users =
session.createQuery("select u.name from User u where u.dept.name like '%人力%'").list();
HQL语言的含义是:查找部门名称中含有“人力”字样的部门中的员工姓名字段。
说明:查询的条件是根据User类中的关联的实体对象dept的名称来进行查询的,其中dept是User类中对Dept的引用,那么这种导航的工作原理是根据配置文件中的关联映射完成的。
在HQL语句中采用 . 进行导航
HQL语言-连接查询:
Hibernate中支持3种连接查询:
内连接
左外连接
右外连接
说明: HQL中的连接必须采用对象导航进行连接,否则将抛出
org.hibernate.hql.ast.QuerySyntaxException异常。
(1)内连接
示例:查询员工的姓名及所在部门的名称
内连接有两种方式:
不使用inner关键字
List users = session.createQuery("select d.name, u.name from User u
使用inner关键字
List users = session.createQuery("select d.name, u.name from User u inner join u.dept d").list();
说明:
(2)左外连接
示例:查询部门的名称(包含没有员工的部门)及员工的姓名
List users = session.createQuery("select d.name, u.name from Dept d left join d.users u").list();
说明:
(3)右外连接
示例:查询员工姓名(包含未分配部门的员工)及员工所在部门的名称。
List users = session.createQuery("select d.name, u.name from Dept d right join d.users u").list();
说明:Hibernate会根据Dept实体类的映射文件中的关联映射关系自动发出右外连接的SQL语句(即t_dept表与t_user表之间采用右外连接的SQL语句)。
HQL语言-统计查询:
示例:查询员工人数
统计总数,一般有编程两种方式:
(1)采用获取返回的List集合长度的方式
说明:这种方式效率不高,因为返回的是一个List,而该List中就一个元素(即统计的数量值),然后还需要使用get方法将该元素取出。
(2)采用从返回的Long对象中获取统计数值
Long count =
说明:这种方式效率高,因为返回的不是一个List集合,而是一个长整型的对象,该对象即是统计出来的数据,因为Hibernate使用长整型来封装统计出来的数值,目的是防止统计出来的数值过大,造成数据溢出异常。数据类型一定要切记,是Long(即封装类)。
HQL语言-DML查询:
示例:
session.createQuery("update User u set u.name=? where u.id < ?")
.setParameter(0, "XXX")
.setParameter(1, 5)
.executeUpdate();
说明:执行完批量的update操作后数据库中的值已经发生了变化,但是缓存中的值并没有发生变化还是原来的值,但是当我们访问时,其实访问的是缓存中的值,这就造成了数据的不准确性。
重点!!! |
注:
session.get()和session.load()区别:
get()方法:
1. 不支持延迟加载,会马上发出查询sql语句,加载User对象;
2. 使用get()方法加载数据,如果数据库中不存在相应的数据,则返回null。
load()方法:
1. 支持延迟加载,只有真正使用这个对象的时候,才会发出查询sql语句
2. 使用load加载数据,如果数据库中没有相应的数据,那么抛出ObjectNotFoundException,即不会返回null。
get(),load()方法会把查询上来的数据放入缓存,也会使用缓存
get(),load()方法会将查询上来的数据放入一级缓存中一份,然后再将该数据放入二级缓存中一份;先查找一级缓存再查找二级缓存,如果在任何一级缓存中能够查到该数据,则直接进行使用,否则发出SQL加载数据
结论:session是共享二级缓存(SessionFactory缓存)的
Query query = session.createQuery("")
query.iterate()
iterate() 查询实体对象:
iterate() 使用缓存(但不包括查询缓存,oid是存储在查询缓存中的),发出查询id的SQL语句,不会发出查询实体对象的SQL语句。
原理同load()方法,缓存中如果存在该实体对象,则直接使用,如果不存在该实体对象,则发出SQL语句去查询。
iterate() 方法查询普通属性:
一级缓存、二级缓存只存储实体对象
发出查询姓名属性的SQL语句;不会发出查询ID属性列表的SQL语句(因为此时的HQL语句查询的是普通属性而不是实体对象)
* 一(二)级缓存是缓存实体对象的,即如果查询出来的是实体对象,则才向一(二)级缓存中存放,如果查询出来的是普通属性,则不向一(二)级缓存中存放。
* 思考:执行如下语句,是否将查询出来的数据存放到一级缓存中?
Query.list()方法:
1、当关闭查询缓存(默认)时,list()方法不使用一级缓存和二级缓存中的数据
2、当开启查询缓存时,list()方法就会使用一级缓存和二级缓存中的数据,顺序是先找一级缓存再找二级缓存
Session 、SessionFactory:
clear() 方法清除缓存中的全部实体对象
evict() 方法可以指定要从缓存中清除哪个实体对象