《精通Hibernate》学习(7)——Hibernate的检索方式(上)

一、Hibernate检索方式简介

 

Hibernate提供以下几种检索对象的方式:

  • 导航对象图检索方式。(根据已经加载的对象,导航到其他对象。)
  • OID检索方式。(按照对象的OID来检索对象。)

  • HQL检索方式。(使用面向对象的HQL查询语言。)

  • QBC检索方式。(使用QBC(Query By Criteria)API来检索对象)

  • 本地SQL检索方式。(使用本地数据库的SQL查询语句。)

 1、HQL检索方式

HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相识。在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。它具有以下功能:

l         在查询语句中设定各种查询条件。

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

l         支持分页查询。

l         支持连接查询。

l         支持分组查询,允许使用having和group by关键字。

l         提供内置聚集函数,如sum()、min()和mac()。

l         能够调用用户定义的SQL函数。

l         支持子查询,即嵌入式查询。

l         支持动态绑定参数。

 

示例代码:

Query query = session.createQuery("from Customer as c where c.name=:customerName and c.age=:customerAge"); 
// 动态绑定参数
query.setString("customerName", "Test");
query.setInteger("customerAge", 21);
// 执行检索
List result = query.list();

 

由上述示例代码中可以看出,HQL的检索方式包括以下步骤:


Query接口支持方法链接编程风格,如下:

// 方法链编程风格
List result1 = session.createQuery("from Customer as c where c.name=:customerName  and c.age=:customerAge").setString("customerName","Test").setInteger("customerAge", 21).list();


尽管HQL与SQL的语法形式上形似,但是他们并非神似:

  • HQL查询语句是面向对象的,Hibernate根据配置文件负责解析成SQL语句。
  • SQL查询语句直接与关系数据库打交道。

2、QBC检索方式

采用HQL检索方式时,在应用程序中需要定义基于字符串形式的HQL查询语句。QBC API提供了检索对象的另一种方式,它主要由Criteria接口、criterion接口和Restrictions类组成,它支持在运行时动态生成查询语句。

示例代码:

//创建一个Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
//设定查询条件,然后把查询条件加入Criteria中
Criterion criterion1 = Restrictions.like("namr", "T%");
Criterion criterion2 = Restrictions.eq("age", new Integer(21));
criteria = criteria.add(criterion1);
criteria = criteria.add(criterion2); 
// 执行检索
List result = criteria.list(); 
// 方法链编程风格
List result1 = session.createCriteria(Customer.class).add(Restrictions.like("namr", "T%")).add(Restrictions.eq("age", new Integer(21))).list();

由代码可以看出,QBC检索方式的基本步骤:

 

Hibernate还提供了QBE(Qurey By Example)检索方式,它是QBC的子功能。QBE允许先创建一个随想模板,然后检索出和这个样板相同的对象。
示例:(检索年龄为21的Customer对象)

Customer exampleCustomer = new Customer();
exampleCustomer.setAge(21);
List result1 = session.createCriteria(Customer.class).add(Example.create(exampleCustomer)).list();

 

QBE的功能不是特别强大,仅在某些场合下有用。一个典型的使用场合就是在查询窗口中让用户输入一系列的查询条件,然后返回匹配的对象。QBE只支持“=”和“like”比较运算符,无法支持区间值,及其“或”匹配。在这种情况下,还是采用HQL检索方式或QBC检索方式。

3、本地SQL检索方式

采用HQL或QBC检索方式时,Hibernate生成标准的SQL查询语句,使用于所有的数据库平台,因此这两种检索方式都是跨平台的

有的应用程序可能需要根据底层数据库的SQL方言,来生成一些特殊的查询语句。在这种情况下,可以利用Hibernate提供的SQL检索方式。

 示例代码:

Query query = session.createSQLQuery("select * from CUSTOMER as c where c.NAME like :customerName and c.AGE=:customerAge"); 
// 动态绑定参数
query.setString("customerName", "Test");
query.setInteger("customerAge", 21); 
// 执行检索
List result = query.list();


从以上程序可以看出,本地的SQL检索方式与HQL检索方式都使用Query接口,区别在于本地SQL检索方式通过Session的createSQLQuery()方法来创建SQLQuery对象。

4、对查询结果进行排序

HQL和QBC都支持对查询结果进行排序。

//HQL检索方式
Query query = session.createQuery("from Customer as c order by c.name asc,c.age desc");//姓名升序、年龄降序
//QBC检索方式
Criteria criteria=session.createCriteria(Customer.class);
criteria.addOrder(Order.asc("name")); 
criteria.addOrder(Order.desc("age"));

5、分页查询

当批量查询的结果过多导致在单个页面上无法显示时,此时就需要对查询结果进行分页。介入CUSTOMER表中有99条记录,可以在用户终端上分10页来显示结果,每一页最多只显示10个Customer对象,用户即可以导航到下一页,也可以导航到上一页。

Query和Criteria接口都提供了用于分页显式查询结果的方法。

l         setFirstResult(int firstResult):设置从哪一个对象开始检索,参数firstResult表示这个对象在查询结果中的索引位置,索引位置的起始值为0。默认从0检索。

l         setMaxResult(int maxResults):设置一次最多检索出的对象数目。默认检索所有。

以下代码从查询结果的起始对象开始,共检索出10个Customer对象,查询结果对name属性排序:

//采用HQL检索方式
Query query = session.createQuery("from Customer c order by c.name asc");
query.setFirstResult(0);
query.setMaxResults(10);
List result=query.list();

//采用QBC检索方式
Criteria criteria=session.createCriteria(Customer.class);
criteria.addOrder(Order.asc("name"));
criteria.setFirstResult(0);
criteria.setMaxResults(10);
List result = criteria.list();

也可以采用链编程风格。

6、检索单个对象

Query和Criteria接口都提供了以下用于查询语句并返回查询结果的方法。

l         list()方法:返回一个List类型的查询结果。

l         uniqueResult()方法:返回单个对象。

注:Query接口还提供了一个iterate()方法,它和list()方法一样,能返回所有满足条件的持久化对象,但是两者使用不同的SQL查询语句。

示例代码:

// HQL单个检索
Customer customer = (Customer) session.createQuery("from Customer as c order by c.name").setMaxResults(1).uniqueResult();

// QBC单个检索
Customer customer = (Customer) session.createCriteria(Customer.class).setMaxResults(1).uniqueResult();

如果明明知道一个对象,可以不调用setMaxResults(1)方法。

7、按主键逐个处理查询结果(iterate()方法)

Query接口还提供一个iterate()方法,它和list()方法一样,也能执行查询操作。iterate()和list()的区别在于两者使用的查询机制不一样。

对于以下代码:

List c1=session.createQuery("from Customer c where c.age>10").list;
Iterator c2=session.createQuery("from Customer c where c.age>10").iterate();

第一行程序的list()执行的语句为:

select ID,NAME,AGE from CUSTOMERS where AGE>10;

第二行程序的iterate()执行的语句为:

select ID from CUSTOMERS where AGE>10;

使用方法:

Customer c1=(Customer)session.get(Customer.class,new Long(1));
Iterator c=session.createQuery("from Customer c where c.age>10").iterate();
while(c.hasNext()){
Customer customer=(Customer)customers.next();
System.out.println(customer.getName);}


大多数情况下会考虑使用Query的list()来执行查询,iterate()对查询性能的提高并不是很好。

8、可滚动的结果集

JDBC API提供了一种可滚动的结果集,它是利用DB system里的游标来实现的。游标用于定位查询结果中的记录,应用程序可以通过任意游标来定位到特定记录。
Query接口以及Criteria接口的scroll()方法返回一个org.hibernate.ScrollableResults对象,它代表可滚动的结果集。ScrollableResults接口包含以下用于移动游标的方法:

  • first()使游标移动到第一行
  • last()使游标移动到最后一行
  • beforeFirst()使游标移动到结果集的开头(第一行之前)
  • afterLast()使游标移动到结果集的末尾(最后一行之后)
  • previous()使游标从当前位置向上(或者说向前)移动一行
  • next()使游标从当前位置向下(或者说向后)移动一行
  • scroll(int n)使游标从当前位置移动n行(n>0,向下移动;n<0,向上移动)
  • setRowNumber(int n)使游标移动到行号为n的行。编号是从0开始的,n=-1时表示移动到最后一行
    以上方法除了beforeFirst()和afterLast()返回void类型,其他都返回Boolean类型。

假设有这样一个表:

行号IDNAMEAGE
01TOM21
12MIKE24
23JACK23
34LINA52
45JEMMERY32

代码演示:

ScrollableResults rs=session.createQuery("from Customer c").scroll;
//游标移动到结果集的第一行
rs.first();

Object[] o=rs.get();
Customer customer=(Customer)o[0];//获取对象数组的第一个对象
System.out.println(customer.getId());

rs.scroll(2);//游标从当前的位置移动2行
Customer customer=(Customer)rs.get(0);//获取当前行中的第一个字段,为Customer对象
System.out.println(customer.getId());
rs.close();

以下程序代码演示了ScrollableResults接口在分页处理数据时的用法:

	final int PAGE_SIZE=3;//每页处理三个Customer对象
	List firstNameOfPages=new ArrayList();//存储所有页中的第一个客户的姓名
	List pageOfCustomers=new ArrayList();//存放第一页的所有Customer对象
	
	ScrollableResults rs=session.createQuery("select c.name ,c from Customer c").scroll();
	if(rs.first()){
		//获取所有页中的第一个客户的姓名
		do{
			String name=rs.getString(0);//获取当前行的第一个字段,为name字段
			firstNameOfPages.add(name);
		}while(rs.scroll(PAGE_SIZE));
		
		//获取第一页中的所有Customer对象
		rs.beforeFirst();
		int i=0;
                while((PAGE_SIZE>i++)&&rs.next()){
                                                   pageOfCustomers.add(rs.get(1));//获取当前行的第二个字段,为Customer对象
                                                 }
                rs.close();
                for(int i=0;i<firstNameOfPages.size();i++){System.out.println(firstNameOfPages.get(i));}
                for(int i=0;i<pageOfCustomers.size();i++){}System.out.println(pageOfCustomers.get(i));}

9、在HQL查询语句中绑定参数

在实际的应用中,经常会有用户在查询窗口中输入一些查询条件,要求返回满足条件的记录。如下:

public List findCustomers(String name,int age){
Session session=getSession();
Query query=session.createQuery("from Customer as c where c.name='"+name+"'and c.age='"+age+"'");}

虽然以上的代码可以实现,但是不安全。为什么呢?假如有一个恶意的用户在姓名的输入框中输入以下的内容:

Tom' and SomeStoredProcedure() and 'hello' = 'hello

那么实际的HQL查询语句即为:

from Customer as c where c.name='Tom' and SomeStoredProcedure() and 'hello' = 'hello' and c.age=20

以上的查询语句不仅会执行查询数据库,而且还会执行一个名为“SomeStoredProcedure”的存储过程。Hibernate采取绑定参数的方式来解决这个问题。

参数绑定机制有以下优点:

  • 安全
  • 能够利用底层数据库预编译SQL语句的功能,提高查询数据的性能。

1、参数绑定的形式

(1)按参数名字绑定

Query query=session.createQuery("from Customer as c where c.name:=customerName and c.age:=customerAge);

命名参数以“:”开头。

接下来调用Query的setXXX()方法来绑定参数:

query.setString("customerName",name);
query.setInteger("customerAge",age);

这些setXXX()方法第一个参数为命名参数的名字,第二个参数为命名参数的值。

假如有一个恶意的用户在姓名的输入框中输入以下的内容:

Tom' and SomeStoredProcedure() and 'hello' = 'hello

Hibernate就会解析成:

 from Customer as c where c.name='Tom'' and SomeStoredProcedure() and ''hello' = 'hello' and c.age=20


(2)按参数位置绑定

在HQL查询语句中用“?”来定义参数的位置,形式如下:

Query query=session.createQuery("from Customer as c where c.name=? and c.age=?);

按位置绑定:

query.setString(0,name);
query.setInteger(1,age);

 

2、绑定各种类型的参数

Query接口提供了绑定各种Hibernate映射类型的参数方法,如下:

  • setBinary()绑定映射类型为binary的参数
  • setBoolean(),setByte(),setCalender(),setCharacter(),setDate(),setDouble(),setString(),setText(),setTime(),setTimestamp()

Hibenate还提供三种特殊的参数绑定方法:

  • setEntity():把参数与一个持久化类的实例绑定
  • setParameter():绑定任意类型的参数
  • setProperties():用于把命名参数与一个对下岗的属性值绑定

10、设置查询附属事项 

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

  • setFlushMode()方法:设置清理缓存的模式
  • setCacheMode()方法:设置Session与二级缓存的交互模式
  • setTimeout()方法:设置执行查询数据库操作的超时时间
  • setFetchSize()方法:为JDBC驱动程序设置批量抓取的数目
  • setLockMode()方法:设定锁定模式
  • setCommet()方法:为SQL日志设置注解

11、在HQL查询语句中调用函数 

HQL中可以调用SQL函数。而且还可以调用专门的HQL函数。


 

二、设定查询条件

 

Hibernate中的query 博客分类: • Hibernate HibernateSQLSQL ServerMySQLJDBC Hibernate中的query.setFirstResult(),query.setMaxResults(); 一、query.scroll()和query.setFirstResult(),query.setMaxResults();这两种方法都可以取到一定范围内的数据,用来数据分页显示。那么两者区别,以及两者的效率如何? 答:1.scroll是用JDBC2.0的可滚动结果集实现;query.setMaxResults();query.setFirstResult()是数据库SQL语句实现。 2.你说是在数据库就分页好呢?还是把结果集都取到内存再分页好呢?(应该是在数据库就分了好些吧,但是如果在内存分页的话,换页的时候是不是更快一些呢?) 3.在数据库进行分页是首选的方式数据库分页实际上是利用数据库本身SQL扩展的功能进行分页,例如MySQL的 limit 0,50这样的SQL语句。不但速度快,而且非常节省内存。不过不是每种数据库的都有这种分页支持的SQL,例如SQL Server就不支持。 4.scroll是利用JDBC2.0的功能做分页的,那么就完全取决于特定数据库的JDBC Driver的实现了。事实上大部分JDBC Driver都是把所有的结果集都一次取到内存,然后再分页的。如果这个结果集非常大,例如几万条,不但程序执行速度会很慢,而且很容易导致out of memory。当然个别JDBC Driver使用了服务器端游标来实现,那么就不会导致这种问题,例如jTDS。 二、Hibernate可以使用Query.setMaxResults方法简单地设置需要查询的最大结果集。 然后Hibernate会自动根据所设置的数据库方言翻译成相应的SQL语句提交给数据库。比如如果数据库是Oracle,SQL Server等,则翻译为类似select ... top 10之类的SQL语句,若是MySQL,则翻译为select ... limit 10之类的SQL。 三、举例: Java代码 1. import org.hibernate.Query; 2. 3. query.setFirstResult(0),query.setMaxResults(4);相当于MySQL中的limit 0, 4; 4. public void testQuery() { 5. Session session = null; 6. try { 7. session = HibernateUtils.getSession(); 8. session.beginTransaction(); 9. Query query = session.createQuery("from User"); 10. query.setFirstResult(0);//从第一条记录开始 11. query.setMaxResults(4);//取出四条记录 12. List userList = query.list(); 13. for (Iterator iter=userList.iterator(); iter.hasNext();) { 14. User user = (User)iter.next(); 15. System.out.println(user.getId()); 16. System.out.println(user.getName()); 17. } 18. session.getTransaction().commit(); 19. }catch(Exception e) { 20. e.printStackTrace(); 21. session.getTransaction().rollback(); 22. }finally { 23. HibernateUtils.closeSession(session); 24. } 25. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值