Session概述

Session是Hibernate提供的最主要的接口,它提供了基本的保存, 更新, 删除和加载 Java 对象的方法.
Session 具有一个缓存, 位于缓存中的对象称为持久化对象

1.Session缓存

1.1本质是什么?

缓存缓存,给人的第一感觉就是存储对象用的;对象在Java中要存在哪?明显的,数据结构集合里嘛。所以易得,Session缓存就是Session接口实现的Java集合。

1.1.1Session初体验
News news=session.get(News.class,1);
       System.out.println(news);

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

我们写下如下语句时,如果没有Session缓存帮我们帮,在控制台的话,我们怎么也想不到只会打印一条select语句。两条get获取对象不应该进行两次select吗?
这就是Session的功能:减少 Hibernate 应用程序访问数据库的频率。

1.2能存多久?

既然它是用来存对象的,我们不禁要问,能存多久?官方用词,生命周期如何?
答:只要 Session 实例没有结束生命周期, 且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期。

1.3怎么清理?

clear()缓存中的内容将被清空。

1.4缓存是怎样和数据库保持同步的?

添加一个缓存的确能提高效率,那么缓存又如何与数据库建立联系呢?

Session的作用域可以设置多个嘛 session里面有什么_Session的作用域可以设置多个嘛

1.4.1 flush

Session 按照缓存中对象的属性变化来同步更新数据库.
默认情况下 Session 在以下时间点刷新缓存:

  • 显式调用 Session 的 flush() 方法
  • 当应用程序调用 Transaction 的 commit()方法的时, 该方法先 flush ,然后在向数据库提交事务
  • 当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态.
  • 如果对象使用 native 生成器生成 OID, 那么当调用 Session 的 save() 方法保存对象时, 会立即执行向数据库插入该实体的 insert 语句.
    :flush方法执行一系列sql语句,但不提交事务;
      commit方法先调用flush()方法,然后提交事务,只有提交事务才意味着对数据库操作永久保存下来。
1.4.2 Reflesh

通常情况下,在进行一次查询后,由于session缓存,第二次查询时,将不再发送SQl语句,但是如果调用的refresh方法,则强制发送sql语句进行查询,以获取当前数据库记录的最新状态。
会强制发送 SELECT 语句, 以使 Session 缓存中对象的状态和数据表中对应的记录保持一致!

@Test
    public void testSessionReflesh(){
        News news=(News) session.get(News.class,1);
        System.out.println(news);
//打印News{id=1, author='sum', date=2018-09-03 19:53:26.0, title='network'}
//此时在数据库中把title改成"IT Network",借助reflesh()让Session状态和flush保持一致
        session.refresh(news);
/*打印  select
        news0_.id as id1_0_0_,
        news0_.title as title2_0_0_,
        news0_.author as author3_0_0_,
        news0_.date as date4_0_0_ 
    from
        news news0_ 
    where
        news0_.id=?
 */
        System.out.println(news);
//令人意外的是,title并没有变成"IT Network"
//仍然打印News{id=1, author='sum', date=2018-09-03 19:53:26.0, title='network'}
    }
在 Hibernate 中设置隔离级别

这是由于mysql默认的事务隔离级别是REPETABLE_READ,即可重复读,这会确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新。所以我们无法看到测试效果,解决方法是在hibernate的配置文件中设置事务的隔离级别为READ_COMMITTED(编号为2):

<!-- 设置 Hibernate 的事务隔离级别 -->
<property name="connection.isolation">2</property>

1 READ UNCOMMITED
2 READ COMMITED
4 REPEATABLE READ
8 SERIALIZEABLE

2.Session特定方法

Session的作用域可以设置多个嘛 session里面有什么_数据库_02


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

  • 临时对象(Transient):
    在使用代理主键的情况下,OID 通常为 null;
    不处于Session的缓存中;
    在数据库中没有对应的记录。
    通常使用new创建一个新的对象,并且不设置它的id属性值,则这个对象就是临时对象。
  • 持久化对象 (也叫“托管”)(Persist):
    OID不为null;
    位于Session缓存中;
    若在数据库中已经有和其对应的记录,则持久化对象和数据库中的相关记录对应;
    Session在flush缓存时,会根据持久化对象的属性变化,来同步更新数据库;
    在同一个Session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象。
  • 游离对象 (也叫”脱管”) (Detached):
    OID不为null;
    不再处于Session缓存中;
    一般情况需下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录。
  • 删除对象 (Removed):
    在数据库中没有和其OID对应的记录;
    不再处于Session 缓存中;
    一般情况下,应用程序不该再使用被删除的对象。

2.2 save()方法

Session 的 save() 方法使一个临时对象转变为持久化对象;
Session 的 save() 方法完成以下操作:
1. 把 News 对象加入到 Session 缓存中,使它进入持久化状态;
2. 选用映射文件指定的标识符生成器, 为持久化对象分配唯一的 OID。
(在使用代理主键的情况下, setId() 方法为 News 对象设置 OID 是无效的)
3. 在 flush 缓存的时候,计划执行一条 insert 语句。hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系,当 News 对象处于持久化状态时, 不允许程序随意修改它的 ID。

@Test
    public void testSave(){
        News news = new News();
        news.setTitle("CC");
        news.setAuthor("c");
        news.setDate(new Date());
    // 设置了id但是不起作用
        news.setId(300);
        System.out.println(news);
        session.save(news);
        System.out.println(news);
    //save后改id会抛异常
        news.setId(101);
    }

persist() 和 save() 区别:
当对一个OID不为Null的对象执行save()方法时,会把该对象以一个新的OID保存到数据库中; 但执行persist()方法时会抛出一个异常。

2.3 get()、load()方法

  1. 这两个方法都可以根据给定的 OID 从数据库中加载一个持久化对象;
  2. 执行 get 方法: 会立即加载对象;(立即检索)
    执行 load 方法:若不使用该对象,则不会立即执行查询操作,而返回一个代理对象,直到使用到该对象的时候才会加载。(延迟检索)
  3. 如果在需要初始化代理对象之前已经关闭了session,load方法可能会抛出 LazyInitializationException 异常(懒加载异常)。例如:下面这段代码会抛出懒加载异常:
public void testLoad(){

        News news = (News) session.load(News.class, 1);

        session.close();
        System.out.println(news); 
    }

而下面这段代码则可以正常加载:

public void testLoad(){

        News news = (News) session.load(News.class, 1);

        System.out.println(news);
        session.close();
        System.out.println(news); 
    }
  • 若数据表中不存在与OID对应的记录,且Session也没有被关闭:
     get方法会返回null;
    load方法若不使用该对象的任何属性,则不会出现问题;否则将抛出ObjectNotFoundException异常。

2.4 update()方法

  • 若更新一个持久化对象,通常不需要显示的调用update方法,因为在调用Transaction的commit()方法时,会先执行session的flush方法。
  • Session 的update()方法可以使一个游离对象转变为持久化对象,并且计划执行一条 update 语句。

2.5 saveOrUpdate()

saveOrUpdate()
首先判断保存的对象是临时对象还是游离对象,若为临时对象,则调用save()方法,若是游离对象,则调用update()方法。
判定对象为临时对象的标准:
标准一: Java 对象的 OID 是否为 null,为null是临时对象,否则是游离对象。
标准二: 如果在映射文件中为 设置了unsaved-value属性, 则OID取值等于这个unsaved-value属性值的Java对象为临时对象,否则为游离对象。

2.6 delete()

  1. Session 的delete()方法既可以删除一个游离对象,也可以删除一个持久化对象。只要 OID 和数据表中一条记录对应,就会准备执行 delete 操作,若 OID 在数据表中没有对应的记录,则抛出异常。
  2. Session 的 delete() 方法处理过程为:计划执行一条delete 语句,把对象从 Session 缓存中删除,然后该对象进入删除状态。
  3. 默认情况下,在执行delete方法后,对象的id值仍然被保存在对象中,这在某些情形下(例如又对该对象执行saveOrUpdate()方法)会很不方便。Hibernate的配置文件hibernate.cfg.xml中有一个hibernate.use_identifier_rollback属性,其默认值为false,若把它设为true,则在执行delete()方法后,对象的id值将被置为null。