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的查询方式:QBC检索(*****)
QBC查询:Query By Criteria,条件查询。是一种更加面向对象化的查询的方式
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
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
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、抓取策略很有魅力,研究起来很有意思