• hibernate知识整理


    为什么要使用
    ①.对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。 
    ②.Hibernate 是一个基于JDBC的主流持久化框架,是一个优秀的ORM 实现。他很大程度的简化DAO层的编码工作 
    ③.hibernate 的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
     

    什么是延迟加载?

        延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。在Hibernate中提供了对实体对象的延迟加载以及对集合的延迟加载,另外在Hibernate3中还提供了对属性的延迟加载。
     
    hibernate一级缓存 
    (1)hibernate支持两个级别的缓存,默认只支持一级缓存; 
    (2)每个Session内部自带一个一级缓存; 
    (3)某个Session被关闭时,其对应的一级缓存自动清除; 
    hibernate二级缓存
    (1) 二级缓存独立于session,默认不开启;

    Hibernate的查询方式有哪些?

    本地SQL查询、Criteria、Hql
     

    如何优化Hibernate?

    1.使用双向一对多关联,不使用单向一对多
    2.灵活使用单向一对多关联
    3.不用一对一,用多对一取代
    4.配置对象缓存,不使用集合缓存
    5.一对多集合使用Bag,多对多集合使用Set
    6. 继承类使用显式多态
    7. 表字段要少,表关联不要怕多,有二级缓存撑腰 
     

    Hibernate中GET和LOAD的区别?

        请注意如果没有匹配的数据库记录,load()方法可能抛出无法恢复的异常(unrecoverable exception)。 如果类的映射使用了代理(proxy),load()方法会返回一个未初始化的代理,直到你调用该代理的某方法时才会去访问数据库。若你希望在某对象中创建一个指向另一个对象的关联,又不想在从数据库中装载该对象时同时装载相关联的那个对象,那么这种操作方式就用得上的了。 如果为相应类映射关系设置了batch-size, 那么使用这种操作方式允许多个对象被一批装载(因为返回的是代理,无需从数据库中抓取所有对象的数据)。 如果你不确定是否有匹配的行存在,应该使用 get()方法,它会立刻访问数据库,如果没有对应的行,会返回null。
        session.get 方法, 查询立即执行 , 返回Customer类对象
        session.load 方法,默认采用延迟加载数据方式,不会立即查询,返回 Customer类子类对象 (动态生成代理对象)
    * 如果 PO类使用final修饰,load无法创建代理对象,返回目标对象本身 (load效果和 get效果 相同 ) 
     

    SQL和HQL有什么区别?

    sql 面向数据库表查询 
    hql 面向对象查询 
     
    inverse和cascade的区别 
    作用的范围不同: 
        Inverse是设置在集合元素中的。 
        Cascade对于所有涉及到关联的元素都有效。 
        <many-to-one/><ont-to-many/>没有inverse属性,但有cascade属性 
    执行的策略不同 
        Inverse 会首先判断集合的变化情况,然后针对变化执行相应的处理。 
        Cascade 是直接对集合中每个元素执行相应的处理 
    执行的时机不同 
        Inverse是在执行SQL语句之前判断是否要执行该SQL语句 
        Cascade则在主控方发生操作时用来判断是否要进行级联操作 
    执行的目标不同 
        Inverse对于<ont-to-many>和<many-to-many>处理方式不相同。 
        对于<ont-to-many>,inverse所处理的是对被关联表进行修改操作。 
        对于<many-to-many>,inverse所处理的则是中间关联表 
        Cascade不会区分这两种关系的差别,所做的操作都是针对被关联的对象。 
    总结:  
    <one-to-many> 
        <one-to-many>中,建议inverse=”true”,由“many”方来进行关联关系的维护 
        <many-to-many>中,只设置其中一方inverse=”false”,或双方都不设置 
        Cascade,通常情况下都不会使用。特别是删除,一定要慎重。 
    操作建议:
        一般对many-to-one和many-to-many不设置级联,这要看业务逻辑的需要;对one-to-one和one-to-many设置级联。 
        many-to-many关联关系中,一端设置inverse=”false”,另一端设置为inverse=”true”。在one-to-many关联关系中,设置inverse=”true”,由多端来维护关系表
     

    Hibernate一级缓存相关问题

    1.Session中的一级缓存
        Hibernate框架共有两级缓存, 一级缓存(Session级别缓存)、二级缓存(SessionFactory级别缓存)
        在Session接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存.  持久化对象保存Session一级缓存中(一级缓存引用持久化对象地址),只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
        Hibernate Session接口的实现类SessionImpl类(查看源码,右击session,选择Open Type Hierarchy) ,里面有2个重要的字段:
    *private transient ActionQueue actionQueue;                       ----行动队列(标记数据活动)
    *private transient StatefulPersistenceContext persistenceContext;----持久化上下文 
        当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库
        Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷出缓存(flush)
            *  Transaction的commit()
            *  应用程序执行一些查询操作时
            *  调用Session的flush()方法
     
        ①验证一级缓存的存在
    Book book =(Book) session.get(Book.class,1);// 第一次查询,缓存中没有
    System.out.println(book);
     
    Book book2 =(Book) session.get(Book.class,1);// 因为第一次查询,对象已经被放入1级缓存,不会查询数据
    System.out.println(book2);
     
    *生成一条SQL语句,返回同一个对象,第一次查询生成SQL,查询对象,将对象放入一级缓存,第二次查询,直接从一级缓存获得
        
     
     
        ②测试Hibernate快照 (深入理解一级缓存内存结构原理)
        hibernate 向一级缓存放入数据时,同时保存快照数据(数据库备份),当修改一级缓存数据,在flush操作时,对比缓存和快照,如果不一致,自动更新(将缓存的内容同步到数据库,更新快照)
     
    *  快照区使用,在Session 保存一份与数据库相同的数据,在session的flush时, 通过快照区比较得知一级缓存数据是否改变,如果改变执行对应操作(update)
    /**
    * 测试快照区的使用
      */
    @Test
    publicvoid demo3(){
        // 获得Session
    Session session =HibernateUtils.openSession();
    // 开启事务
    Transaction transaction = session.beginTransaction();
     
    // 查询id 为1 的图书对象
    Book book =(Book) session.get(Book.class,1);// 第一次查询,将对象加入一级缓存
    System.out.println(book);
     
    book.setName("深入浅出Hibernate技术");// 修改书名(一级缓存被修改,自动update)
     
    // 没有手动执行update,因为快照区原因,自动更新
     
    // 提交事务,关闭Session
    transaction.commit();
    session.close();
    }
        使用Debug模式进行截图说明:
        我们重点关注session中的2个属性actionQueue和persistenceContext
     
        大白话解析:
            **当执行get后,缓存里面有数据了,活动队列没有发生变化,说明没有需要提交到数据的内容,而PersistenceContext里面有内容了。
                我们说,持久化上下文里面存放的是一个Map,它的键为一级缓存对象,值为快照(它是一级缓存对象的一个副本)。 
            **当执行setName后,一级缓存里面的数据发生了改变,在缓存flush时,会对比缓存和快照,如果不一致,那么会将缓存中的内容同步到数据库,并更新快照!
     
    *  Hibernate中 持久态 对象具有自动更新数据库能力 (持久态对象 才保存在 Session中,才有快照 )
     
    2.一级缓存常见操作
        所有操作需要使用断点调试才能看得比较清楚!
        1)flush : 修改一级缓存数据针对内存操作,需要在session执行flush操作时,将缓存变化同步到数据库
         * 只有在缓存数据与快照区不同时,生成update语句
        2)clear : 清除所有对象 一级缓存
        3)evict : 清除一级缓存指定对象
        4)refresh :重新查询数据库,更新快照和一级缓存
    @Test
    // Session 对于 一级缓存操作
    publicvoid demo4(){
    // 获得Session
    Session session =HibernateUtils.openSession();
    // 开启事务
    Transaction transaction = session.beginTransaction();
     
    // 查询id 为1 的图书对象
    Book book =(Book) session.get(Book.class,1);// 第一次查询,将对象加入一级缓存
    System.out.println(book);
     
    // book.setPrice(80d); // 修改一级缓存数据
    // 将缓存内容同步到数据库
    // session.flush();
     
    // 清除一级缓存所有数据
    // session.clear();
     
    // 清除一级缓存中 指定对象
    // session.evict(book);
     
    book.setPrice(30d);// 一级缓存改变
    session.refresh(book);// 用数据库内容 覆盖快照区和一级缓存
     
    // 提交事务,关闭Session
    transaction.commit();
    session.close();
    }
     
    3.一级缓存刷出时间点设置 (FlushMode)
    ALWAYS :在每次查询时,session都会flush  (不要求 )
    AUTO   : 在有些查询时,session会flush  (默认)  ---------- 查询、commit 、session.flush
    COMMIT : 在事务提交时,session会flush   ------- commit 、session.flush
    MANUAL :只有手动调用  session.flush 才会刷出  ----  session.flush
     
    刷出条件(时间点严格程度 )
    MANUAL > COMMIT> AUTO> ALWAYS
    @Test
    // 理解 FlushMode作用
    publicvoid demo5(){
    // 获得Session
    Session session =HibernateUtils.openSession();
     
    // 设置 flushMode
    session.setFlushMode(FlushMode.MANUAL);
     
    // 开启事务
    Transaction transaction = session.beginTransaction();
     
    // 查询id 为1 的图书对象
    Book book =(Book) session.get(Book.class,1);// 第一次查询,将对象加入一级缓存
    System.out.println(book);
     
    book.setPrice(1000d);// 修改价格
     
    session.createQuery("from Book").list();// 查询所有图书 (AUTO 级别 flush)
     
    // 提交事务,关闭Session
    transaction.commit();// (COMMIT 级别 flush)
     
    // session.flush(); // MANUAL 级别 flush
     
    session.close();
    }
     
    4.session持久化对象操作方法
        1) save 将数据保存到数据库 , 将瞬时对象转换持久对象 
    持久化对象,不允许随便修改 OID
     
        2) update 更新数据 ,主要用于脱管对象的更新(持久对象,可以根据快照自动更新 ), 将脱管对象转换持久对象        
    @Test
    // 脱管对象更新
    publicvoid demo6(){
    // 获得Session
    Session session =HibernateUtils.openSession();
    // 开启事务
    Transaction transaction = session.beginTransaction();
     
    Book book =newBook();// 瞬时
    book.setId(1);// 脱管
    book.setName("java入门");
    book.setPrice(40d);
     
    session.update(book);// 持久
     
    session.flush();
     
    // book.setPrice(50d);
     
    // 提交事务,关闭Session
    transaction.commit();
    session.close();
    }
            问题一: 调用update,默认直接生成update语句,如果数据没有改变,不希望生成update
        在hbm文件 <class>元素 添加 select-before-update="true"
    <classname="cn.itcast.domain.firstcache.Book"table="book"catalog="hibernate3day2"select-before-update="true">
    问题二: 当update,脱管对象变为持久对象, 一级缓存不允许出现相同OID 两个持久对象
    @Test
    // 一级缓存 存在两个相同OID 持久态对象 报错
    publicvoid demo7(){
    // 获得Session
    Session session =HibernateUtils.openSession();
    // 开启事务
    Transaction transaction = session.beginTransaction();
     
    // 查询
    // Book b = (Book) session.get(Book.class, 1); // 持久
     
    Book book =newBook();// 瞬时
    book.setId(1);// 脱管
    book.setName("java入门");
    book.setPrice(50d);
    session.update(book);// 持久
     
    // 提交事务,关闭Session
    transaction.commit();
    session.close();
    }
        org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [cn.itcast.domain.firstcache.Book#1]
    问题三: 脱管对象 OID 在数据表中不存在,update时,发生异常
        org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [cn.itcast.domain.firstcache.Book#20]
     
        3) saveOrUpdate , 如果参数是一个瞬时对象执行save, 如果参数是一个脱管对象执行update, 如果参数是持久对象直接返回
    判断对象是瞬时对象 : OID为null , 在hbm文件中为 <id>元素指定 unsaved-value属性,如果PO对象OID为 unsaved-value 也是瞬时对象
    <id name="id" unsaved-value="-1">  如果对象 OID为-1 也是瞬时对象,此时执行的是save操作
    @Test
    // PO对象,OID为 hbm文件 配置 unsaved-value 也是瞬时对象, saveOrUpdate 执行 save操作
    publicvoid demo8(){
    // 获得Session
    Session session =HibernateUtils.openSession();
    // 开启事务
    Transaction transaction = session.beginTransaction();
     
    Book book =newBook();// 瞬时
    book.setId(-1);// 存在OID , -1是unsaved-value 也是瞬时
    book.setName("xxx");
    book.setPrice(100d);
     
    session.saveOrUpdate(book);
     
    // 提交事务,关闭Session
    transaction.commit();
    session.close();
    }
     
        4) get/load
            如果查询OID不存在, get方法返回 null , load 方法返回代理对象 (代理对象初始化时抛出 ObjectNotFoundException )
     
        5) delete 方法既可以删除一个脱管对象, 也可以删除一个持久化对象
            **如果删除脱管,先将脱管对象 与 Session 关联,然后再删除
    **执行delete,先删除一级缓存数据,在session.flush 操作时,删除数据表中数据
     

    Hibernate二级缓存相关问题

    1.二级缓存的相关介绍

    缓存好处: 将数据库或者硬盘数据,保存在内存中,减少数据库查询次数,减少硬盘交互,提高检索效率

        hibernate 共有两个级别的缓存

            * 一级缓存,保存Session中, 事务范围的缓存

            * 二级缓存,保存SessionFactory ,进程范围的缓存

        SessionFacoty 两部分缓存

        内置 :Hibernate 自带的, 不可卸载. 通常在 Hibernate 的初始化阶段, Hibernate 会把映射元数据和预定义的 SQL 语句放到SessionFactory 的缓存中, 映射元数据是映射文件中数据的复制, 而预定义 SQL 语句时 Hibernate 根据映射元数据推到出来的. 该内置缓存是只读的.

        外置 :一个可配置的缓存插件. 在默认情况下, SessionFactory 不会启用这个缓存插件. 外置缓存中的数据是数据库数据的复制, 外置缓存的物理介质可以是内存或硬盘,必须引入第三方缓存插件才能使用。

     
    2.二级缓存的内部结构以及存储特点
    Hibernate二级缓存分为:   
        * 类缓存区域
        * 集合缓存区域 
        * 更新时间戳区域 
        * 查询缓存区域
     
     
    类缓存区数据存储特点
    * 从二级缓存区返回数据每次地址都是不同的(散装数据 )。每次查询二级缓存,都是将散装数据构造为一个新的对象 
     
    集合缓存区
     
    如果注释掉 Order类缓存,orders 集合无法缓存 
    * 集合缓存区数据缓存依赖类缓存区数据缓存

    ** 一级缓存的操作会同步到二级缓存

    更新时间戳区域

    作用:记录数据最后更新时间,确保缓存数据是有效的

        Hibernate 提供了和查询相关的缓存区域:

        **时间戳缓存区域: org.hibernate.cahce.UpdateTimestampCache 
        时间戳缓存区域存放了对于查询结果相关的表进行插入, 更新或删除操作的时间戳.  Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期, 其运行过程如下: 
        T1 时刻执行查询操作, 把查询结果存放在 QueryCache 区域, 记录该区域的时间戳为 T1 
        T2 时刻对查询结果相关的表进行更新操作, Hibernate 把 T2 时刻存放在 UpdateTimestampCache 区域. 
        T3 时刻执行查询结果前, 先比较 QueryCache 区域的时间戳和 UpdateTimestampCache 区域的时间戳, 若 T2 >T1, 那么就丢弃原先存放在 QueryCache 区域的查询结果, 重新到数据库中查询数据, 再把结果存放到 QueryCache 区域; 若 T2 < T1, 直接从 QueryCache 中获得查询结果。
     
    **更新时间戳区域,记录数据最后更新时间,在使用二级缓存时,比较缓存时间t1 与更新时间 t2 , 如果 t2 > t1 丢弃原来缓存数据,重新查询缓存
    查询缓存
        有人称查询缓存 为hibernate 第三级缓存

    * 二级缓存缓存数据都是类对象数据,数据都是缓存在 "类缓存区域" ,二级缓存缓存PO类对象,条件(key)是id

        查询缓存适用场合: 

            **应用程序运行时经常使用查询语句

            **很少对与查询语句检索到的数据进行插入, 删除和更新操作

        如果查询条件不是id查询, 缓存数据不是PO类完整对象 =====> 不适合使用二级缓存

    查询缓存: 缓存的是查询数据结果, key是查询生成SQL语句  , 查询缓存比二级缓存功能更加强大

     
    适用查询缓存的步骤
        1)配置二级缓存(查询缓存依赖二级缓存)

        2)启用查询缓存 hibernate.cfg.xml

     

    <property name="hibernate.cache.use_query_cache">true</property>
        3)必须在程序中指定使用查询缓存
    query.setCacheable(true);
     
    3.二级缓存的并发策略

        transactional : 提供Repeatable Read事务隔离级别,缓存支持事务,发生异常的时候,缓存也能够回滚

        read-write    : 提供Read Committed事务隔离级别,更新缓存的时候会锁定缓存中的数据

        nonstrict-read-write :导致脏读, 很少使用

        read-only     : 数据不允许修改,只能查询

    * 很少被修改,不是很重要,允许偶尔的并发问题, 适合放入二级缓存。考虑因素(二级缓存的监控【后面学习】,它是是否采用二级缓存主要参考指标)

    4.Hibernate支持哪些二级缓存技术? 
        *  EHCache  (主要学习,支持本地缓存,支持分布式缓存)
            可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持。 
        *  OSCache 
            可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持 
        *  SwarmCache 
            可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存 
        *  JBossCache 
            可作为集群范围内的缓存, 支持 Hibernate 的查询缓存
  • 相关阅读:
    VC++中对数据类型的限制limits.h文件内容
    阿里巴巴-2015秋招研发工程师附加题
    阿里巴巴-2015秋招研发工程师附加题
    如何成为一个牛逼的程序猿
    Windows7中Java64位环境变量配置:javac不是内部命令或外部命令,也不是可运行的程序或批处理文件。
    程序员面试宝典:求十进制数字的二进制数位中包含1的个数
    程序员面试宝典:与或数值运算
    docker配置阿里云镜像
    原生JS中获取位置的方案总结
    vue项目中上拉加载和下拉刷新页面的实现
  • 原文地址:https://www.cnblogs.com/applemy/p/6993545.html
Copyright © 2020-2023  润新知