目录
一、Hibernate的检索方式:
在实际开发项目时,对数据进行最多的操作就是查询,数据的查询在所有ORM框架中都占有极其重要的地位。那么,如何利用Hibernate查询数据呢?我们接下来就来学习Hibernate的检索方式。
Hibernate检索方式的分类:
Hibernate的检索方式主要有5种,分别为导航对象图检索方式、OID检索方式、HQL检索方式、QBC检索方式和SQL检索方式。下面就对这5种检索方式的使用进行讲解。
对象图导航检索
对象图导航检索方式是根据己经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。譬如要查找一个联系人对应的客户,就可以由联系人对象自动导航找到联系人所属的客户对象。当然,前提是必须在对象关系映射文件上配置了多对一的关系。其检索方式如下所示。
1 2 |
LinkMan linkMan = session.get(LinkMan.class, 1l); Customer customer = linkMan.getCustomer(); |
OID检索方式
OID检索方式主要指用Session的get()和load()方法加载某条记录对应的对象。如下面两种加载客户对象的方式,就是OID检索方式,具体如下。
1 2 |
Customer customer = session.get(Customer.class, 1); Customer customer = session.load(Customer.class, 1); |
HQL检索
HQL (Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似,但它使用的是类、对象和属性的概念,而没有表和字段的概念。在Hibernate提供的各种检索方式中,HQL是官方推荐的查询语言,也是使用最广泛的一种检索方式。它具有如下功能。
- 在查询语句中设定各种查询条件。
- 支持投影查询,即仅检索出对象的部分属性。
- 支持分页查询。
- 支持分组查询,允许使用group by和having关键字。
- 提供内置聚集函数,如sum(), min()和max() 。
- 能够调用用户定义的SQL函数。
- 支持子查询,即嵌套查询。
- 支持动态绑定参数。
Hibernate提供的Query接口是专门的HQL查询接口,它能够执行各种复杂的HQL查询语句。
完整的HQL语句结构如下。
1 |
select...from...where...group by...having...order by...asc/desc |
可见HQL查询非常类似于标准SQL查询。通常情况下,当检索数据表中的所有记录时,查询语句中可以省略select关键字,示例如下所示。
1 |
String hql = "from Customer"; |
如果执行该查询语句,则会返回应用程序中的所有Customer对象,需要注意的是Customer是类名,而不是表名,类名需要区分大小写,而关键字from不区分大小写。
我们已经对HQL有了基本的了解,那么我们具体的来使用一下HQL 。
基本检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@Test //HQL:基本检索 public void testDemo2() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //基本查询 /*Query query = session.createQuery("from Customer"); List<Customer> list = query.list();*/ //起别名 //Query query = session.createQuery("from Customer c"); Query query = session.createQuery("from Customer c"); List<Customer> list = query.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
排序检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Test //HQL:排序检索 public void testDemo3() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- Query query = session.createQuery("from Customer order by cust_id desc"); List<Customer> list = query.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
条件检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@Test //HQL:条件检索 public void testDemo4() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //按位置绑定参数 Query query = session.createQuery("from Customer where cust_name = ?"); //query.setString(0, "小军军");//绑定方式一 query.setParameter(0, "小军军");//绑定方式二 List<Customer> list = query.list(); for (Customer customer : list) { System.out.println(customer); } //按名称绑定参数 Query query1 = session.createQuery("from Customer where cust_name = :name"); query1.setParameter("name", "小添添"); List<Customer> list1 = query1.list(); //如果查询的数据只有一条,就用uniqueResult()获取结果 Customer customer1 = (Customer) query1.uniqueResult(); System.out.println(customer1); //------------------------------------------- transaction.commit(); } |
分页检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Test //HQL:分页查询 public void testDemo5() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- Query query = session.createQuery("from Customer order by cust_id desc"); query.setFirstResult(5); query.setMaxResults(5); List<Customer> list = query.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
统计检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Test //HQL:统计查询 public void testDemo6() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- Query query = session.createQuery("select count(*) from Customer"); long num = (long) query.uniqueResult(); System.out.println(num); //------------------------------------------- transaction.commit(); } |
投影检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
@Test //HQL:投影查询 public void testDemo7() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //投影查询一列 List<String> list = session.createQuery("select cust_name from Customer").list(); for (String string : list) { System.out.println(string); } //投影查询多列 List<Object[]> list1 = session.createQuery("select cust_id,cust_name from Customer").list(); for (Object[] objects : list1) { System.out.println(Arrays.toString(objects)); } //投影的构造的方式查询 List<Customer> list2 = session.createQuery("select new Customer(cust_id,cust_name) from Customer").list(); for (Customer customer : list2) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
QBC检索
QBC(Query By Criteria)是Hibernate提供的另一种检索对象的方式,它主要由Criteria接口、Criterion接口和Expression类组成。Criteria接口是Hibernate API中的一个查询接口,它需要由session进行创建。Criterion是Criteria的查询条件,在Criteria中提供了add(Criterion criterion)方法来添加查询条件。使用QBC检索对象的示例代码,如下所示。
1 2 3 4 5 6 7 8 |
//创建Criteria对象 Criteria criteria = session.createCriteria(Customer.class); //设定查询条件 Criterion criterion = Restrictions.eq("id", 1); //添加查询条件 criteria.add(criterion); //执行查询,返回查询结果 List<Customer> cs = criteria.list(); |
上述代码中查询的是id为1的Customer对象。
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限定查询 |
对QBC查询有了一定的基础以后,我们具体来使用一下QBC完成查询。
基本检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Test //QBC:简单的查询 public void testDemo1() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //简单查询 Criteria criteria = session.createCriteria(Customer.class); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
条件检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@Test //QBC:条件检索 public void testDemo2() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //条件查询 Criteria criteria = session.createCriteria(Customer.class); //设限条件 criteria.add(Restrictions.eq("cust_name","小军军")); criteria.add(Restrictions.like("cust_name","%小%")); criteria.add(Restrictions.gt("cust_id",1l)); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
分页检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Test //QBC:分页查询 public void testDemo3() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //条件查询 Criteria criteria = session.createCriteria(Customer.class); //设置分页查询 criteria.setFirstResult(5); criteria.setMaxResults(5); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
排序检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Test //QBC:排序查询 public void testDemo4() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //排序查询 Criteria criteria = session.createCriteria(Customer.class); criteria.addOrder(Order.desc("cust_id")); List<Customer> list = criteria.list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
统计检索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Test //QBC:统计查询 public void testDemo5() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //排序查询 Criteria criteria = session.createCriteria(Customer.class); criteria.setProjection(Projections.rowCount()); Long count = (Long) criteria.uniqueResult(); System.out.println(count); //------------------------------------------- transaction.commit(); } |
离线条件检索
DetachedCriteria翻译为离线条件查询,因为它是可以脱离Session来使用的一种条件查询对象,我们都知道Criteria对象必须由 Session对象来创建。那么也就是说必须先有Session才可以生成Criteria对象。而DetachedCriteria对象可以在其他层对条件进行封装。
这个对象也是比较有用的,尤其在SSH整合以后这个对象经常会使用。它的主要优点是做一些特别复杂的条件查询的时候,往往会在WEB层向业务层传递很多的参数,业务层又会将这些参数传递给DAO层。最后在DAO中拼接SQL完成查询。有了离线条件查询对象后,那么这些工作都
可以不用关心了,我们可以在WEB层将数据封装好,传递到业务层,再由业务层传递给DAO完成查询。
我们可以先简单的测试一下离线条件查询对象,然后具体的使用我们会在后期整合中使用。到那时会更加体会出它的优势。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Test //QBC:离线条件查询:DetachedCriteria(SSH整合经常使用) //* 可以脱离session设置参数 public void testDemo6() { //获得一个离线条件查询的对象 DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class); detachedCriteria.add(Restrictions.eq("cust_name","小童童")); Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- //离线条件查询对象与session绑定 List<Customer> list = detachedCriteria.getExecutableCriteria(session).list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
本地SQL检索方式
采用HQL或QBC检索方式时,Hibernate生成标准的SQL查询语句,适用于所有的数据库平台,因此这两种检索方式都是跨平台的。但有的应用程序可能需要根据底层数据库的SQL方言,来生成一些特殊的查询语句。在这种情况下,可以利用Hibernate提供的SQL检索方式。使用SQL检
索方式检索对象的示例代码,如下所示:
1 |
SQLQuery sqlQuery = session.createSQLQuery("select cust_id.cust_name from cst_customer"); |
在这,SQL的检索方式我们不做太多的介绍。之前无论我们使用的是HQL或是QBC或是SQL其实都是单表的查询,而实际的开发中我们往往需要多个表的联合查询才可以获得我们想要的数据,那么接下来我们就来学习一下多表的查询吧。
Hibernate的多表查询
在做多表查询之前,我们需要先来回顾一下使用SQL是如何完成多表的查询的。在学习SQL语句的时候进行多表联合查询一般都会采用连接查询,那么我们就来回顾一下SQL中的多表的联合查询。
SQL多表联合查询
连接查询:
【交叉连接】
交叉连接返回的结果是被连接的两个表中所有数据行的笛卡尔积,也就是返回第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数,例如department表中有4个部门,employee表中有4个员工,那么交又连接的结果就有4*4=16条数据。
交叉连接的语法格式如下:
1 |
SELECT * FROM 表1 CROSS JOIN 表2; |
也可以写成如下格式:
1 |
SELECT * FROM 表1,表2; |
从上述描述情况可以看出,交叉连接的结果就是两个表中所有数据的组合。需要注意的是,在实际开发中这种业务需求是很少见的,一般不会使用交叉连接,而是使用具体的条件对数据进行有目的的查询。
【内连接】
内连接(INNER JOIN)又称简单连接或自然连接,是一种常见的连接查询。内连接使用比较运算符对两个表中的数据进行比较,并列出与连接条件匹配的数据行,组合成新的记录,也就是说在内连接查询中,只有满足条件的记录才能出现在查询结果中。内连接查询的语法格式如下所示:
1 |
SELECT 查询字段 FROM 表1 [INNER] JOIN 表2 ON 表1.关系字段 = 表2.关系字段 |
在上述语法格式中,INNER JOIN用于连接两个表,ON来指定连接条件,其中INNER可以省略。内连接其实还可以细分为如下两类
- 隐式内连接:顾名思义隐式的就是我们看不到inner join的关键字。而使用where关键字替代。
1SELECT * FROM 表1 ,表2 WHERE 表1.关系字段 = 表2.关系字段; - 显示内连接:显示的就是在语句中明显的调用了inner join的关键字。
12SELECT * FROM 表1 INNER JOIN 表2 ON 表1.关系字段 = 表2.关系字段;SELECT * FROM 表1 JOIN 表2 ON 表1.关系字段 = 表2.关系字段;
【外连接】
前面讲解的内连接查询中,返回的结果只包含符合查询条件和连接条件的数据,然而有时还需要包含没有关联的数据,即返回查询结果中不仅包含符合条件的数据,而且还包括左表(左连接或左外连接)、右表(右连接或右外连接)或两个表(全外连接)中的所有数据,此时就需要使用外连接查询,外连接分为左连接和右连接。
外连接的语法格式如下:
1 |
SELECT 所查字段 FROM 表1 LEFT|RIGHT| [OUTER] JOIN 表2 ON 表1.关系字段 = 表2.关系字段 WHERE 条件; |
外连接的语法格式和内连接类似,只不过使用的是LEFT JOIN、RIGHT JOIN关键字,其中关键字左边的表被称为左表,关键字右边的表被称为右表。
在使用左连接和右连接查询时,查询结果是不一致的,具体如下:
- LEFT JOIN(左连接):返回包括左表中的所有记录和右表中符合连接条件的记录。
12SELECT * FROM 表1 LEFT OUTER JOIN 表2 ON 表1.关系字段 = 表2.关系字段;SELECT * FROM A LEFT JOIN 表2 ON 表1.关系字段 = 表2.关系字段; - RIGHT JOIN(右连接):返回包括右表中的所有记录和左表中符合连接条件的记录。
12SELECT * FROM 表1 RIGHT OUTER JOIN 表2 ON 表1.关系字段 = 表2.关系字段;SELECT * FROM A RIGHT JOIN 表2 ON 表1.关系字段 = 表2.关系字段;
SQL语句的连接查询我们会写了,那么Hibernate中的HQL如何进行连接查询呢?我们来看一下Hibernate的多表查询。
HQL连接查询
Hibernate进行多表查询与SQL其实是很相似的,但是HQL会在原来SQL分类的基础上又多出来一些操作。
HQL的多表连接查询的分类如下:
- 交叉连接
- 内连接
- 显示内连接
- 隐式内连接
- 迫切内连接
- 外连接
- 左外连接
- 迫切左外连接
- 右外连接
其实这些连接查询语法大致都是一致的,就是HQL查询的是对象而SQL查询的是表。那么我们来比较一下SQL和HQL的连接查询。
SQL连接查询
1 |
SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON c.cust_id = l.lkm_cust_id; |
HQL连接的查询
1 |
from Customer c inner join c.linkMans |
在HQL中,我们不用写关联字段了,因为客户中的联系人的集合其实对应的就是外键,所以我们在inner join的后面直接可以写c. linkMans。在控制台输出的语句的格式如下
我们发现如果这样写HQL语句的话,生成的底层的SQL语句就是使用inner join进行连接,而连接的条件就是customer0_.cust_id=linkmans1_.lkm_cust_id。就是两个表的关联的字段。所以HQL的连接不用写具体的on条件。直接写关联的属性即可。
那么我们己经知道了HQL的内连接的具体的写法了,那么迫切内连接和内连按写法和使用上有什么区别呢?
迫切内连接其实就是在内连接的inner join后添加一个fetch关键字。我们来比对一下:
内连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Test //HQL的多表的连接查询:内连接 public void testDemo8() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- session.createQuery("from Customer c inner join c.linkMans").list(); //------------------------------------------- transaction.commit(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Test //HQL的多表的连接查询:内连接 public void testDemo9() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- session.createQuery("from Customer c inner join fetch c.linkMans").list(); //------------------------------------------- transaction.commit(); } |
控制台输出效果如下:
我们会发现无论是内连接或是迫切内连接发送的底层SQL都是一样的,而且在生成的SQL语句中也没有fetch关键字,当然fetch本身就不是SQL语句的关键字。所以一定要注意,fetch只能在HOL中使用的,生成了SOL语句以后,fetch就消失了。那么fetch到底有什么作用呢?
其实我们知道HQL内连接查询的和SQL的内连接查询到的结果集是一样的,都是两个表的交集部分的数据。
然后在封装数据的时候,普通内连接会将属于客户的数据封装到Customer对象中,会将属于联系人的数据封装到LinkMan对象中,所以每条记录都会是装有两个对象的集合,所以封装以后的数据是List<Object[]>,在Object[]中有两个对象,一个是Customer另一个是LinkMan。所以在使用普通内连接的时候可以如下编写代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Test //HQL的多表的连接查询:内连接 public void testDemo8() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- List<Object[]> list = session.createQuery("from Customer c inner join c.linkMans").list(); for (Object[] objects : list) { System.out.println(Arrays.toString(objects)); } //------------------------------------------- transaction.commit(); } |
那么加了fetch以后,虽然我们查询到的数据是一样的,但是Hibernate发现HQL中有fetch就会将数据封装到一个对象中,把属于客户的数据封装到Customer对象中,将属于联系人的部分封装到Customer中的联系人的集合中,这样最后封装完成以后是一个List中。所以使用迫切内连接的时候可以如下编写代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Test //HQL的多表的连接查询:内连接 public void testDemo9() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- List<Customer> list = session.createQuery("from Customer c inner join fetch c.linkMans").list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
其实内连接和迫切内连接的主要区别就在与封装数据,因为他们查询的结果集都是一样的,生成底层的SQL语句也是一样的。
- 内连接:发送就是内连接的语句,封装的时候将属于各自对象的数据封装到各自的对象中,最后得到一个List<Object[]>。
- 迫切内连接:发送的是内连接的语句,需要在编写HQL的时候在join后添加一个fetch关键字,Hibernate会发送HQL中的fetch关键字,从而将每条数据封装到对象中,最后得到一个List。
但是迫切内连接封装以后会出现重复的数据,因为我们查询到目前有三条记录,就会被封装到三个对象中,其实我们真正的客户对象只有两个,所以往往自己在手动编写迫切内连接的时候会使用distinct去掉重复值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Test //HQL的多表的连接查询:内连接 public void testDemo9() { Session session = HibernateUtils.getCurrentSession(); Transaction transaction = session.beginTransaction(); //------------------------------------------- List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.linkMans").list(); for (Customer customer : list) { System.out.println(customer); } //------------------------------------------- transaction.commit(); } |
到这我们己经对Hibernate查询己经学习完了,那么我们应该会使用Hibernate的这些查询应对今后的开发了。但是其实Hibernate的查询的效率并不高,我们需要对这些查询进行优化,才能达到高效的编程。
Hibernate的抓取策略:
什么是抓取策略
抓取策略是当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候,Hibernate如何获取关联对象的策略。
Hibernate的抓取策略是Hibernate提升性能的一种手段,可以在获取关联对象的时候,对发送的语句进行优化,但是往往抓取策略需要和延迟加载一起使用来提升性能。我们首先来学习一下延迟加载的相关内容吧。
在学习延迟加载内容之前,我们需要了解延迟加载的分类。
延迟加载的分类:
延迟加载在前面我们有过简单的介绍,延迟加载(lazy load)是(也称为懒加载)Hibernate关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。
通常将延迟加载分为两类:一类叫做类级别延迟,另一类叫做关联级别的延迟。类级别的延迟指的是查询某个对象的时候,是否采用有延迟,这个通常在标签上配置lazy属性。关联级别的延迟指的是,查询一个对象的关联对象的时候是否采用延迟加载。这个通常在或
上配置lazy属性。
【类级别的延迟加载】
使用load方法检索某个对象的时候,这个类是否采用延迟加载的策略,就是类级别的延迟。类级别的延迟一般在上配置lazy属性,lazy的默认值是true。默认是延迟加载的,所以使用load方法去查询的时候,不会马上发送SQL语句,当真正使用该对象的时候,才会发送SQL语句。
1 |
Customer customer = session.load(Customer.class, 1l); |
其实如果不想使用延迟加载也有很多种方法,当然最简单的就是将这个类的映射文件上的lazy设置为false,当然也可以将这个持久化类改为final修饰,之前也介绍过,如果改为final修饰的话,就无法生成代理类,就会使延迟加载失效。
这是类级别的延迟加载,类级别的延迟加载一般我们不进行修改,采用默认值lazy=”true”,就可以了。
其实主要的是关联级别的延迟加载,关联级别的延迟加载指的是查询到某个对象以后,检索它的关联对象的时候是否采用延迟加载。
【关联级别的延迟加载】
1 2 |
Customer customer = session.get(Customer.class, 1l); Set<LinkMan> linkMans = customer.getLinkMans(); |
通过客户查询其关联的联系人对象,在查询联系人的时候是否采用延迟加载称为是关联级别的延迟。关联级别的延迟通常是在和上来进行配置。
标签上的lazy通常有三个取值
- true:默认值,采用延迟加载
- false:检索关联对象的时候,不采用延迟加载。
- extra:及其懒惰的。
标签上的lazy通常有三个取值
- proxy:默认值,是否采用延迟取决于一的一方类上的lazy属性的值。
- false:检索关联对象的时候,不采用延迟加载。
- no-proxy:不用研究。
延迟加载介绍过了,延迟加载也是往往和抓取策略一起使用提升开发的程序的性能的。那么接下来我们要来研究抓取策略了。
抓取策略指的是查询到某个对象的时候,如何抓取其关联对象。这个也可以通过配置完成。在关联对象的标签上配置fetch属性。关联上就分为是在和上,也都有不同的取值。
标签上的fetch通常有三个取值
- select:默认值,发送的是普通的select语句查询。
- join:发送一条迫切左外连接去查询。
- subselect:发送一条子查询语句查询其关联对象。
标签上的fetch有两个取值
- select:默认值,发送一条普通的select语句查询关联对象。
- join:发送一条迫切左外连接语句查询其关联对象。
这样来看在set上配置fetch有三个值,lazy有三个值,这样就会产生很多种的效果。其实不用担心,因为fetch如果设置为join, lazy就会失效了。
我们可以通过代码测试的方式来查看每种取值的效果。在标签上的fetch和lazy。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
@Test //查询1号客户,同时查询1号客户的联系人的数量(默认情况) public void testDemo() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //查询一号客户 Customer customer = session.get(Customer.class, 1l);//发送一条select语句查询1号客户 //获得一号客户的联系人的数量 System.out.println(customer.getLinkMans().size());//发送一条select语句查询1号客户的联系人 //---------------------------------------------- tx.commit(); } @Test //查询1号客户,同时查询1号客户的联系人的数量 //在<set>上配置fetch="select" lazy="true" public void testDemo1() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //查询一号客户 Customer customer = session.get(Customer.class, 1l);//发送一条select语句查询1号客户 //获得一号客户的联系人的数量 System.out.println(customer.getLinkMans().size());//发送一条select语句查询1号客户的联系人 //---------------------------------------------- tx.commit(); } @Test //查询1号客户,同时查询1号客户的联系人的数量 //在<set>上配置fetch="select" lazy="true" public void testDemo2() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //查询一号客户 Customer customer = session.get(Customer.class, 1l);//发送两条select语句 1条:查询1号客户 1条:查询1号客户的所有联系人 //获得一号客户的联系人的数量 System.out.println(customer.getLinkMans().size());//不发送SQL语句 //---------------------------------------------- tx.commit(); } @Test //查询1号客户,同时查询1号客户的联系人的数量 //在<set>上配置fetch="select" lazy="extra" public void testDemo3() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //查询一号客户 Customer customer = session.get(Customer.class, 1l);//发送一条select语句查询1号客户 //获得一号客户的联系人的数量 System.out.println(customer.getLinkMans().size());//发送一条select count(*)语句统计个数 //---------------------------------------------- tx.commit(); } @Test //查询1号客户,同时查询1号客户的联系人的数量 //在<set>上配置fetch="join" lazy="失效" public void testDemo4() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //查询一号客户 Customer customer = session.get(Customer.class, 1l);//发送一条迫切左外连接直接将关联的对象一起查询了 //获得一号客户的联系人的数量 System.out.println(customer.getLinkMans().size());//不发送了 //---------------------------------------------- tx.commit(); } @Test //查询1号客户,同时查询1号客户的联系人的数量 //在<set>上配置fetch="subselect" lazy="true" public void testDemo5() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //子查询不能查询某一个对象,查询多个对象来测试 List<Customer> list = session.createQuery("from Customer").list();//发送查询客户的语句 for (Customer customer : list) { System.out.println(customer.getLinkMans().size());//发送一条子查询 查询 客户关联的联系人 } //---------------------------------------------- tx.commit(); } @Test //查询1号客户,同时查询1号客户的联系人的数量 //在<set>上配置fetch="subselect" lazy="false" public void testDemo6() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- //子查询不能查询某一个对象,查询多个对象来测试 List<Customer> list = session.createQuery("from Customer").list();//发送查询所有客户的及了查询查询客户的联系人 for (Customer customer : list) { System.out.println(customer.getLinkMans().size()); } //---------------------------------------------- tx.commit(); } |
集合上的fetch和lazy
fetch:控制的是查询其关联对象的时候采用的SQL语句的格式
- select:默认值。发送一条select语句查询其关联对象。
- join:发送 一条迫切左外连接查询关联对象。
- subselect:发送一条子查询查询其关联对象。
lazy:控制的是查询其关联对象的时候是否采用延迟加载的策略。
- true : 默认值。默认查询联系人的时候采用延迟加载。
- false:查询关联对象的时候 不采用延迟加载。
- extra:及其懒惰。查询关联对象的时候 采用比延迟加载更懒惰的方式进行查询。
我们可以简单的总结一下fetch和lazy的作用,其实fetch主要控制抓取关联对象的时候的发送SQL语句的格式的。lazy主要控制查询其关联对象的时候是否采用延迟加载的。
批量抓取
在抓取的策略中有一种叫做批量抓取,就是同时查询多个对象的关联对象的时候,可以采用批量抓取进行优化。当然这个就不是特别的重要了。
如果要实现批量的抓取效果,可以通过配置batch-size来完成。来看下下面的效果:
【查询客户批量抓取联系人】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Test //查询客户的时候批量抓取联系人 //在Customer.hbm.xml中<set>集合上配置batch-sire="4" public void testDemo7() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- List<Customer> list = session.createQuery("from Customer").list(); for (Customer customer : list) { System.out.println("客户名称:"+customer.getCust_name()); for (LinkMan linkMan : customer.getLinkMans()) { System.out.println("客户对应的联系人的名称:"+linkMan.getLkm_name()); } } //---------------------------------------------- tx.commit(); } |
在没有设置batch-size之前运行以上代码,会出现如下的效果:
目前数据库中有两个客户,那么会发现执行代码会发生三条SQL语句。那么我们能不能发生一条SQL直接将两个客户的关联的联系人一起查询出来呢,我们可以在
配置完了batch-size以后,再次运行以上的代码会发现:
这时会发现SQL语句发生了变化,当然数据量越大,效果越明显。这是在查询所有客户的时候,批量抓取联系人,那么如果我们要实现查询多个联系人的时候,批量抓取联系人所对应的客户对象呢?这时同样需要配置batch-size。但是不是在
【查询联系人批量抓取客户】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Test //查询联系人的时候批量抓取客户 //在Customer.hbm.xml中<class>集合上配置batch-sire="2" public void testDemo8() { Session session = HibernateUtils.getCurrentSession(); Transaction tx = session.beginTransaction(); //--------------------------------------------- List<LinkMan> list = session.createQuery("from LinkMan").list(); for (LinkMan linkMan : list) { System.out.println(linkMan.getLkm_name()); System.out.println(linkMan.getCustomer().getCust_name()); } //---------------------------------------------- tx.commit(); } |
注意:这时候要在客户一端的映射文件中的class标签上配置batch-size。