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包
环境搭建总体结构
实体类 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<Minister> ministers;

    public Country() {
        ministers = new HashSet<Minister>();
    }
    public Country(String cname) {
        this();    //直接去执行前面的   ministers = new HashSet<Minister>();
        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<Minister> getMinisters() {
        return ministers;
    }
    public void setMinisters(Set<Minister> 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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.vrv.yinkailong.bean">
    <class name="Country">
    <!-- 并发访问策略   制定该类为二级缓存对象  即类缓存 -->
 <!--    <cache usage="read-only"/> -->
          <id name="cid">
          <generator class="native"/>
          </id>
    <property name="cname"/>
    <!-- cascade="save-uodate"  才能执行级联操作  -->
    <set name="ministers" cascade="save-update"  fetch="select" lazy="true" >
    <!-- 为当前集合制定缓存       指定该集合为缓存对象  即 集合缓存 -->
  <!--   <cache usage="read-only"/> -->
    <!-- 指明当前主键 -->
    <key column="countrId"/>
    <!-- 指明对应关系 -->
    <one-to-many  class="Minister"/>

    </set>
    </class>
    </hibernate-mapping>

映射文件 com/vrv/yinkailong/bean/Minister.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping package="com.vrv.yinkailong.bean">
    <class name="Minister">
    <!-- <cache usage="read-only"/> -->
          <id name="mid">
          <generator class="native"/>
          </id>
    <property name="mname"/>
    </class>
    </hibernate-mapping>

总体的配置文件

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <!-- session的不同含义   http:session (view层)   hibernate :session(DAO层)   MyBatis: session(SQL  session) -->
    <!-- 三大数据库  Mysql  DB2 Oracal -->
   <hibernate-configuration>
                  <session-factory>
                        <!-- DB链接四要素 -->
                        <property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
                        <property name="hibernate.connection.url">jdbc:mysql://localhost/test</property><!--  ///    相当于127.0.0.1:3306 -->
                        <property name="hibernate.connection.username">root</property>
                        <property name="hibernate.connection.password">123456</property>
                      <!-- 需要指定方言,才能生成相应的sql 语句  去hibernate core 里面找相应的版本  -->
                       <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
                       <!-- 数据源  数据库连接池   链接数据库的那个容器我们成为数据库连接池 决定啥时候创建链接   :一般是先建立好,然后用户需要使用的时候直接从数据库连接池获取-->
                       <!--占用资源 :释放连接    连接个数  当达到下限需要再创建   然后释放之后需要设置最多放置多少条(上限)  空闲时间 :空闲多久会自动进行销毁   使用第三方机构的连接池-->
                       <!-- 链接提供者 -->
                        <property name="hibernate.co  nnection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
                          <!-- 保证在当前线程上下文中获取到的session是同一个session -->
                        <property name="hibernate.current_session_context_class">thread</property>

                        <!-- 配置自动建表   DDL数据库定义语言  DML数据库操作语言 DQL  数据源查询语言  create 每运行一次都会重新见一个表  update只会做更新 -->
                        <property name="hibernate.hbm2ddl.auto">update</property>
                        <!-- 调试常用  :显示SQL -->
                         <property name="hibernate.show_sql">true</property>
                         <!-- 生成sql语言进行格式化 -->
                         <property name="hibernate.format_sql">true</property>

                         <!-- 开启二级缓存 -->
                         <property name="hibernate.cache.use_second_level_cache">true</property>
                         <!-- 专门给二级缓存开辟了一个空间  注册二级缓存区域工厂    hibernate.cache.region.factory_class    在二级缓存的时候应该去除   .class   否则 会报错-->
                         <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>



                        <!-- 映射文件 -->
                        <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 里面解压之后直接拷贝即可 前面注释省略

    <!--
    Mandatory Default Cache configuration. These settings will be applied to caches
    created programmtically using CacheManager.add(String cacheName)
    最多可以放多少个二级对象   是否永久   最长空闲时间s    最长生存时间s    溢出之后存到硬盘    应用技术 硬盘是否存放二级数据   硬盘到期线程时间s
 内存存储驱除策略   LRU    最近最少使用算法   FIFO 先进先出    LFU  使用频率最小    LRU  未被使用时间最少        overflowToDisk="true"                   diskPersistent="false"-->
 <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<Minister> ministers = country.getMinisters();

            System.out.println("ministers.size = " +ministers.size());




            //第2次查询
            Country country2 = session.get(Country.class, 1);

            Set<Minister> ministers2= country2.getMinisters();

            System.out.println("ministers.size = " +ministers2.size());
            session.clear();

            /*
             * 类缓存对象存放在专门的一个称为实体区域的缓存中,缓存的内容为对象的详情,
             * 集合缓存对象存放在专门的一个称为集合区域的缓存中,缓存的内容为集合中所包含的对象的id
             */
            //第3次查询
            Country country3 = session.get(Country.class, 1);

            Set<Minister> 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);
    }
}

运行结果截图
验证二级缓存集合所放数据    全体id结果截图
这些demo都是自己在学习过程中亲身时间了的,如果有问题可以在下面可以作评论,我看到了回回复的,谢谢!大家一起努力!!秋招找份8K工作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值