Hibernate框架(四):HQL与QBC查询详解&抓取策略优化机制

Hibernate系列文章推荐:
👉 Hibernate框架(一):入门及映射文件配置
👉 Hibernate框架(二):主键生成策略&一级缓存&事务管理
👉 Hibernate框架(三):表操作&多对多配置
👉 待更新

1、上一章内容回顾

 Hibernate的一对多
   表与表之间关系
     一对多关系
     多对多关系
     一对一关系
   Hibernate的一对多配置
     搭建Hibernate基本环境
     创建表
     创建实体
       一的一方
         放的是多的一方的集合
       多的一方
         放的是一的一方的对象
     创建映射
       一的一方
         配置的集合
       多的一方
         配置
     编写测试类
   Hibernate的一对多的操作
     级联操作:cascade,用于操作其关联的对象。
       级联保存或更新
       级联删除
     测试对象导航
     放弃外键维护权:inverse,用户控制是否有外键维护能力
 Hibernate的多对多
   Hibernate的多对多配置
     搭建Hibernate环境
     创建表
     创建实体
       放置的是对方的集合
     创建映射
       配置的是对象的
     编写测试类
   Hibernate的多对多操作
     级联操作
       级联保存或更新
       级联删除(了解)
     其他的操作
       给用户选择角色
       给用户改选角色
       给用户删除角色

2、Hibernate的查询方式

在Hibernate中提供了很多种的查询方式,Hibernate共提供了五种查询方式

Hibernate的查询方式:OID和对象导航检索

OID检索
OID检索:Hibernate根据对象的OID(主键)进行检索

1、使用get方法
Customer customer = session.get(Customer.class,1l);
2、使用load方法
Customer customer = session.load(Customer.class,1l);

对象导航检索
对象导航检索:Hibernate根据一个已经查询到的对象,获得其关联的对象的一种查询方式

LinkMan linkMan = session.get(LinkMan.class,1l);
Customer customer  = linkMan.getCustomer();

Customer customer = session.get(Customer.class,2l);
Set<LinkMan> linkMans = customer.getLinkMans();
Hibernate的查询方式:HQL检索(*****)

HQL查询:Hibernate Query Language,Hibernate的查询语言,是一种面向对象的方式的查询语言,语法类似SQL。通过session.createQuery(),用于接收一个HQL进行查询方式

初始化一些数据

初始化数据

HQL查询-简单查询和别名查询

HQL的简单查询
简单查询
HQL的别名查询
别名查询

HQL查询-排序查询和条件查询

HQL的排序查询
排序查询
HQL的条件查询
条件查询

HQL查询-投影查询和分页查询

HQL的投影查询
投影查询:查询对象的某个或某些属性
投影查询
HQL的分页查询
分页查询

HQL查询-分组统计查询

其中Query中uniqueResult的作用:
  如果查询结果有多个值则抛出错误;
  如果查询结果有且只有一个值,返回一个object;
  如果没值,返回null

@Test
//HQL的分组统计查询
public void demo07(){
	Session session = HibernateUtils.getCurrentSession();
	Transaction transaction = session.beginTransaction();
	/*Object object = session.createQuery("select count(*) from LinkMan").uniqueResult();
	System.out.println(object);*/
	Query query = session.createQuery("select cust_name,count(*) from Customer where cust_source=? group by cust_name");
	query.setParameter(0, "广告");
	List<Object[]> list = query.list();
	for (Object[] object : list) {
		System.out.println(Arrays.toString(object));
	}
	transaction.commit();
}
HQL查询-多表查询

 SQL的多表查询
   连接查询
     交叉连接:笛卡尔积(基本不用)
      select * from A,B;
     内连接 :inner join (inner 可以省略),两个表的交集
       隐式内连接:
        select * from A,B where A.id = B.aid;
       显示内连接:
        select * from A inner join B on A.id = B.aid;
     外连接 :
       左外连接:left outer join(outer 可以省略)
        select * from A left outer join B on A.id= B.aid;
       右外连接:right outer join(outer 可以省略)
        select * from A right outer join B on A.id = B.aid;
   子查询

 HQL的多表查询
   连接查询
     交叉连接
     内连接
       显示内连接
       隐式内连接
       迫切内连接
     外连接
       左外连接
       右外连接
       迫切左外连接
hibernate多表查询

Hibernate的查询方式:QBC检索(*****)

QBC查询:Query By Criteria,条件查询。是一种更加面向对象化的查询的方式

QBC查询-简单查询和排序查询

QBC的简单查询
QBC的简单查询
QBC的排序查询
QBC的排序查询

QBC查询-分页查询和条件查询

QBC的分页查询
QBC的分页查询
QBC的条件查询
QBC的条件查询
QBC的条件查询更适合用于网页或应用程序的多条件查询,更适用于动态的,比如年龄、姓名、状态等多条件查询,因为QBC可以随时设置条件

QBC查询-统计查询

QBC的统计查询

QBC查询-离线条件查询(SSH)

DetachedCriteria的个人理解
  DetachedCriteria翻译为离线条件查询,因为它是可以脱离Session来使用的一种条件查询对象,我们都知道Criteria对象吧必须由Session
  对象来创建。那么就是说必须先有Session才可以生成Criteria对象。而DetachedCriteria对象可以在其他层对条件进行封装。
  这个对象也是比较有用的,尤其在SSH整合以后这个对象会经常使用。它的主要优点是做一些特别复杂的条件查询的时候,
  往往会在WEB层向业务层传递很多的参数,业务层又会将这些参数传递给DAO层。最后在DAO中拼接SQL完成查询。有了离线条件查询对象后,
  那么这些工作都可以不用关心了,我们可以在WEB层将数据封装好,传递到业务层,再由业务层传递给DAO完成查询。
  简单的说就是:封装条件的对象criteria,必须现有session,而session是在DAO生成的,那么就意味着,web层到service层再到DAO层,中间传递的都是条件字符串,然后再对应封装条件。这样很麻烦,不如在web层开始就把条件封装好,然后传递条件就是了,最后到了DAO层让这个条件对象绑定当前session就OK。这就是离线条件查询,DetachedCriteria

//用于个人理解etachedCriteria
public void demo1(){//普通做法,接收name、sex、phone、state
	//1、web层
	String name = request.getparameter("name");
	String sex = request.getparameter("sex");
	String phone = request.getparameter("phone");
	String state = request.getparameter("state");
	if(name!=null&&sex!=null&&phone!=null&&state!=null)方法1
	if(name==null&&sex!=null&&phone!=null&&state!=null)方法2
	if(name==null&&sex==null&&phone!=null&&state!=null)方法3
	if(name==null&&sex==null&&phone==null&&state!=null)方法4
	if(name==null&&sex==null&&phone==null&&state==null)方法5
	省略n行...
	//2、service层
	调方法:方法1、方法2、方法3、方法4、方法5……
	//3、dao层
	书写:
	方法1、方法2、方法3
	省略n行...
}
public void demo2(){//离线查询,接收name、sex、phone、state
	//1、web层
	String name = request.getparameter("name");
	String sex = request.getparameter("sex");
	String phone = request.getparameter("phone");
	String state = request.getparameter("state");
	DetachedCriteria criteria = DetachedCriteria.forClass(user.class);
	if(name!=null)criteria.add("……");if(sex!=null)criteria.add("……");
	if(phone!=null)criteria.add("……");if(state!=null)criteria.add("……");
	//2、service层
	调用dao的方法
	//3、dao层
	书写:方法
}

代码案例
离线条件查询

Hibernate的查询方式:SQL查询

SQL查询:直接使用SQL语句进行查询,一般比较少用,多用QBC和HQL
SQL查询

3、Hibernate的抓取策略

延迟加载

什么是延迟加载
延迟加载:lazy(懒加载)。执行到该行代码的时候,不会发送语句去进行查询,在真正使用这个对象的属性的时候才会发送SQL语句进行查询
延迟加载的分类
 类级别的延迟加载
   指的是通过load方法查询某个对象的时候,是否采用延迟。session.load(Customer.class,1l);
   类级别延迟加载通过<class>上的lazy进行配置,如果让lazy失效
     将lazy设置为false
     将持久化类使用final修饰
     Hibernate. Initialize()
 关联级别的延迟加载
   指的是在查询到某个对象的时候,查询其关联的对象的时候,是否采用延迟加载。
    Customer customer = session.get(Customer.class,1l);
    customer.getLinkMans();----通过客户获得联系人的时候,联系人对象是否采用了延迟加载,称为是关联级别的延迟。
   抓取策略往往会和关联级别的延迟加载一起使用,优化语句。

抓取策略

抓取策略的概述
 通过一个对象抓取到关联对象需要发送SQL语句,SQL语句如何发送,发送成什么样格式通过策略进行配置。
   通过set或者many-to-one上通过fetch属性进行设置
   fetch和这些标签上的lazy如何设置优化发送的SQL语句

set集合上的fetch和lazy

set上的fetch和lazy
 fetch:抓取策略,控制SQL语句格式
   select :默认值,发送普通的select语句,查询关联对象
   join :发送一条迫切左外连接查询关联对象
   subselect :发送一条子查询查询其关联对象
 lazy:延迟加载,控制查询关联对象的时候是否采用延迟
   true :默认值,查询关联对象的时候,采用延迟加载
   false :查询关联对象的时候,不采用延迟加载
   extra :及其懒惰,比如在延迟加载的前提下,要查size就用count来查
 在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join

fetch和lazy可以配合来使用,简单的来说,fetch改变的是查询的方式,lazy改变的是查询的延迟与否。
lazy理解起来很简单,如果true则用到再查,false则查关联对象的时候直接把该信息也查了
fetch理解起来还是需要敲代码的。
1、fetch的特殊情况就是join,当设置join的时候,就相当于连接查询,把所有一起查了,这时设置延迟加载也没用。
2、fetch中的subselect和select的区别这里用代码与效果截图来表现(同样的代码,设置的fetch不同):

@Test
//Hibernate的抓取策略:fetch="subselect"和fecth="select"的区别
public void demo14(){
	Session session = HibernateUtils.getCurrentSession();
	Transaction transaction = session.beginTransaction();
	Query query = session.createQuery("from Customer");
	List<Customer> list = query.list();
	for (Customer customer : list) {
		System.out.println(customer);
		System.out.println(customer.getlinkMans().size());
	}
	transaction.commit();
}

上图为select,下图为subselect
select
subselect

many-to-one上的fetch和lazy

many-to-one上的fetch和lazy
 fetch :抓取策略,控制SQL语句格式
   select :默认值,发送普通的select语句,查询关联对象。
   join :发送一条迫切左外连接
 lazy :延迟加载,控制查询关联对象的时候是否采用延迟。
   proxy :默认值,proxy具体的取值,取决于另一端的class上的lazy的值。
   false :查询关联对象,不采用延迟
   no-proxy :(不会使用)
 在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join

many-to-one的lazy中没有true,只有proxy,但是proxy与set的true还有有点不一样的,因为proxy的延迟加载策略取决于一的一方的class的lazy是否为true,如果为true,则proxy就相当于延迟加载,否则proxy也相当于没用。

@Test
//Hibernate的抓取策略:lazy="proxy"与一的一方的lazy的关系
public void demo15(){
	Session session = HibernateUtils.getCurrentSession();
	Transaction transaction = session.beginTransaction();
	Criteria criteria = session.createCriteria(LinkMan.class);
	List<LinkMan> list = criteria.list();
	for (LinkMan linkMan : list) {
		System.out.println(linkMan.getCustomer().getCust_name());
	}
	transaction.commit();
}
批量抓取

批量抓取的概念
在进行查询的时候,将n个关联对象一起抓取,这就是批量抓取,批量抓取通过设置batch-size来实现
测试批量抓取
1、测试Customer的批量抓取

@Test
//Hibernate的抓取策略:批量抓取策略,测试Customer对象
//1、在Customer.hbm.xml中设置fetch="subselect" lazy="false"
//2、在Customer.hbm.xml中设置fetch="join"
//3、在Customer.hbm.xml中设置fetch="select" lazy="false"
//4、在Customer.hbm.xml中设置batch-size="4" lazy="false"
public void demo16(){
	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.getCust_name());
		for (LinkMan linkMan : customer.getlinkMans()) {
			System.out.println(linkMan.getLkm_name());
		}
	}
	transaction.commit();
}

这里我之所以写4个前提下测试的原因是因为,我发现了一个问题,在学set的fetch的时候,我发现使用subselect也是可以一次性提取出来的,提取时的sql语句最后是in;使用join也能提取;同样的,使用select,lazy设置false也能一次性提取,具体效果如下:
第一种情况
第二种情况
第三种情况
第四种情况
抓取策略小结
1、关于批量抓取的其他案例就不写了
2、以上四种情况的查询,虽然看起来好像作用都一样,没什么区别。但是事实上是有差别的,比如查询LinkMan这种多对一的对象时,要获取LinkMan中的Customer对象,查询时是与Customer配置文件中的lazy设置有关的。
3、Customer中的lazy为false,则查询LinkMan的时候也会把Customer一起查了,但是有的时候我们没有输出Customer信息,所以就造成了过度查询,这时就有区别了。上面4中方法中,只有subselect和batch-size的时候是不需要设置lazy的,也就是说,不会影响到关联对象信息的使用
4、抓取策略很有魅力,研究起来很有意思

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hillain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值