迫切左连接
//查询cid为1的Country的信息以及所有关联的Minister的信息 迫切左外链接
String hql = “select distinct c from Country c left outer join fetch c.ministers”; //去重
Hibernate的检索优化:真正需要的时候才执行select,将select的语句执行时间推到最后才执行 有可能不执行
对象加载:直接通过get() load()等查询语句直接加载的对象,称为主加载对象,而主加载对象所关联的对象,称为关联加载对象,或从加载对象
直接加载:不使用优化进行的对象检索的过程
延迟加载:(懒加载)经过优化之后进行检索的过程
延迟加载的底层实现,在Hibernate3.3之前使用CGLIB加载,之后使用javassist代理实现 若实体类使用final修饰,将无法生成CGLIB代理,考虑到老版本的兼容问题,实体最好不要使用final修饰
当前对象的检索优化:主对象
get和load区别和不同:(会背常考面试中常考)
共同点:都是根据id来加载对象,当你加载的对象在数据库中不存在是get会返回NULL,load会抛出异常
为什么?
原因是:load采用了延迟加载,get采用直接加载,这个异常是什么时候报粗来的?load延迟加载采用代理是代理模式,load执行完后会返回一个代理对象,这时候是没有抛出异常的,
但这个代理对象并没有被初始化,紧接着当你访问查询对象的详情(id以外)时候,会击发select查询,这时候也没有报错,其结果结果为NULL,拿查询结果去初始化代理对象就会爆出异常,因为代理模式中要求
目标对象必须要有目标对象,此时便会抛出异常。load默认是延迟加载 也可以设置立即加载 (通过设置lazy属性 = false)
list和iterator
list进行得是详情查询,而iterator先进行主键查询,后面在每次根据id到session中进行查询,之后再到数据库进行查询,session是一个mapper(key: value:在堆中)
第一次执行效率特别低,但是如果session中存在数据,则下次会在session中直接查找 N+1问题
set标签里面有个inverse可以放弃当前对象的管理权 多对多设置主外键需要一致 ,否则容易出错
自关联:应用场景 缓存对象是session MyBatis缓存得是sql语句
多对多:是通过中间表连接在一起的,需要新增table many-to-many column 指明主外键 可以使用inverse放弃 关联关系的维护权
多对多关系中:第三方表才是多方 ,因为他有外键
级联删除一般不使用
级联中迫切左连接查询的是一张表: String hql = “select distinct c from Country c left outer join fetch c.ministers”; //去重 先把表进行组合然后在进行查询
关联对象的检索优化:即对于从加载对象的检索,也可进行延迟加载优化 讨论关联的表啥时候查询,采用何种策略,映射文件中对于关联对象的优化属性配置属性有两个:lazy fetch
设置在映射文件的关联属性的映射中的
多端加载对象:是以一方为主加载对象,而多方作为从加载对象 set标签 fetch=“select”加载方式 lazy=“false”加载时机
fetch=”join”时候lazy属性失效
fetch=”select” lazy=”false”查询一张表,把关联表一并查询了
fetch=”subselect” lazy=”true”有时候可以用在具有多个查询需要具体列表查询
单端加载对象:多方为主加载对象,而一方作为从加载对象
Hibernate缓存机制:
缓存就是数据交换的缓冲区,是但session缓存,其只能被当前事务访问访问,每个事务都有自己的缓存,缓存的生命周期依赖于事务的生命周期
一级缓存:就是session缓存,就是内存中一块空间,session缓存是事务范围的缓存,伴随事务的开启而开启,结束而结束。
存在性证明:通过两次一模一样的查询,结果第二次没有查询但是有数据,根据id去缓存查找,如果插在就不需要再去数据库查询
应用范围缓存:即二级缓存,是单SessionFactory缓存,可以被应用程序的所有事务共享访问
集全范围缓存:是多SessionFactory缓存,在集群环境中,缓存被一个机器或多个机器的进程共享
快照:即副本,Hibernate中的快照,即数据库的副本,快照中的数据始终与DB中的数据保持一致,不能由代码对副本内容进行修改
/*
* session.get()都做了那些工作:
* 1:将数据从DB中读取出来
* 2:将数据转换成对象,存放到堆内存
* 3:将对象的id放入到session缓存map的key中
* 将对象的引用放入到session缓存的map的value中
* 4:将对象的详情放入带“快照”中 (快照数据和DB数据必须保持一致 从而用session中数据和”快照“中数据进行对比)
*/
Student student = session.get(Student.class, 9);
//Student student = session.get(Student.class, 9);
student.setName(“zhangsan”;
//session.update(student);
System.out.println(“————————————————”);
//second select
Student student2 = session.get(Student.class, 9);
System.out.println("student2.id = " + student2.getId());
System.out.println("student2.name = " + student2.getName());
//6:执行事务
/*事务提交时执行的操作:
* 1:将堆内存中的数据与“快照”中数据进行比较,若比较结果不同,则执行update操作,
* 若比较结果相同,则不执行update操作
*/
session.getTransaction().commit();
Session的刷新与同步:
增删改同事存在执行顺序是 ——》增改删 若要改变执行顺便,需要执行reflush操作
刷新指的是Session缓存中数据的更新,Session的同步指的是将Session缓存的数据同步跟新到DB中去,执行同步的时间点只有一个:事务的提交
当代码中执行了update和delete操作之后,Session缓存并不会立即刷新,不会马上执行update和delete操作,而是在某个时间点来到,才会执行刷新,更新缓存中数据,刷新时间点有三个:
1)执行Query查询
2)执行session.flush()操作
3)执行事务的提交
删除操作在刷新时间点到来,会马上进行缓存更新,马上执行delete语句
修改操作 当刷新时间点到来,需要卡快照和修改后的数据是否一致,若一致,则不会执行update操作,否则自行update操作
删除操作执行
Student student = session.get(Student.class, 5);
/*
* 执行到session.delete()之后,并不会直接执行delete操作,而是到达刷新点之后才会执行,这就是对session缓存数据的刷新,实际就是对堆内存的数据的更新
*/
session.delete(student);
更新操作执行
Student student = session.get(Student.class, 10);
student.setName("王旭小航");
/*
* 当执行修改操作是update时,及时到达刷新点,是否执行update的SQL语句,还要取决于修改的数据与快照中的数据对比结果,相同则不执行,否则执行update的sql
*/
session.update(student);
删除操作执行
//刷新操作无需到达刷新点就执行插入操作
Student student = new Student(“王旭航”,22,99);
session.save(student);
修改刷新点
//修改刷新查询点 直接使用FlushMode.COMMIT进行提交即可
session.setFlushMode(FlushMode.COMMIT);
Student student = session.get(Student.class, 8);
/*
* 执行到session.delete()之后,并不会直接执行delete操作,而是到达刷新点之后才会执行,这就是对session缓存数据的刷新,实际就是对堆内存的数据的更新
*/
session.delete(student);
//直接进行查询
session.createQuery("from Student").list();
二级缓存基础知识:
二级缓存是SessionFactory级的缓存,其生命周期与SessionFactory周期一致,SessionFactory的内置缓存中存放了映射文件数据和预定义SQL语句,二级缓存指的是SessionFactory的外置缓存 Hibernate本身只提供二级缓存的规范,需要借助三方插件进行管理
Hibernate缓存执行顺序:当Hibernate根据id访问数据对象,先在一级缓存session中查找,若查找不到再到二级缓存中进行查找,若还查找不到再到DB中进行查找,把结果按照id把结果放到缓冲
二级缓存内容分类:三类
类缓存:缓存对象为实体类对象
集合缓存:缓存对象为集合类对象
查询缓存:缓存对象的查询结果
二级缓存的并发访问策略:
事务型
读写性(默认 只读的,不能修改)
非严格读写性
最后增加一个二级缓存的demo,其中很多注解在代码中都有,作者在这里就不做深入分析,学习最好的方法就是动手!!
首先就是环境的搭建:
用到的jar包的下载链接:
代码结构总体结构 就增加ehcache的jar包
实体类 Country.java
package com.vrv.yinkailong.bean;
import java.util.HashSet;
import java.util.Set;
//自定义一个类 国家可以看到部长
public class Country {
private Integer cid;
private String cname;
//关联属性
private Set ministers;
public Country() {
ministers = new HashSet();
}
public Country(String cname) {
this(); //直接去执行前面的 ministers = new HashSet();
this.cname = cname;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Set getMinisters() {
return ministers;
}
public void setMinisters(Set ministers) {
this.ministers = ministers;
}
@Override
public String toString() {
return "Country [cid=" + cid + ", cname=" + cname + ", ministers=" + ministers + "]";
}
}
实体类 Minister.java
package com.vrv.yinkailong.bean;
//自定义一个属性 部长 看不到国家
public class Minister {
private Integer mid;
private String mname;
public Minister() {
super();
}
public Minister(String mname) {
super();
this.mname = mname;
}
public Integer getMid() {
return mid;
}
public void setMid(Integer mid) {
this.mid = mid;
}
public String getMname() {
return mname;
}
public void setMname(String mname) {
this.mname = mname;
}
@Override
public String toString() {
return "Minister [mid=" + mid + ", mname=" + mname + "]";
}
}
映射文件 com/vrv/yinkailong/bean/Country.hbm.xml
<hibernate-mapping package="com.vrv.yinkailong.bean">
<class name="Country">
<id name="cid">
<generator class="native"/>
id>
<property name="cname"/>
<set name="ministers" cascade="save-update" fetch="select" lazy="true" >
<key column="countrId"/>
<one-to-many class="Minister"/>
set>
class>
hibernate-mapping>
映射文件 com/vrv/yinkailong/bean/Minister.hbm.xml
<hibernate-mapping package="com.vrv.yinkailong.bean">
<class name="Minister">
<id name="mid">
<generator class="native"/>
id>
<property name="mname"/>
class>
hibernate-mapping>
总体的配置文件
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driverproperty>
<property name="hibernate.connection.url">jdbc:mysql://localhost/testproperty>
<property name="hibernate.connection.username">rootproperty>
<property name="hibernate.connection.password">123456property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialectproperty>
<property name="hibernate.co nnection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProviderproperty>
<property name="hibernate.current_session_context_class">threadproperty>
<property name="hibernate.hbm2ddl.auto">updateproperty>
<property name="hibernate.show_sql">trueproperty>
<property name="hibernate.format_sql">trueproperty>
<property name="hibernate.cache.use_second_level_cache">trueproperty>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactoryproperty>
<mapping resource="com/vrv/yinkailong/bean/Minister.hbm.xml"/>
<mapping resource="com/vrv/yinkailong/bean/Country.hbm.xml"/>
<class-cache usage="read-only" class="com.vrv.yinkailong.bean.Country"/>
<class-cache usage="read-only" class="com.vrv.yinkailong.bean.Minister"/>
<collection-cache usage="read-only" collection="com.vrv.yinkailong.bean.Country.ministers"/>
session-factory>
hibernate-configuration>
在二级缓存需要引入的文件ehcache.xml 直接放在src路径下即可,该文件在E:\eclipse code\hibernate-release-5.1.0.Final\hibernate-release-5.1.0.Final\lib\optional\ehcache.ehcache-2.10.1.jar 里面解压之后直接拷贝即可 前面注释省略
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
defaultCache>
ehcache>
最后是测试类:MyTest .java
package com.vrv.yinkailong.test;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.hql.internal.ast.HqlASTFactory;
import org.junit.Test;
import com.vrv.yinkailong.bean.Country;
import com.vrv.yinkailong.bean.Minister;
import com.vrv.yinkailong.util.HiberUtil;
public class MyTest {
@Test
public void test00()
{
Session session = HiberUtil.getSession();
try {
session.getTransaction().begin();
//session.beginTransaction();
Minister minister1 = new Minister("洛杉矶");
Minister minister2 = new Minister("自由女神");
Minister minister3 = new Minister("纽约");
Minister minister4 = new Minister("北京");
Minister minister5 = new Minister("新疆");
Minister minister6 = new Minister("武汉");
Country country = new Country("USA");
country.getMinisters().add(minister1);
country.getMinisters().add(minister2);
country.getMinisters().add(minister3);
session.save(country);
Country country2 = new Country("中国");
country2.getMinisters().add(minister4);
country2.getMinisters().add(minister5);
country2.getMinisters().add(minister6);
session.save(country2);
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
}
@Test
public void test01()
{
Session session = HiberUtil.getSession();
try {
session.getTransaction().begin();
//第一次查询
Country country = session.get(Country.class, 2);
System.out.println("country = " + country);
//已经把session清空了,然后不通过select还能查询导数据说明二级缓存确实存在
session.clear();
//如何证明二级缓存和一级缓存对象不一样,去除其中一个tostring看原始地址是否一致即可
//二级缓存缓存得是对象的详情内容,不是缓存的引用 二级换粗你的类缓存 那么集合缓存缓存的是什么呢?
//第2次查询
Country country1 = session.get(Country.class, 2);
System.out.println("country1 = " + country1);
session.clear();
//第3次查询
Country country2 = session.get(Country.class, 2);
System.out.println("country2 = " + country2);
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
}
///演示集合缓存中所缓存的内容到底是什么? 是集合对象的所有id 没有详情 所以他需要再次根据id去查询
@Test
public void test01Collection()
{
Session session = HiberUtil.getSession();
try {
session.getTransaction().begin();
//第一次查询
Country country = session.get(Country.class, 1);
Set ministers = country.getMinisters();
System.out.println("ministers.size = " +ministers.size());
//第2次查询
Country country2 = session.get(Country.class, 1);
Set ministers2= country2.getMinisters();
System.out.println("ministers.size = " +ministers2.size());
session.clear();
/*
* 类缓存对象存放在专门的一个称为实体区域的缓存中,缓存的内容为对象的详情,
* 集合缓存对象存放在专门的一个称为集合区域的缓存中,缓存的内容为集合中所包含的对象的id
*/
//第3次查询
Country country3 = session.get(Country.class, 1);
Set ministers3 = country3.getMinisters();
System.out.println("ministers.size = " +ministers3.size());
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
}
//查看文件的临时路径并输出
@Test
public void test()
{
String PATH = System.getProperty("java.io.tmpdir");
System.out.println(PATH);
}
}
运行结果截图
这些demo都是自己在学习过程中亲身时间了的,如果有问题可以在下面可以作评论,我看到了回回复的,谢谢!大家一起努力!!秋招找份8K工作