Hibernate高级查询

来源:互联网 发布:负面情绪 知乎 编辑:程序博客网 时间:2024/04/30 15:12

:动态查询

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_.priceOrder [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 orderswhere 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);