Hibernate学习3 二级缓存和延迟加载

迫切左连接
//查询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包
Hibernate学习3 二级缓存和延迟加载_第1张图片
实体类 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);
    }
}

运行结果截图
Hibernate学习3 二级缓存和延迟加载_第2张图片
这些demo都是自己在学习过程中亲身时间了的,如果有问题可以在下面可以作评论,我看到了回回复的,谢谢!大家一起努力!!秋招找份8K工作

你可能感兴趣的:(Hibernate学习,hibernate,二级缓存,select,延迟加载)