Hibernate高级查询

:动态查询

HQLQBC能够完成许多相同的任务,相比之下,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查询方式。但是,如果只有在程序运行中才能明确查询语句的内容(也称为动态查询)QBCHQL更加方便。

在实际应用中,经常有这样的查询需求:用户在客户界面的查询窗口输入查询条件,按下”查询按钮”后,业务层执行查询操作,返回匹配的查询结果。


以下的程序通过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的属性为21Hibernate执行的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对象与1000Order对象关联,就会加载1000Order对象。

在实际应用中,往往只需要访问orders集合中的部分Order对象,如访问价格大于100Order对象,此时调用Customer.getOrders().iterator()方法会影响运行时性能,因此它会多余加载应用程序不需要访问的Order对象。

-> 不能对orders集合中的Order对象进行排序,如按照Order对象的价格或者订单的编号排序。

有两种解决上问题的变法,一种办法是通过HQLQBC查询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

SessioncreateFilter()方法用来过滤集合,它具有以下特点。

它返回Query类型的实例。

它的第一个参数指定一个持久化对象的集合,这个集合是否已被初始化并没有关系,‘

但它所属的对象必须处于持久化状态。对于以上程序代码,如果Customer对象处于游离状态或临时状态,Hibernate在运行时会抛出以下异常。

[java] org.hibernate.QueryException:The collection was unreferenced

它的第二个参数指定过滤条件,它由合法的HQL查询语句组成。

不管持久化对象的集合是否已被初始化,Querylist()方法都会执行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对象,Querylist()方法不会创建Order对象,

仅返回已经存在的Order对象的引用,下图



如果Customer对象的orders集合没有被初始化,Querylist()方法会创建相应的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的子查询依赖于底层数据库对子查询的支持能力。并不是所有的数据库都支持子查询。例如,MySQL4.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);




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值