美文网首页Java
Hibernate学习笔记 | Session详解

Hibernate学习笔记 | Session详解

作者: 一颗白菜_ | 来源:发表于2019-08-09 19:42 被阅读2次

    Session概述

    • Session提供了基本的保存,更新,删除和加载Java对象的方法。

    • Session具有一个缓存,位于缓存中的对象称为持久化对象,它和数据库中的相关记录对应。

    • 站在持久化的角度,Hibernate把对象分为4种状态,持久化状态,临时状态,游离状态,删除状态。Session的特定方法能使对象从一个状态转换到另一个状态。

    Session缓存

    • 在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存,只要Session实例没有结束生命周期,且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期。

    • Session缓存可减少Hibernate应用程序访问数据库的频率。

    假设有代码如下:

    News news = session.get(News.class,1);
    System.out.println(news);
    News news1 = session.get(News.class,1);
    System.out.println(news1);
    

    上述代码会输出news对象和news1对象,但是只会打印一次SQL语句。
    第一个News对象会去Session缓存中查找有没有要查找对象的引用,没有就去查数据库。查完数据库就把该对象的引用存入Session缓存,然后第二个News对象去Session查的时候,发现有待查找对象的引用,即不用去数据库中查询了。

    操作Session缓存的三个方法

    • flush():下面细说。
    • refresh():会强制发送SELECT语句,以使Session缓存中对象的状态和数据表中对应的记录保持一致。
      *clear():清理Session缓存。

    flush缓存

    • flush:使数据表中的记录和Session缓存中的对象的状态保持一致。为了保持一致,则可能会发送对应的SQL语句。
      Transactioncommit()方法中,先调用sessionflush方法,再提交事务。
      flush()方法可能会发送SQL语句,不会提交事务。只有等事务提交后。

    • 在未提交事务或显式的调用session.flush()方法之前,也有可能进行flush()操作的情况如下:
      执行HQL或QBC查询,会先进行flush操作,以得到数据表的最新的记录。

    • flush操作的例外情况
      若记录的ID是由底层数据库使用自增的方式生成的,则在调用save()方法时,就会立即发送INSERT语句,因为save()后,必须保证对象的ID是存在的。

    • commit()flush()的区别
      flush()执行一系列sql语句,但不会提交事务;而commit()先调用flush(),然后再提交事务。所以提交事务意味着对数据库操作永久保存下来。

    持久化对象的状态

    • 临时对象
      在使用代理主键的情况下,OID通常为null
      不处于Session的缓存中
      在数据库中没有对应的记录

    • 持久化对象(也叫“托管”)
      OID不为null
      位于Session缓存中
      若在数据库中已经有和其对应的记录,持久化对象和数据库中的相关记录对应
      Session在flush缓存时,会根据持久化对象的属性变化,来同步更新数据库
      在同一个Session实例的缓存中,数据库表中的每一条记录只对应唯一的持久化对象

    • 删除对象
      在数据库中没有和其OID对应的记录
      不再处于Session缓存中
      一般情况下,应用程序不该再使用被删除的对象

    • 游离对象(也叫“脱管”)
      OID不为null
      不再处于Session缓存中
      一般情况下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录

    对象的转换图

    四种对象之间转换图

    Session中save()方法

    • 使一个临时对象变为持久化对象
    • 为对象分配id
    • 在flush缓存时会发送一条INSERT语句。
    • save()之前设置的id是无效的,而在save()之后的持久化对象的id是不能被修改的。

    Session中persist()方法

    • save()一样也会执行INSERT
    • 在调用persist()之前,如果对象已经有id了,则不会执行INSERT,而抛出异常

    Session中的get()和load()

    • 执行get()会立即加载对象,也可以说是立即检索;而执行load()时,若不使用该对象则不会立即执行查询操作,而返回一个代理对象,也可以说是延迟检索。

    • load()可能会抛出LazyInitializationException异常:在需要初始化代理对象之前已经关闭了Session。

    • 若数据表中没有对应的记录,get()返回nullload()若不使用该对象的任何属性就不会抛出异常,如果需要使用该对象就会抛出异常

    Session中的update()方法

    • 若更新一个持久化对象,不需要显式地调用update(),因为在调用Transactioncommit()时,会先执行sessionflush()
    • 若更新一个游离对象,则需要显式地调用update(),可以把一个游离对象变为一个持久化对象。
    • 对于游离对象,无论要更新的游离对象和数据表的记录是否一致,都会发送UPDATE语句;对于持久化对象,如果要更新的和数据表的记录一致,则不发送UPDATE语句,如果不一致,就发送UPDATE语句。
      .hbm.xml文件中的class节点设置select-before-update=true就不会使update()不再盲目的触发UPDATE语句,但是通常不需要设置该属性。
    <hibernate-mapping>
        <!--加入select-before-update="true" -->
        <class name="com.cerr.hibernate.helloworld.News" table="news" schema="hibernate5" select-before-update="true">
            <id name="id" column="id"/>
            <property name="title" column="title"/>
            <property name="author" column="author"/>
            <property name="date" column="date"/>
        </class>
    </hibernate-mapping>
    
    • 若数据表中没有对应的记录,但还调用了update(),就会抛出异常
    • update()关联一个游离对象时,如果在Session的缓存中已经存在相同OID的持久化对象,会抛出异常。因为在Session缓存中不能有两个OID相同的对象。

    Session中的saveOrUpdate()

    • Session的saveOrUpdate()同时包含了save()与update()的功能


      image.png
    • 判定对象为临时对象的标准
      Java对象的OID为null
      映射文件中为<id>设置了unsaved-value属性,并且java对象的OID取值与这个unsaved-value属性值匹配。

    • 注意
      若OID不为null,但数据表中还没有和其对应的记录,则会抛出一个异常

    Session中的delete()

    • 只要OID和数据表中一条记录对应,就会准备执行delete(),若OID在数据表中没有对应的记录,则抛出异常

    • 可以通过设置hibernate配置文件use_identifier_rollbacktrue,使删除对象后,把其OID置为null。

    Session中的evict()

    从Session缓存中把指定的持久化对象移除。
    示例:

    package com.cerr.hibernate.helloworld;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.util.Date;
    
    public class NewsTestTest {
        private SessionFactory sessionFactory;
        private Session session;
        private Transaction transaction;
    
        @Before
        public void init() throws Exception {
            Configuration configuration = new Configuration().configure();
            sessionFactory = configuration.buildSessionFactory();
            session = sessionFactory.openSession();
            transaction = session.beginTransaction();
        }
    
        @After
        public void destory() throws Exception {
            transaction.commit();
            session.close();
            sessionFactory.close();
        }
    
        @Test
        public void testEvict(){
            News news = session.get(News.class,1);
            News news1 = session.get(News.class,2);
            session.evict(news);
            news.setAuthor("aa");   //不会修改
            news1.setAuthor("bb");  //会修改
        }
    

    Hibernate与触发器协同工作产生的问题

    • 问题1:触发器使Session的缓存中的持久化对象与数据库中对应的数据不一致,触发器运行在数据库中,他执行的操作对Session是透明的。
      解决:在执行完Session的相关操作后,立即调用Session的flush()和refresh(),迫使Session的缓存与数据库同步。

    • 问题2:Session的update()盲目地激发触发器,无论游离对象的属性是否发生变化,都会执行update语句,而update语句会激发数据库中相应的触发器。
      解决:在映射文件的<class>元素中设置select-before-update属性,当Session的update()saveOrUpdate()更新一个游离对象时,会先执行SELECT语句,获得当前游离对象在数据库中的最新数据,只有在不一致的情况下才会执行UPDATE语句

    相关文章

      网友评论

        本文标题:Hibernate学习笔记 | Session详解

        本文链接:https://www.haomeiwen.com/subject/hxzcjctx.html