Hibernate的检索方式(一)

来源:互联网 发布:kmp算法程序 编辑:程序博客网 时间:2024/06/05 08:12

Hibernate提供了一下的几种检索对象的方式。

(1):导航对象图检索方式

根据已经加载的对象,导航到其他对象。如对于已经加载的Customer对象,调用它的getOrders().iterator()方法,就可以导航到所有关联的Order对象,假如在关联级别使用了延迟加载检索策略,那么首次执行此方法时,Hibernate会从数据库中加载关联的Order对象,否则就从Session缓存中取得Order对象。

(2):OID检索方式

按照对象的IOD来检索对象。Sessionget()load()方法提供了这种功能。

如果在应用程序中事先知道了OID,就可以使用这种检索对象的方式。

(3):HQL检索方式

使用面向对象的HQL查询语言。Hibernate还提供了Query接口,它是专门的HQL查询接口,能够执行复杂的HQL查询语句。

(4):QBC检索方式

使用QBC API来检索对象。

(5):本地SQL检索方式

一:HQL检索方式

HQL(Hibernate Query Language)是面向对象的查询语言。是使用最为广泛的一种方式。

具有以下的功能:

->:在查询语句中设定各种查询条件。

->:支持投影查询,即仅检索出对象的部分属性。

->支持分页查询。

->支持连接查询

->支持分组查询,允许使用havinggroup by关键字

->提供内置的聚集函数,sum(),min()max()

->能够调用用户定义的SQL函数或者标准的SQL函数。

->支持子查询。

->支持动态的绑定参数

Query接口支持方法链的编程风格,它的setString()方法以及其他setXXX()方法都返回自身实例,而不是返回void 类型。

 下面的例子用的数据表为:


(1):对查询结果进行排序

HQL采用order by关键字对象查询结果排序。

Query query = session.createQuery("from Customer as c order by c.name desc");

(2):分页查询

采用HQL检索方式:

Query query = session.createQuery(“from Customer c order by c.name asc”)

query.setFirstResult(0);

query.setMaxResults(10);

List result = query.list();

(3):检索单个对象(uniqueResult()方法)

(4):按照主键逐个处理插叙的结果(iterate()方法)

Query接口还提供了一个iterator()方法,它和list()方法一样。

只是二者使用的查询机制不一样。

(5):可滚动的结果集()

(6):HQL查询语句中绑定参数

对于实际的应用,经常有这样的需求,用户在查询窗口中输入一些查询条件,要求返回满足条件的记录,如:用户提供了姓名和年龄信息,要求查询匹配的Customer对象。

应用程序可以定义一个findCustomers()方法来提供这一功能。


public static List findCustomers (String name) {Session session = HibernateSessionFactory.getSession();Query query = session.createQuery("from Customer as c where c.name =:_name");query.setString("_name", name);List list = query.list();HibernateSessionFactory.closeSession();return list;}


Tips:应该优先考虑使用按名字绑定方式。

(7):设置查询附属事项

在采用HQL检索方式或者QBC检索方式来检索数据库时,可以通过Query或者Criteria接口的一些方法来设定查询附属事项。

->setFlushMode()方法:设置清理缓存的模式。

->setCacheMode()方法:设置Session与第二级缓存的交互模式。

->setTimeout()方法:设置执行查询数据库操作的超时时间。

->setFetchSize()方法:为JDBC驱动程序设置批量抓取的数目。

->setLockMode()方法:设置锁定的模式。

->对于HQL检索方式,还可以通过Query接口的setReadOnly()方法来设置查询结果是否只允许读,而不允许修改。

设置清理缓存模式

Session清理缓存时,会先进性脏检查,即比较Customer持久化对象的当前属性与它的快照,来判断Customer对象的属性是否发生了变化,如果发生了变化,就称这个对象是“脏对象”,Session的缓存有3中清理模式,

下表中列出3种清理模式各种执行清理缓存操作的时间点。

图:


FlushMode.AUTO是默认值,这也是优先考虑的清理模式,它会保证在整个事物中,Session缓存中的对象和数据库数据保持一致。如果事物仅仅包含查询数据库的操作,

而不会修改数据库中的数据,也可以选用FlushMode.COMMIT模式,这个可以避免在执行各种查询操作时先清理缓存,以便稍微提高应用程序的性能。

->设置批量抓取数目

以下程序代码通过setFetchSize()方法,把底层JDBC驱动程序进行批量抓取的数目设置为50

List customers = session.createQuery(“from Customer”).setFetchSize(50).list();

以上Query接口的setFetchSize()方法和JDBC APIjava.sql.Statement接口的setFetchSize()方法的作用是一样的,都是为底层JDBC驱动程序设置批量抓取数目。

那么为底层JDBC驱动程序设置批量抓取数目的意义。

下面的程序通过JDBC API查询CUSTOMERS表中的所有记录。

ResultSet rs = stmt.executeQuery(“select id,name from CUSTOMERS”);While (rs.next()) {  String id = rs.getLong(1);  ......}

假如CUSTOMERS表中有100000条记录,那么java.sql.Statement对象的executeQuery()方法返回的java.sql.ResultSet对象中是否立即存放了这100000条记录呢?假如ResultSet对象中存放了这么多记录,那将消耗多大内存空间啊。实际上,

ResultSet对象实际上并不会包含这么多的数据,只有当程序遍历结果集时,ResultSet对象才会到数据库中抓取相应的数据。ResultSet对象的抓取数据过程对程序完全透明。

那么,是否每当程序访问结果集中的一条记录时,ResultSet对象就要到数据库中抓取一条记录?按照这种方式抓取大量记录需要频繁的访问数据库,显然效率很低。为了提高减少访问数据库的次数,JDBC希望ResultSet接口的实现能支持批量抓取,每次从数据库中抓取多条记录,将它们存放在ResultSet对象的缓存中,让程序慢慢的使用。

java.sql.Connection,java.sql.Statementjava.sql.ResultSet接口中都提供了setFetchSize(int size)方法。

HQL中还可以调用SQL函数。

->:HQL查询语句中调用函数()

->设定查询条件

 和sql查询一样,HQL查询语句通过where子句来设定查询条件。

From Customer c where c.name=’Tom’

Tips:where子句中给出的是对象的属性名,而不是字段名。

对于QBC查询,必须创建一个Criterion对象来设定查询条件。Restrictions类提供了创建Criterion实例的工厂方法:

  Criteria criteria = session.createCriteria(Customer.class);  Criterion nameEq = Restrictions.eq(“name”,”Tom”);  criteria.add(nameEq);

HQLQBC在设定查询条件中可用的各种运算符

图:


->字符串模式匹配

SQL查询一样,HQLlike关键字进行模糊查询,而QBCRestrictions类的like()方法进行模糊查询。模糊查询能够比较字符串是否与指定的字符串模式匹配,


->集合运算

(1):检索不包含任何订单的Customer对象:

  //HQL检索方式  List result = session.createQuery(“from Customer c where c.orders is empty”).list();

对于以上的HQL检索方式,Querylist()方法执行的SQL select 语句为:


  Select ID,NAME,AGE from CUSTOMERS c where  Not (exists(select o.ID from ORDERS o where c.ID=O.CUSTOMER_ID))

-----------------------------------------------------------------------------------------------------------------------------------


一:连接查询

HQLQBC支持的各种连接类型

图:


(1):默认情况下关联级别的运行时检索策略

在映射文件中可以设置关联级别的立即检索,延迟检索或迫切左外连接检索策略。

下面的程序代码没有显示指定与Customer关联的Order对象的检索策略:

  //HQL检索方式  List result = session.createQuery(“from customer c where c.name like ‘T%’”);  //QBC检索方式  List result = session.createCriterria(Customer.class);  .add(Restrictions.like(“name”,”T”,MatchMode.START)).list();

此时将采用Customer.hbm.xml映射文件中对orders集合设置的检索策略,但有个例外,那就是HQL会忽略映射文件中设置的迫切左外连接检索策略。

(2):迫切左外连接

下面程序覆盖映射文件中指定的检索策略(),显示指定对于Customer关联的order对象采用迫切左外连接检索策略:(就算customer对象的orders集合设置了lazytrue,也会被忽略掉)

  Query query = session.createQuery("from Customer c left join fetch c.orders o");  List result = query.list();

生成的SQL查询语句为:

  Select c.id C_ID,c.name,c.age,o.ID O_ID,o.order_number ,o.customer_id from customers c left outer join  Orders o on c.id = o.customer_id where (c.name like ‘T%’);

图:


HQL查询语句中,left join fetch 关键字表示迫切左外连接检索策略。在

QBC查询中,FetchMode.JOIN表示迫切左外连接检索策略。使用迫切左外连接检索 策略时,QueryCriterialist()方法返回的集合中存放Customer对象的引 用,每个Cusotmer对象的orders集合都被初始化,存放所有的关联的Order对象。

根据以上的查询结果,result 集合包含4Customer类型的元素,其中前3个元素相同,都引用OID1Customer持久化对象,它的orders集合中包含3Order对象,最后一个引用OID5Customer持久化对象。

图:


由此可见,当使用迫切左外连接检索策略时,查询结果中可能包含重复元素,

可以通过一个HashSet来过滤重复元素:

  List result = session.createCriteria(Customer.class);  .setFetchMode(“orders”,FetchMode.JOIN);  .add(Restrictions.like(“name”,”T”,MatchMode.START))  .list();  HashSet set = new HashSet(result);    for (Iterator it = set.iterator();it.hasNext();) {  Customer customer = (Customer)it.next();  ........  }

HQL查询语句中,对于各种类型的连接,都可以为被连接的类指定别名,例如:

  From Customer c left join fetch c.orders o  Where c.name like ‘T%’ and o.orderNumber like ‘T%’;

以上的HQL查询语句为Customer赋予别名“c”,还为c.orders赋予别名“o”,

在查询语句中可以通过o.xxx的形式引用Order对象的属性。

Hibernate允许在一条查询语句中迫切左外连接多个多对一或一对一关联的类。

下面的图中显示了3个类的关联关系,其中类A和类B以及类A和类C都是多对一的关联关系。

图:




以下二种检索方式是等价的,他们都能同时迫切左外连接类B和类C:

//HQL迫切左外连接检索方式

From A a left join fetch a.b b

Left join fetch a.c c

Where b is not null and c is not null;

//QBC迫切左外连接检索方式

List result=session.createCriteria(A.class);

.setFetchMode(“this.b”,FetchMode.JOIN);

.setFetchMode(“this.c”,FetchMode.JOIN);

.add(Restrictions.isNotNull(“this.b”));

.add(Restrictions.isNotNull(“this.c”));

假定有3个类,他们的关系下图,类A和类B以及类B和类C都是多对一关联关系。

:


可以通过HQL来同时迫切左外连接BC,但是QBC无法表达这种形式的迫切左外连接:


  From A a left join fetch a.b b   Left join fetch b.c c where b is not null and c is not null;

:左外连接

以下HQL查询语句指定左外连接查询:


  //HQL检索方式List result = session.createQuery("from Customer c left join c.orders where c.name like 'T%'").list();for (Iterator pairs = result.iterator();pairs.hasNext();){Object[]pair = (Object[])pairs.next();Customer customer = (Customer)pair[0];Order order = (Order)pair[1];  //如果orders集合使用延迟检索策略,以下代码会初始化Customer对象的orders集合。  Iterator orders = customer.getOrders().iterator();}

HQL查询语句中,left join关键字表示左外连接查询。使用左外连接查询时,

将根据映射文件的配置来决定orders集合的检索策略,不过,即使在Customer.hbm.xml文件中对orders集合设置了延迟检索策略,在运行以上的list()方法时,Hibernate执行的SQL查询语句仍然与迫切左外连接生成的查询语句相同。

selectc.id C_ID c.name,c.age,o.id O_ID,o.order_number,o.customer_id from customers c  left outer join orders o on c.id = o.customer_id where (c.name like ‘T%’);

Hibernate创建二个Customer持久化对象,他们的OID分别为15,还创建了3Order对象,他们的OID1,23。值得注意的时,Querylist()方法返回的集合中包含4个元素,每个元素对应的查询结果中的一条记录,每个元素都是对象数组类型,

图:

从上图中可以看出,每个对象数都存放了一对CustomerOrder对象。第一个对象数组引用OID 1Customer对象和OID1Order对象,第二个对象数组引用OID1Customer对象和OID2Order对象,第三个数组引用OID1Customer对象和OID3Order对象,第四个对象数组引用OID5Customer对象和null

可见,前3个对象数组重复引用OID1Customers对象,此外,由于Custmer对象的orders集合采用延迟检索策略,因此他的orders集合没有被初始化。

当程序第一次调用OID1Customer对象的getOrders().iterator()方式时,会初始化Customer对象的orders集合,Hibernate执行的SQL查询语句为:

Select * from orders where customer_id = 1;


以上的查询返回3ORDERS记录,由于和这3条记录对应的Order持久化对象已经存在,因此Hibernate不会在创建这些Order对象,仅让Customer对象的orders集合引用已经存在的Order对象:

图:


如果希望Querylist()方法返回的集合中仅包含Customer对象,可以在HQL查询语句中使用select关键字:

List result = session.createQuery("select c from Customer c left join c.orders o where c.name like 'T%'").list();for (Iterator it = result.iterator();it.hasNext();){Customer customer = (Customer)it.next();  //如果orders集合使用延迟检索策略,以下代码会初始化  Customer对象的orders集合。  Iterator orders = customer.getOrders().iterator();  }

运行Querylist()方法时,Hibernate执行的SQL查询语句为:

Select c.id,c.name,c.age from customers c leftJoin orders o on c.id = o.customer_idWhere c.name like ‘T%’;

上面的查询语句的查询结果如下:

如图:

根据以上的查询结果,Hibernate创建二个Customer持久化对象,他们的OID分别为15.值得注意的时,Querylist()方法返回的集合中包含4Customer类型的元素,

每个元素和查询结果中的一条记录对应:

图:


从图中看出,result集合中前3个元素都引用同一个Customer对象。此外,

由于Customer对象的orders集合采用延迟检索策略,因此orders集合没有被初始化。

当程序第一次调用OID1Customer对象的getOrders().iterator()方法时,会初始化Customer对象的orders集合,Hibernate执行的SQL查询语句为:

select * from orders where customer_id = 1;

以上查询语句返回3ORDERS记录,由于和这3条记录对应的Order持久化对象还不存在,因此Hibernate会创建这些Order对象,并且让Customer对象的orders集合引用这3Order对象:

如图:



:内连接

HQL中,inner join 关键字表示内连接如:

List result = session.createQuery(“from Customer c inner join c.orders o where c.name like ‘T%’”).list();for (Iterator pairs = result.iterator();pairs.hasNext();) {  Object[]pair = (Object[])paris.next();  Customer customer = (Customer)pair[0];  Order order = (Order)pair[1];  //如果orders集合使用延迟检索策略,以下代码会初始化Customer对象的orders集合。  customer.getOrders().iterator();}

假如在Customer.hbm.xml文件中对orders集合设置了延迟检索策略,那么在运行Querylist()方法的时,Hibernate执行的SQL查询语句为:

Select c.id C_ID ,c.name ,c.age,o.id O_ID ,o.order_number ,O.customer_id from customers c inner join orders o on c.id =o.customer_id where (c.name like ‘T%’);

运行结果如下:

图:


根据以上查询结果,Hibernate会创建一个OID1customer持久化对象,还创建了3Order对象,他们的OID分别为123。值得注意的是,Querylist()方法

返回的集合中包含3个元素,每个元素对应查询结果中的一条记录,每个元素都是对象数组类型,

如图:

从图可以看出,每个对象数组都存放了一对CustomerOrder对象。第一个对象数组的应用OID1Customer对象和OID1Order对象,第二个对象数组引用OID1Customer对象和OID2Order对象,第三个对象数组引用OID1Customer对象和OID3Order对象。可见这3个对象数组重复引用OID1Customer对象。此外,由于Customer对象的orders采用延迟检索策略,因此orders集合没有被初始化。

当程序第一次调用OID1Customer对象的getOrders().iterator()方法时,会初始化Customer对象的orders集合,hibernate执行的SQL查询语句为:

Select * from orders where customer_id = 1;

以上的查询语句会返回3orders记录,由于和这3条记录对应的order持久化对象已经存在,因此hibernate不会再创建这些order对象,仅让Customer对象的orders集合引用已经存在的order对象。

如图:


如果希望Querylist()方法返回的哦集合中仅包含Customer对象,可以在HQL查询语句中使用select关键字:

List result = session.creatQuery(“select c from customer c join   C.orders o where c.name like ‘T%’”);For (Iterator it = result.iterator();it.hasNext();) {  Customer customer = (Customer)it.next();  //如果orders集合使用延迟检索策略,以下代码会初始化Customer对象的orders集合  Iterator orders = customer.getOrders().iterator();} 

运行Querylist()方法时,Hibernate执行的SQL查询语句为:

 
Select c.id ,c.name,c.age from customers cinner join orders o on c.id = o.customer_id;Where (c.name like ‘T%’);

图:


根据以上查询结果,Hibernate会创建一个OID1Customer持久化对象。值得注意的是,Querylist()方法返回的集合中包含3Customer类型的元素:

如图:


当上图看出,result集合中3个元素都引用同一个Customer对象。此外,由于对

Customer对象的orders集合采用延迟检索策略,因此orders集合没有被初始化。

当程序第一次调用OID1Customer对象的getOrders().iterator()方法时,会初始化Customer对象的orders集合,Hibernate执行的sql语句为:

Select * from orders where customer_id = 1;

以上查询语句返回3orders记录,由于和这3条记录对应的order持久化对象还不存在,因此Hibernate会创建这些Order对象,并且让Customer对象的orders对象orders集合引用这3Order对象。

图:


:迫切内连接

以下程序覆盖映射文件中指定的检索策略,显示指定对与Cusomter关联的Order 对象采用迫切内连接检索策略:

//HQL检索方式List result = session.createQuery(“from Customer c inner join fetch c.orders o where c.name like ‘T%’”).list();for (Iterator it = result.iterator();it.hasNext();) {  Customer customer = (Customer)it.next();}

以上代码生成的SQL查询语句为:

select c.id C_ID ,c.name ,c.age,o.id O_ID ,o.order_number,o.customer_id from customers c inner join orders o on c.id = o.customer_id where (c.name like ‘T%’);

:


HQL查询语句中,inner join fetch 关键字表示迫切内连接检索策略。使用迫切内连接检索策略时,Querylist()方法返回的集合中存放Customer对象的引用,每个

Customer对象的orders集合都被初始化,存放所有关联的Order对象。根据以上查询的结果,result集合包含3Customer类型的元素,都引用OID1Customer持久化对象,它的orders集合中包含3Order对象。

图:


由此可见,当使用迫切连接检索策略时,查询结果中可能会包含重复元素,可以通过一个HashSet来过滤重复元素:

//HQL检索方式List result = session.createQuery(“from Customer c inner join fetch c.orders o where c.name like ‘T%’”).list();HashSet set = new HashSet(result);for (Iterator it = set.iterator();it.hasNext();) {Customer customer = (Customer)it.next();}


update:

Session session = HibernateSessionFactory.getSession();
session.beginTransaction();
//HQL检索方式
List result = session.createQuery("from Order o inner join fetcho.customer where o.customer.name like 'T%'").list();
/*
for (Iterator it = result.iterator();it.hasNext();) {
Customer customer = (Customer)it.next();
}*/
session.getTransaction().commit();
HibernateSessionFactory.closeSession();
Order order = (Order)result.get(0);
System.out.println(order.getCustomer().getName());

返回的结果为: