Hibernate学习笔记 | Session详解

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缓存中
    一般情况下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录

对象的转换图

13424350-7806d35599ce5c09.png
四种对象之间转换图

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()的功能


    13424350-2ce31e8b075a7922.png
    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语句

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值