Hibernate入门6.Hibernate检索方式 20131128
代码下载 链接: http://pan.baidu.com/s/1Ccuup 密码: vqlv
Hibernate的整体框架已经初步掌握,ORM持久层框架核心的是如何高效的和数据库进行交互以及如何交互。包括数据的增删改查、SQL查询还是HQL查询、查询技巧、离线查询等等。本次主要学习的是Hibernate的检索方式。
Hibernate检索方式
Hibernate提供了多种查询方式进行各种查询。前面已经了解了session的get() load()的方法检索指定id的对象,还有其他的检索方式:
导航对象图检索方式:根据已经加载的对象,利用对象之间的关联关系,导航到其他对象,比如customer.getOrders().iterator()可以导航到一个Order对象
OID检索方式:按照对象的OID来检索对象,可以使用session的load()和get()方法。
HQL方式:使用Hibernate Query Language(HQL)查询语言检索对象,Hibernate中提供了Query接口,该接口是HQL的查询接口,能够执行各种复杂的HQL查询语言。
QBC检索方式:使用Query By Criteria(QBC)API来检索对象,该API提供了更好的面向对象的接口
本地的SQL检索方式:使用数据库的SQL语言,Hibernate负责把检索到的JDBC ResultSet结果集映射为持久化对象图。
其中HQL和QBC是最为常见的,强大的查询方式:
HQL:他是面向对象的查询语言,其操作的是类、对象和属性,所以说HQL是面相对象的查询语言。
语法规则:
[select attribute_name_list]
from ClassName [as ]
[where condition]
[group by ] // select count(o) Order o group by o.customer
[having ]
[order by]
QBC也被称为条件查询,是完全面相对象的查询方式,主要通过下面的类实现
Criteria: 代表依次查询
Criterion:代表一个查询条件
Restrictions: 产生查询条件的工具类
常见的步骤是:获取Session对象,并且使用某个类的Class对象作为参数调用session的createCriteria()方法创建Criteria对象,调用criteria的add方法增减查询条件,执行Criteria的list()获得查询结果集到List接口中。。
HQL和QBC两者各有优点,但是HQL更加强大,类似SQL,QBC是通过特定的API执行的面向对象的查询,在动态查询的时候,QBC比HQL有优势。
下面开始详细的学习HQL和QBC的查询方式。
1.Query和Criteria接口
Query和Criteria接口是Hibernate的查询接口,用于向数据库查询对象,以及控制执行查询过程。Criteria接口完全封装了基于字符串形式的查询语句,比Query接口更加面相对象。
Query接口
HQL方式的查询依赖于Query接口,每一个Query实例都会对应一个查询对象,Query利用session的createQuery(String hql)接口获得一个实例:
int executeQuery();执行更新或者是删除操作,返回值是受影响的记录数
Iterator iterator()返回一个Iterator对象,用于迭代查询的结果集,首先检索ID字段,然后根据ID字段到Hibernate的一级、二级缓存中查找,如果找到则返回,反之执行额外的select语句,根据ID查询数据库,如果在缓存中,这种方式比list()性能高
List list();返回list类型的结果集,如果是投影查询,返回的是 Object[]
Query setFirstResult(int first);设置开始检索的位置,默认是0
Query setMaxResult(int max);设置一次最多检索的对象数目,默认是所有的对象,通常是和setFirstResult配合使用实现分页查询
Object uniqueResult(); 返回单个对象。如果没有查询到结果返回null,通常是和setMaxResult()配合使用,用于返回单个结果
Query setString(String name, String value);绑定映射类型中为string类型的参数
Query setEntity(String name, Object val);把参数和一个持久化对象绑定
Query setParameter(String name, Object value);用于绑定任意形式的参数
Query setProperties(String name,Object value):把命名参数和一个对象的属性值绑定
Criteria接口
Criteria接口表示的一次查询,该查询不具备任何数据筛选的功能。Criteria本身就是一个查询容器,具体的查询条件可以使用add的方法加入到Criteria实例中,开发人员不用编写SQL或者是HQL实现对于数据库的查询,同时QBC编译的时候就进行解析,便于查找错误,他的接口:
Criteria add (Criterion cri);增加查询条件
Criteria addOrder(Order order);增加排序规则,调用Order的asc()或者是desc()
Criteria createCriteria(String path);在相互关联的持久化类之间建立约束条件
List list() 返回list的结果集
Criteria setFirstResult(int first);
Cirteria setMaxResult(int max);
Object uniqueResult();
Criteria setProjection(Projection projectionf);设定统计函数实现分组统计功能,Projection类型的对象代表一个统计函数。
2.使用别名
通过HQL检索一个类的实例的时候,如果在查询语句的时候需要引用该类,一般会给他取一个别名,以便于引用:
from Customer as c where c.realName = ‘yang’//也可以将as关键字省略
form Customer c where c.realName=’yang’
同时在HQL查询语言中的关键字是不区分大小写的,但习惯使用的是小写
QBC检索方式不需要由应用程序显示的指定类的别名,Hibernate会自动把查询语句中的根节点实体赋予this
List result =
session.createCriteria(Customer.class).add(Restrictions.eq(“userName”,”yang”)).list();
等价于
List result =
session.createCriteria(Customer.class).add(Restrictions.eq(“this.userName”,”yang”)).list();
3.结果排序
HQL结果排序 采用的是在HQL中添加order by 关键字实现对于查询结果的排序。
Session session = HibernateUtil.getSession();
String hql = "from Customer as c order by c.userName desc";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
for(Customer customer : list){
System.out.println(customer);
}
QBC排序,采用的是org.hibernate.criterion.Order类实现对于查询结果的排序。Order代表排序类,其中asc()和desc()表示的是静态排序方法
public static void printUserNameByAsc(){
Session session = HibernateUtil.getSession();
Criteria criteria = session.createCriteria(Customer.class);
criteria.addOrder(org.hibernate.criterion.Order.asc("userName"));
List<Customer> list = criteria.list();
for(Customer customer : list){
System.out.println(customer.getUserName());
}
HibernateUtil.closeSession();
}
4.分页查询
HQL使用的是Query接口中的setFirstResult(int first)&& setMaxResult(int max)的方法结合实现
public static void listPageByQuery(int pageNo,int perPageNum){
Session session = HibernateUtil.getSession();
String hql = "form Customer as c where true order by c.id asc";
Query query = session.createQuery(hql);
query.setFirstResult((pageNo-1)*perPageNum);
query.setMaxResults(perPageNum);
List<Customer> list = query.list();
for(Customer customer : list){
System.out.println(customer.getUserName());
}
HibernateUtil.closeSession();
}
public static void listPageByCriteria(int pageNo, int perPageNum){
Session session = HibernateUtil.getSession();
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFirstResult((pageNo-1)*perPageNum);
criteria.setMaxResults(perPageNum);
List<Customer> list = criteria.list();
for(Customer customer : list){
System.out.println(customer.getUserName());
}
HibernateUtil.closeSession();
}
5.检索一条记录
HQL和QBC都会使用setMaxResult(1),然后调用uniqueResult();就会返回一个Object的类型的对象。如果结果只会包含一个对象的话,那么可以不设置setMaxResult(),但是当检索的结果中包含了多个结果的时候,没有调用setMaxResult()的时候就会抛出异常NonUniqueResultException。
6.设定查询条件
在HQL中使用的是where子句设置查询条件的,于SQL不同的是HQL中使用的是对象的属性名字而不是数据库表中的属性名字。对于QBC则使用的是Criterion对象设置查询条件,通常使用Restrictions类创建Criterion对象。
Criteria.add(Restrictions.ge(“age”,20));
7.HQL中绑定参数
在实际查询中,用户输入一些查询条件,要求返回满足查询条件的记录。
有两种方式:一种是按照名称绑定,使用 :paramName 之后再后面使用query.setString(“paramName”,val);绑定参数值。
当然还提供了很多其他的类型,String Integer
另一种是按照参数的位置绑定,使用?来定义参数位置
String hql = “from Customer as c where c.reanName=?”;
query.setString(0, val);这里的位置是在0开始的,而JDBC中的PreparedStatement是在0开始的。
8.连接查询
HQL和SQL一样也是支持连接查询的,此外还有fetch预抓起内连接和fetch左外连接。
内连接 inner join or join
预抓起内连接: inner join fetch or join fetch
左外连接: left outer join or left join
预抓起左外连接: left outer join fetch or left join fetch
右外连接: right outer join or right join
内连接:inner join 默认使用join表示的就是inner join。只要两个持久化类关联字段之间有相符合的值,内连接就会组合两个表中的连接,常见于1-N的关联中
9.投影分组和统计
9.1投影
String hql = “select c.id, c.userName from Customer”;
List<Object[]> list = query.list();
如果是想反返回Customer对象的话,需要声明只有以上参数的构造函数,但是这样的话就会生成大量的Customer持久化对象保存在Session缓冲中。所以当数据量比较大的时候,最好不要使用这一种方式,下面我们介绍使用一种新的方式使用Map
String hql= “selecy new map(c.id, c.userName) from Customer as c”;
List<Map> list = query.list();
for(Map m : list){m.get(“0”); m.get(“1”)};
这一种方式的话,不会占用Session混村,只要程序中失去对他们的引用的时候,就会被JVM回收掉。所以当初局量比较大的时候,推荐是的实例化而不是持久化对象。
9.2HQL分组统计
count min max sum avg 函数都是可以在HQL中使用的函数
String hql = “slect count(c.id) from Customer as c ”;
Long count = (Long)session.createQuery(hql).uniqueResult();
String hql =”select max(c.avg), min (c.age) from Customer as c”
Object[] objs = (Object[]) session.createQuery(hql).uniqueResult();
Integer maxAge = (Integer)objs[0];
Integer maxAge = (Integer) objs[1];
分组查询
HQL中也会使用group by关键字进行分组查询,也可以使用having关键字对数据设定约束条件。
10.动态查询
Hibernate中查询数据的方式有两种:静态查询(在编写程序的时候就已经确定好需要查询的字段)和动态查询(在编写程序的时候不能够确定要查询的字段),所以一般情况下,HQL是和静态查询,而QBC适合动态查询。
HQL需要根据参数,动态的修改HQL语句,从而实现动态查询。
QBC如下:
public static List<Customer> findCustomersByCriteria(String name, Integer age){
Session session = HibernateUtil.getSession();
Criteria criteria = session.createCriteria(Customer.class);
if(name!= null){
criteria.add(Restrictions.ilike("userName", name));
}
if(age != null && age != 0){
criteria.add(Restrictions.eq("age", age));
}
return criteria.list();
}
11.子查询
form Customer as c where c.age = (select c1.age from Customer as c1 where c1.userName = :userName) and c.userName != :username
from Customer as c where 100>all(select o.total from c.orders o);
12.查询方式的比较
Hibernate最常使用的就HQL和QBC两种:
HQL和SQL比较接近,功能强大,支持各种查询,但是必须是提供局域字符串的查询,动态查询比较麻烦
QBC封装了个统一的API,编译期会检查,适合动态查询,但是可读性比较差
最后的一点内容就是关于Hibernate的事务管理机制:
Hibernate是JDBC轻量级的封装,本身是没有事务管理能力的,在事务管理层,Hibernate将其委托给底层的JDBC或者是JTA实现事务管理和调度。同时需要掌握好数据库的事务管理机制,才可以开发更加高效的并发Hibernate应用。
事务具有ACID四大性质,这些是由数据库管理系统实现的,对于事务的Isolation机制,数据库管理系统使用的是锁机制。实际开发中,事务的隔离性不完全,就会导致各种并发的问题:
更新丢失:两个事务同时更新一个数据的话,由于一个事务的撤销导致另一个事务对数据的修改液失效了
脏读:一个事务读取到另一个事务还没有提交但是已经更改过的数据,这种状态下,数据是不一致的
不可重复读:当一个事务读取了某些数据的时候,另一个事务修改了这个数据并且提交。当该事务再次读取这些数据的时候,数据已经被改变了;
幻读:同一个查询操作在一个事务中执行多次,但是因为其他的事物插入操作,这样导致每一次读取的数据都是不同的,称之为幻读。
所以为了避免这些并发问题的出现,保证数据的完整性和一致性,必须实现事务的隔离性。在事务执行的时候,是他们好像是系统在给定的时间内执行的唯一操作,如果这一个时刻会有两个事务同时执行,执行的功能相关,事务的隔离性会保证每一个事务在系统中认为只有该事务使用这个系统。
事务隔离性的标准:
序列化级别(Serializable)所有的事务都是相互隔离的,就不存在并发的情况了,隔离级别最高
可重复读(Repeatbale read):所有的被select获取的数据是不可以被修改的。
读已提交(Read committed):读取数据的事务允许其他事务访问器正在读取的数据,但是未提交的写事务将会禁止其他事务访问正在写的数据。
读未提交(Read uncommitted):如果一个事务已经开始写数据的话,则不允许其他的事务同时进行写操作,但是允许其他的事务读取器正在写的数据,这是事务隔离的最低级别。
在hibernate中配置属性 hibernate.connection.isolation = 4 也就是可重复读,默认是读已提交2.
Transaction trans = session.beginTransaction();
try{
session.save(customer);
}catch(Exception e){
Trans.rollback();
}
trans.commit();
HibernateUtil.closeSession();
YangTengfei
2013.11.29