Hibernate检索方式的分类
Hibernate的检索方式主要有5种,分别为导航对象图检索方式、OID检索方式、HQL检索方式、QBC检索方式和SQL检索方式。下面对这5中检索方式的使用进行详解。
1. 对象图导航检索
对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。譬如要查找一个联系人对应的客户,就可以由联系人对象自动导航找到联系人所属的客户对象。当然,前提是必须在对象关系映射文件上配置了多对一的关系。其检索方式如下所示:
LinkMan linkMan = session.get(LinkMan.class, 1l);
Customer customer = linkMan.getCustomer();
2. OID 检索方式
OID检索方式主要指用Session的get()和load()方法加载某条记录对应的对象。如下面两种加载客户对象的方式,就是OID检索方式,具体如下:
Customer customer = session.get(Customer.class, 1l);
Customer customer1 = session.load(Customer.class, 1l);
3. HQL检索
HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似,但它使用的是类、对象和属性的概念,而没有表和字段的概念。在Hibernate提供的各种检索方式中,HQL是官方推荐的查询语言,也是使用最广泛的一种检索方式,它具有如下功能。
- 在查询语句中设定各种查询条件;
- 支持投影查询,即仅检索出对象的部分属性;
- 支持分页查询;
- 支持分组查询,允许使用group by 和 having关键字;
- 提供内置聚集函数,如sum()、min()和max();
- 能够调用用户定义的SQL函数;
- 支持子查询,即嵌套查询;
- 支持动态绑定参数;
Hibernate提供的Query接口是专门的HQL查询接口,它能够执行各种复杂的HQL查询语句,完整的HQL语句结构如下:
select...from...where...group by...having...order by ... asc/desc
可以HQL查询非常类似于表中SQL查询。通常情况下,当检索数据表中的所有记录时,查询语句中可以省略select关键字,示例如下所示:
String hql = "from Customer";
如果执行该查询语句,则会返回应用程序中的所有Customer对象。
需要注意的是:Customer是类名,而不是表名,类名需要区分大小写,而关键字from不区分大小写。
3.1 基本检索
1 @Test 2 public void test1() { 3 // HQL基本检索 4 Transaction tx = session.beginTransaction(); 5 6 // 基本查询 7 Query query = session.createQuery("from Customer"); 8 List<Customer> custList = query.list(); 9 for(Customer c : custList) { 10 System.out.println(c); 11 } 12 tx.commit(); 13 }
3.2 条件检索
1 @Test 2 public void test2() { 3 // 条件查询 4 Transaction tx = session.beginTransaction(); 5 Query query = session.createQuery("from Customer order by cust_id desc"); 6 List<Customer> custList = query.list(); 7 for(Customer c : custList) { 8 System.out.println(c); 9 } 10 tx.commit(); 11 }
1 @Test 2 public void test3() { 3 // 按位置绑定参数 4 Transaction tx = session.beginTransaction(); 5 Query query = session.createQuery("from Customer where cust_id=?"); 6 // query.setInteger(0, 1); 7 query.setParameter(0, 1l); 8 List<Customer> list = query.list(); 9 for(Customer c: list) { 10 System.out.println(c); 11 } 12 tx.commit(); 13 }
1 @Test 2 public void test4() { 3 // 按名称绑定参数 4 Transaction tx = session.beginTransaction(); 5 Query query = session.createQuery("from Customer where cust_id=:name"); 6 // query.setInteger(0, 1); 7 query.setParameter("name", 1l); 8 List<Customer> list = query.list(); 9 for(Customer c: list) { 10 System.out.println(c); 11 } 12 tx.commit(); 13 }
1 @Test 2 public void test5() { 3 // 分页查询 4 Transaction tx = session.beginTransaction(); 5 Query query = session.createQuery("from Customer"); 6 query.setFirstResult(0); 7 query.setMaxResults(1); 8 List<Customer> cusList = query.list(); 9 for(Customer c: cusList) { 10 System.out.println(c); 11 } 12 tx.commit(); 13 }
1 @Test 2 public void test6() { 3 // 统计检索 4 Transaction tx = session.beginTransaction(); 5 Query query = session.createQuery("select count(*) from Customer"); 6 Long num = (Long) query.uniqueResult(); 7 System.out.println(num); 8 tx.commit(); 9 }
1 @Test 2 public void test7() { 3 // 投影查询 4 Transaction tx = session.beginTransaction(); 5 //投影查询一列 6 Query query = session.createQuery("select cust_name from Customer"); 7 List<String> names = query.list(); 8 for(String s : names) { 9 System.out.println(s); 10 } 11 //投影查询多列 12 Query query1 = session.createQuery("select cust_id, cust_name from Customer"); 13 List<Object[]> cs = query1.list(); 14 for(Object[] o : cs) { 15 System.out.println(Arrays.toString(o)); 16 } 17 tx.commit(); 18 }
4. QBC检索
QBC(Query By Criteria)是Hibernate提供的另一种检索对象的方式,它主要有Criteria接口、Criterion接口和Expression类组成。Criteria接口是HibernateAPI中的一个查询接口,它需要由session进行创建。
Criterion是Criteria的查询条件,在Criteria中提供了add(Criterion criterion)方法添加查询条件。使用QBC检索对象的示例代码,如下所示:
1 @Test 2 public void test1() { 3 Transaction tx = session.beginTransaction(); 4 criteria = session.createCriteria(Customer.class); 5 // 设定查询条件 6 criteria.add(Restrictions.eq("cust_id", 1l)); 7 // 获取查询结果 8 List<Customer> list = criteria.list(); 9 System.out.println(list); 10 tx.commit(); 11 }
QBC检索是使用Restrictions对象编写查询条件的,在Restrictions类中提供了大量的静态方法来创建查询条件。其常用的方法如表所示:
方法名 | 说明 |
Restrictions.eq | 等于 |
Restrictions.allEq | 使用Map,使用key/value进行多个等于的比较 |
Restrictions.gt | 大于> |
Restrictions.ge | 大于等于>= |
Restrictions.lt | 小于 |
Restrictions.le | 小于等于<= |
Restrictions.between | 对应SQL的between子句 |
Restrictions.like | 对应SQL的like子句 |
Restrictions.in | 对应SQL的IN子句 |
Restrictions.and | and关系 |
Restrictions.or | or关系 |
Restrictions.sqlRestriction | SQL限定查询 |
4.1 基本检索
1 @Test 2 public void test2() { 3 Transaction tx = session.beginTransaction(); 4 Criteria criteria = session.createCriteria(Customer.class); 5 // SimpleExpression restrictions = Restrictions.eq("cust_id", 1l); 6 // criteria.add(restrictions); 7 List<Customer> list = criteria.list(); 8 for(Customer c : list) { 9 System.out.println(c); 10 } 11 tx.commit(); 12 }
4.2 条件检索
1 @Test 2 public void test3() { 3 // 条件检索 4 Transaction tx = session.beginTransaction(); 5 Criteria criteria = session.createCriteria(Customer.class); 6 // criteria.add(Restrictions.eq("cust_name", "test1")); 7 // criteria.add(Restrictions.like("cust_name", "%te%")); 8 criteria.add(Restrictions.gt("cust_id", 1l)); 9 List<Customer> custList = criteria.list(); 10 for(Customer c : custList) { 11 System.out.println(c); 12 } 13 tx.commit(); 14 }
4.3 分页检索
1 @Test 2 public void test4() { 3 // 分页检索 4 Transaction tx = session.beginTransaction(); 5 Criteria criteria = session.createCriteria(Customer.class); 6 criteria.setFirstResult(0); 7 criteria.setMaxResults(2); 8 List<Customer> custList = criteria.list(); 9 for(Customer c : custList) { 10 System.out.println(c); 11 } 12 tx.commit(); 13 }
4.4 排序检索
1 @Test 2 public void test5() { 3 // 排序检索 4 Transaction tx = session.beginTransaction(); 5 Criteria criteria = session.createCriteria(Customer.class); 6 criteria.addOrder(Order.desc("cust_id")); 7 List<Customer> custList = criteria.list(); 8 for(Customer c : custList) { 9 System.out.println(c); 10 } 11 tx.commit(); 12 }
4.5 统计检索
1 @Test 2 public void test6() { 3 // 统计检索 4 Transaction tx = session.beginTransaction(); 5 Criteria criteria = session.createCriteria(Customer.class); 6 criteria.setProjection(Projections.rowCount()); 7 Long count = (Long) criteria.uniqueResult(); 8 System.out.println(count); 9 tx.commit(); 10 }
4.6 离线条件检索
DetachedCriteria翻译为离线条件查询,因为它是可以脱离Session来使用的一种条件查询对象,我们都知道Criteria对象必须由Session对象来创建。那么也就是说必须先有Session才可以生成Criteria对象。而DetachedCriteria对象可以在其他层对条件进行封装。
这个对象也是比较有用的,尤其在SSH整合以后这个对象经常会使用。它的主要优点是做一些特别复杂的条件查询的时候,往往会在WEB层向业务层传递很多的参数,业务层又会将这些参数传递给DAO层。最后在DAO中拼接SQL完成查询。有了离线条件查询对象后,那么这些工作都可以不用关心了,我们可以在WEB层将数据封装好,传递到业务层,再由业务层传递给DAO完成查询。
我们可以先简单的测试一下离线条件查询对象,然后具体的使用我们会在后期整合中使用,到那时会更加体会出它的优势。
1 @Test 2 public void test7() { 3 // 离线条件查询:DetachedCriteria(SSH整合经常使用) 4 // 可以脱离Session设置参数 5 6 // 获得一个离线条件查询对象 7 DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class); 8 detachedCriteria.add(Restrictions.eq("cust_name", "test1")); 9 Transaction tx = session.beginTransaction(); 10 List<Customer> list = detachedCriteria.getExecutableCriteria(session).list(); 11 for(Customer c : list) { 12 System.out.println(c); 13 } 14 tx.commit(); 15 }
5. 本地SQL检索方式
采用HQL或QBC检索方式时,Hibernate生成标准的SQL查询语句,适用于所有的数据库平台,因此这两种检索方式都是跨平台的。但有的应用程序可能需要根据底层数据库的SQL方言,来生成一些特殊的查询语句。在这种情况下,可以利用Hibernate提供的SQL检索方式。使用SQL检索方式检索对象的示例代码,如下所示:
SQLQuery sqlQuery = session.createSQLQuery("select id,name,age,city from customer");
在这,SQL的检索方式我们不做太多的介绍。之前无论我们使用的是HQL或是QBC或是SQL其实都是单表的查询,而实际的开发中我们往往需要多个表的联合查询才可以获得我们想要的数据。
6. Hibernate的多表查询
在做多表查询之前,我们需要先来回顾下使用SQL是如何完成多表的查询的。在学习SQL语句的时候进行多表联合查询一般都会采用连接查询,那么我们就来回顾一下SQL中的多表的联合查询。
6.1 连接查询
- 交叉连接
交叉连接返回的结果是被连接的两个表中所有数据行的笛卡尔积,也就是返回第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数,例如department表中有四个部门,employee表中有四个员工,那么交叉连接的结果就是4*4=16条数据。
交叉连接的语法格式如下:
select * from 表1 CROSS JOIN 表2;
也可以写为如下格式:
SELECT * from 表1, 表2;
从上述描述情况可以看出,交叉连接的结果就是两个表中所有数据的组合。需要注意的是,在实际开发中这种业务需求很少见的,一般不会使用交叉连接,而是使用具体的条件对数据进行有目的的查询。
- 内连接
内连接(INNER JOIN)又称简单连接或自然连接,是一种常见的连接查询。内连接使用比较运算符对两个表中的数据进行比较,并列出与连接条件匹配的数据行,组合成新的记录,也就是说在内连接查询中,只有满足条件的记录才能出现在查询结果中。内连接查询的语法格式如下所示:
select 查询字段 from 表1 [INNER] JOIN 表2 on 表1.关系字段=表2.关系字段
在上述语法格式中,INNER JOIN用于连接两个表,ON来指定连接条件,其中INNER可以省略。内连接其实还可以细分为如下两类:
-
- 隐式内连接:顾名思义,隐式的就是我们看不到inner join关键字,而是用where关键字替代。
select * from 表1, 表2 where 表1.关系字段 = 表2.关系字段;
-
- 显式内连接:显示的就是在语句中明显的调用了inner join的关键字。
select * from 表1 inner join 表2 on 表1.关系字段 = 表2.关系字段;
select * from 表1 join 表2 on 表1.关系字段 = 表2.关系字段;
- 外连接
前面讲解的内连接查询中,返回的结果只包含符合查询条件和连接条件的数据,然而有时还需要包含没有关联的数据,即返回查询结果中不仅包含符合条件的数据,而且还包括左表(左连接或左外连接)、右表(右连接或右外连接)或两个表(全外连接)中的所有数据,此时就需要使用外连接查询,外连接分为左连接和右连接,外连接的语法格式如下:
select 所查字段 from 表1 left | rigth [outer] JOIN 表2 on 表1.关系字段 = 表2.关系字段 where 条件
外连接的语法格式和内连接类似,只不过使用的是LEFT JOIN、RIGHT JOIN关键字,其中关键字左边的表被称为左表,关键字右边的表被称为右表。
在使用左连接和右连接查询时,查询结果是不一致的,具体如下:
6.2 LEFT JOIN(左连接): 返回包括左表中的所有记录和右表中符合连接条件的记录。
select * from 表1 left outer join 表2 on 表1.关系字段=表2.关系字段;
select * from A left join 表2 on 表1.关系字段 = 表2.关系字段;
6.3 RIGHT JOIN(右连接):返回包括右表中的所有记录和左表中符合连接条件的记录。
select * from 表1 right outer join 表2 on 表1.关系字段 = 表2.关系字段;
select * from A right join 表2 on 表1.关系字段 = 表2.关系字段;
SQL语句的连接查询我们会写了,那么Hibernate中的HQL如何进行连接查询呢?
6.2 HQL连接查询
Hibernate进行多表查询与SQL其实是很相似的,但是HQL会在原来SQL分类的基础上又多出来一些操作。
HQL的多表连接查询的分类如下:
- 交叉连接
- 内连接
- 显式内连接
- 隐式内连接
- 迫切内连接
- 外连接
- 左外连接
- 迫切左外连接
- 右外连接
其实,这些连接查询语法大致都是一致的,就是HQL查询的是对象而SQL查询的是表:
SQL连接查询:select * from cst_customer c inner join cst_linkman l on c.cust_id = l.lkm_cust_id;
HQL连接查询:from Customer c inner join c.linkmans
Hibernate懒加载策略
在Hibernate中,<many-to-one.../>中的lazy默认为proxy。这样hibernate在数据库中查询数据时事不会把关联的对象查出来的,而是保存一个获得该值得方法:getXxxx()。当我们需要使用这个值的时候,也就是使用getXxx()方法来调用的时候,Hibernate就会利用这个方法从数据库中获取相应的数据。但是很不幸,我们的session早就关闭了。
这是因为Hibernate的懒加载策略,在Hibernate中是使用sessionFactory来管理session,我们每进行一次数据库操作时都会新建一个session对象,当我们操作完成后,hibernate就会在dao层立即关闭该session。这样做就可以严格控制session,避免出现低级错误。
下面给出三种解决办法,个人认为第二种比较好
1、把lazy设成false。这个是最简单的办法,个人认为也是比较笨的方法。因为这是在用效率作为代价。
2、使用OpenSessionInViewFilter。这种方法是将session交给servlet filter来管理,每当一个请求来之后就会开启一个session,只有当响应结束后才会关闭。如下:
1 <filter-name>hibernateFilter</filter-name> 2 <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> 3 </filter 4 <filter-mapping> 5 <filter-name>hibernateFilter</filter-name> 6 <url-pattern>/*</url-pattern> 7 </filter-mapping>
上面的配置文件时在web.xml中配置的。
3、将hibernate的抓起策略改为join。也就是是left join fetch或inner join fetch语法。就是在<many-to-one../>中配置lazy="false" fetch="join"即可。如:
1 <many-to-one name="worker" lazy="false" fetch="join" class="com.paixie.domain.Worker"> 2 3 <column name="positionId"></column> 4 5 </many-to-one>