一:动态查询
HQL与QBC能够完成许多相同的任务,相比之下,HQL能更加直观地表达复杂的查询语句。
而通过QBC来表达复杂的查询语句很麻烦。以下的两个代码完成相同的任务,但是HQL检索方式的程序代码更加简洁:
//HQL检索方式
Query query=session.createQuery(“from Customer c”
+”where (c.name like ‘T%’ and c.name like ‘%m’)”
+”or (c.age not between 18 and 25)”
);
//QBC检索方式
Criteria criteria = session.createCriteria (Customer.class);
criteria.add(Restrictions.or(
Restrictions.add(Restrictions.like(“name”,”T%”)),
Restrictions.like(“name”,”%m”)),
Restrictions.not(
Restrictions.between(“age”,new Integer(18),
new Integer(25)))))
;
因此,如果在程序运行前就明确了查询语句的内容(也称为静态查询),应该优先考虑HQL查询方式。但是,如果只有在程序运行中才能明确查询语句的内容(也称为动态查询),QBC比HQL更加方便。
在实际应用中,经常有这样的查询需求:用户在客户界面的查询窗口输入查询条件,按下”查询按钮”后,业务层执行查询操作,返回匹配的查询结果。
以下的程序通过HQL来生成动态的查询语句,包含了大量的逻辑判断流程:
public static List findCustomers (String name,int age) throws HibernateException {
StringBuffer hqlStr = new StringBuffer("from Customer c");
if (name != null){
hqlStr = hqlStr.append(" where lower(c.name) like :name");
}
if (age != 0 && name != null) {
hqlStr.append(" and c.age = :age");
}
if (age != 0 && name == null) {
hqlStr.append(" where c.age = :age");
}
Session session = HibernateSessionFactory.getSession();
Query query = session.createQuery(hqlStr.toString());
if (name != null) {
query.setString("name", name);
}
if (age != 0) {
query.setInteger("age", age);
}
return query.list();
}
如果采用QBC检索方式,可以简化程序代码中的逻辑判断流程:
public List findCustomers(String name,int age)
throws HibernateException {
Criteria criteria = getSession().createCriteria(Customer.class);
if (name != null) {
criteria.add(
Restrictions.ilike(“name”,name.toLowerCase(),
MatchMode.ANYWHERE));
}
if (age != 0) {
criteria.add(Restrictions.eq(“age”,new Integer(age)));
}
return criteria.list();
}
如果Customer对象的name属性为”T”,age的属性为21,Hibernate执行的sql语句为:
select id,name,age from customers where lower(name) like ‘%t%’
and age = 21;
二:集合过滤
对于应经加载的Customer持久化对象,假定它的orders集合由于使用延迟检索策略而没有被初始化,那么只要调用customer.getOrders().iterator()方法,Hibernate就会初始化orders集合,在初始化时从数据库中加载所有与Customer关联的Order持久化对象。这种方式存在两大不足:
-> 假定这个Customer对象与1000个Order对象关联,就会加载1000个Order对象。
在实际应用中,往往只需要访问orders集合中的部分Order对象,如访问价格大于100的Order对象,此时调用Customer.getOrders().iterator()方法会影响运行时性能,因此它会多余加载应用程序不需要访问的Order对象。
-> 不能对orders集合中的Order对象进行排序,如按照Order对象的价格或者订单的编号排序。
有两种解决上问题的变法,一种办法是通过HQL或QBC查询orders集合:
Customer customer = (Customer) session.load(Customer.class, new Long(1));
List result = session.createQuery("from Order o where o.customer =:customer and o.price > 100 order by o.price")
.setEntity("customer",customer).list();
Iterator it = result.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
生成的SQL语句为,以及SQL查询语句为:
Hibernate:
select
order0_.id as id1_,
order0_.order_number as order2_1_,
order0_.price as price1_,
order0_.customer_id as customer4_1_
from
orders order0_
where
order0_.customer_id=?
and order0_.price>100
order by
order0_.price
Order [id=2, orderNumber=Tom_Order002, price=200.0]
Order [id=3, orderNumber=Tom_Order003, price=300.0]
还有一种办法是使用集合过滤:
List result = session
.createFilter(customer.getOrders(),”where this.price > 100
order by this.price”).list();
Iterator it = result.iterator();
while (it.hasNext()) {
Order order = (Order)it.next();
.....
}
生成的SQL语句为:
select
order0_.id as id1_,
order0_.order_number as order2_1_,
order0_.price as price1_,
order0_.customer_id as customer4_1_
from
orders order0_
where
order0_.customer_id = ?
and order0_.price>100
order by
order0_.price
Session的createFilter()方法用来过滤集合,它具有以下特点。
它返回Query类型的实例。
它的第一个参数指定一个持久化对象的集合,这个集合是否已被初始化并没有关系,‘
但它所属的对象必须处于持久化状态。对于以上程序代码,如果Customer对象处于游离状态或临时状态,Hibernate在运行时会抛出以下异常。
[java] org.hibernate.QueryException:The collection was unreferenced
它的第二个参数指定过滤条件,它由合法的HQL查询语句组成。
不管持久化对象的集合是否已被初始化,Query的list()方法都会执行SQL查询语句,到数据库中检索Order对象,对于以上程序代码,Hibernate执行的SQL查询语句为:
select id,order_number,price from orders
where customer_id = 1 ans price > 100 order by price;
如果Customer对象的orders集合已经被初始化,为了保证Session的缓存中不会出现OID相同的Order对象,Query的list()方法不会创建Order对象,
仅返回已经存在的Order对象的引用,下图
如果Customer对象的orders集合没有被初始化,Query的list()方法会创建相应的Order对象,但是不会初始化Customer对象的orders集合:
如:
集合过滤除了用于集合排序或设置约束条件,还有其他用途,下面例子:
(1):为Customer对象的orders集合分页:
List result = session.createFilter(customer.getOrders(),”order by this.price asc”)
.setFirstResult(10).setMaxResults(50).list();
(2):检索Customer对象的orders集合中Order对象的订单编号:
List result = session.createFilter(Customer.getOrders(),”select this.orderNumber”).list();
(3): 检索数据库中与 Customer 对象的 orders 集合中 Order 对象的价格相同的所有的 Order 对象 :
List result = session.createFilter(customer.getOrders(),
“select other from Order other where other.price = this.price”
).list();
生成的SQL查询语句为:
Hibernate:
select
order0_.id as id1_,
order0_.order_number as order2_1_,
order0_.price as price1_,
order0_.customer_id as customer4_1_
from
orders order0_,
orders order1_
where
order1_.customer_id = ?
and order0_.price=order1_.price
(4):假定Order类与LineItem类一对多关联,而LineItem类与Item类多对一关联
。Item类表示商品,LineItem类表示订单中购买单项商品的信息。集合过滤可用于
检索Order对象的lineItems集合中Lineitem对象的Item:
List result = session.createFilter(order.getLineItems(),”
select this.item
”).list();
二:子查询
HQL支持在where子句中嵌套入子查询语句。如:以下HQL查询语句返回具有1条以上订单的客户。
List customerLists = query.list();
from Customer c where 1 <(select count(o) from c.orders o);
子查询语句必须放在括号内。和以上HQL查询语句对应的SQL语句为:
select * from customers c where 1 <
(select count(o.ID) from orders o where c.id = o.customer_id)
相同的代码如下:(没有使用子查询)
Query query = session.createQuery("select c from Customer c left outer join c.orders o group by c.id having count(o) > 1");
关于子查询的用法,有以下几点说明。
(1):子查询可以分为相关子查询和无关子查询。
相关子查询是指子查询语句引用了外查询语句定义的别名,如本节开头的子查询语句引用了别名”c”,它是外层查询语句为Customer类定义的别名。无关子查询是指子查询与外层查询语句无关。
from Order o where o.price > (select avg(ol.price) from Order o1)
(2):HQL的子查询依赖于底层数据库对子查询的支持能力。并不是所有的数据库都支持子查询。例如,MySQL从4.1.x版本开始才支持子查询,而4.0.x或者更老的版本都不支持子查询。如果希望应用程序能够在不同的数据库平台之间的移植,应该避免使用HQL的子查询功能。无关子查询语句可以改写为单独的查询语句;相关子查询语句可以改写为连接查询和分组查询语句,例如,以下HQL查询语句也能查询具有一条以上订单的客户:
select c from Customer c join c.orders o group by c.id having
count(o) > 1;
(3):如果子查询语句返回多条记录,可以用以下关键字来量化。
-> all:表示子查询语句返回的所有记录。
-> any:表示子查询语句返回的任意一条记录。
-> some:与 “any”等价
-> in:与”=any”等价
-> exists:表示子查询语句至少返回一条记录。
如:以下HQL查询语句返回所有订单的价格都小于100的客户:
from Customer c where 100 > all (select o.price from c.orders o);
生成的SQL语句为:
select
customer0_.id as id0_,
customer0_.name as name0_,
customer0_.age as age0_
from
customers customer0_
where
100>all (
select
orders1_.price
from
orders orders1_
where
customer0_.id=orders1_.customer_id
)
以下查询语句返回有一条订单的价格小于100的客户:
from Customer c where 100 > any (select o.price from c.orders o);
生成的SQL语句为:
select
customer0_.id as id0_,
customer0_.name as name0_,
customer0_.age as age0_
from
customers customer0_
where
100>any (
select
orders1_.price
from
orders orders1_
where
customer0_.id=orders1_.customer_id
)
以下返回有一条订单的价格等于100的客户。
from Customer c where 100 = any (select o.price from c.orders o);
或者: from Customer c where 100 = some(select o.price from c.orders o);
或者:from Customer c where 100 in (select o.price from c.orders o);
以下HQL查询语句返回至少有一条订单的客户:
from Customer c where exists(from c.orders);