Hibernate---Session讲解

Session概述

  • Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载 Java 对象的方法.
  • Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应. Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush)
  • 站在持久化的角度, Hibernate 把对象分为 4 种状态: 持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态.
  • Session缓存

在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期
Session 缓存可减少 Hibernate 应用程序访问数据库的频率。

  • 编写一个测试程序
package mao.shu.vo;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;



public class NewsTest {
    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;

    @Before
    public void init(){
        Configuration configuration = new Configuration().configure();
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties())
                .buildServiceRegistry();
        this.sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        this.session = this.sessionFactory.openSession();
        this.transaction = this.session.beginTransaction();
    }
    @Test
    public void add(){
        News vo = new News(1,"测试2","程序测试2",new Date(),200.5);
        this.session.save(vo);

    }
    @Test
    public void get(){
        News vo = (News) this.session.get(News.class,1);
        System.out.println(vo);

        News temp = (News) this.session.get(News.class,1);
        System.out.println(temp);
    }
    @After
    public void Destroy(){
        this.transaction.commit();
        this.session.close();
        this.sessionFactory.close();
    }
}
  • 其中的get()方法之中,进行了查询操作,想news表中查询id为1的数据,并输出查询到的数据
  • get()方法之中对id为1的数据进行了两次查询查询并输出打印操作,那么应该控制台会输出两次select的语句
  • 但是发现控制台只输出了一次查询语句.

在这里插入图片描述

  • 这是因为当Session对象的生命周期还没有结束的时候,在查询相同的数据对象,会现在缓存之中寻找,如果有找到就直接使用缓存中的数据.

在这里插入图片描述

  • 操作Session缓存

在这里插入图片描述

  • flush:Session 按照缓存中对象的属性变化来同步更新数据库
    默认情况下 Session 在以下时间点刷新缓存:
  1. 显式调用 Session 的 flush() 方法
  2. 当应用程序调用 Transaction 的 commit()方法的时, 该方法先 flush ,然后在 向 数据库提交事务
  3. 当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态
  • 测试flush()方法
    @Test
    public void testFlush(){
        News vo = (News) this.session.get(News.class,1);
        vo.setInfo("flush()方法测试");
        this.session.flush();//调用flush()方法
        System.out.println(vo);
    }
  • 控制台输出结果
  • 可以发现当调用了修改了缓存之中的对象,在调用了flush()方法之后,会执行update语句以来保证缓存之中的数据和数据库之中的数据保持同步.

在这里插入图片描述

  • 数据库之中保存的数据

在这里插入图片描述

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

  • 注意: 若在为提交事务的时候或显示的调用session.flush()方法之前,也有可能会进行flush()操作.

    1. 执行HQL或QBC查询,会先进行flush()操作,已得到数据表的最新记录
    2. 若记录的id是由底层数据库使用自增的方式生成的,则在调用save方法之后,就会立即发送INSERT语句,因为save方法后,必须保证对象的id是存在.
  • 测试reflush()

    @Test
    public void testReFlush(){
        News vo = (News) this.session.get(News.class,1);
        /*使用断点,暂停程序,暂停程序的时候修改数据库中的数据,查看最后输出的内容是否有变化*/
        this.session.refresh(vo);
        System.out.println(vo);
    }

在这里插入图片描述

  • 在暂停的时候修改数据库中的info数据
    在这里插入图片描述

  • 但是会发现最后输出的结果并不是修改过的数据

在这里插入图片描述

  • 这是因为在Mysql数据库之中默认的隔离级别为 “REPETABLE READ” 可重读的,

在这里插入图片描述

  • 可以通过在Hibernate的映射文件之中设置mysql的隔离级别
  • 在hibernate.cfg.xml文件之中设置数据库的隔离级别.
 <!--设置数据库的隔离级别-->
 <property name="connection.isolation">2</property>

在这里插入图片描述

  • 重新断点修改数据

在这里插入图片描述

  • 最后输出最新的数据

在这里插入图片描述

  • reflush()会强制发送SELECT语句,以使Session缓存中的状态和数据表中对应的记录保持一致.

Session核心方法1

在这里插入图片描述

  • 测试save()方法
  • save()方法最大的特点就是将一个临时对象变为持久化对象
  • 为对象分配ID
    @Test
    public void testSave(){
        News vo = new News();
        vo.setTitle("testSave()");
        vo.setInfo("测试Save");
        vo.setPrice(55.0);

        System.out.println(vo);
        this.session.save(vo);
        System.out.println(vo);
    }

在这里插入图片描述

  • 在flush()缓存时会发送一条INSERT语句.
  • 在save()执行前,设置ID的方法是无效的.
    @Test
    public void testSave(){
        News vo = new News();
        vo.setTitle("testSave()");
        vo.setInfo("测试Save");
        vo.setPrice(55.0);
        
        //在save()方法执行前设置ID
        vo.setId(12345);
        
        System.out.println(vo);
        this.session.save(vo);
        System.out.println(vo);
    }

在这里插入图片描述

  • 持久化对象的ID是不能够被修改的.
    @Test
    public void testSave(){
        News vo = new News();
        vo.setTitle("testSave()");
        vo.setInfo("测试Save");
        vo.setPrice(55.0);

        System.out.println(vo);
        this.session.save(vo);
        //在save()方法执行过后修改id
        vo.setId(12345);
        System.out.println(vo);
    }
  • 如果在save()方法执行过后再次修改id值,就会出现要给异常
    在这里插入图片描述

  • 测试Presisit()方法

  • persist()方法也会执行INSERT操作.

  • 在presist()方法执行之前,如果对象已经有ID值,则不会执行INSERT,而会抛出一个异常.

    @Test
    public void testPersist(){
        News vo = new News();
        vo.setTitle("testSave()");
        vo.setInfo("测试Save");
        vo.setPrice(55.0);

        vo.setId(12345);

        System.out.println(vo);
        this.session.persist(vo);
        System.out.println(vo);
    }

在这里插入图片描述

  • 测试get()方法和load()方法
  1. 执行get()方法会立即加载对象,而执行load方法,若不使用该对象,则不会立即执行查询操作,而返回一个代理对象.

get 是立即检索,load 是延迟检索

    @Test
    public void testGet(){
        News getVo = (News)this.session.get(News.class,1);

        System.out.println(getVo.getClass().getName());
    }
  • testGet()方法的执行结果

在这里插入图片描述

    @Test
    public void testLoad(){
        News  loadVo = (News)this.session.load(News.class,1);

        System.out.println(loadVo.getClass().getName());
    }
  • tstLoad()方法的执行结果,可以发现使用load()方法得到的对象在没有使用的情况下是一个代理对象,这是因为使用load()方法得到的对象,在没有使用任何属性的情况下,不会立即加载,而是加载到内存之中,在有需要的时候载家在对象.
    在这里插入图片描述
  1. 若查询一个数据表中没有的记录,而且Session也没有被关闭,同时需要使用对象的时候.
    • 使用get方法查询会 返回null
    • 使用load方法查询 :若不是用该对象的任何属性没有问题,若需要初始化了,抛出异常.
    @Test
    public void testGet(){
        News getVo = (News)this.session.get(News.class,1888);

        System.out.println(getVo.getClass().getName());
    }
  • 使用get()方法直接抛出空指针异常

在这里插入图片描述

    @Test
    public void testLoad(){
        News  loadVo = (News)this.session.load(News.class,1888);

        System.out.println(loadVo.getClass().getName());
    }
  • 使用load()方法不会出现异常

在这里插入图片描述
3. 在需要初始化代理对象之前若关闭Session. load方法可能会抛出 异常.

    @Test
    public void testLoad(){
        News  loadVo = (News)this.session.load(News.class,1888);
        this.session.close();//在使用loadVO类之前关闭session
        System.out.println(loadVo);
    }
  • 抛出的异常:org.hibernate.LazyInitializationException: could not initialize proxy - no Session(无法初始化代理,没有Session)

在这里插入图片描述

Session核心方法2

  1. update()方法
  • update()方法相当于sql语句之中的Update语句

当修改一个持久化对象的时候不需要现实调用update()方法,在执行transaction.commit()方法时会先执行flush()方法.就会自动执行update语句.

  • 如果更新一个游离对象需要使用的调用update()方法

  • update()方法可以将一个游离对象变为一个持久化对象.

  • 需要注意的问题:

  1. 无论要更新的游离对象是否与数据表中的记录是否一致,都会发送Update语句.
    @Test
    public void  testUpdate(){
        News temp =new News();
        temp.setId(1);
        temp.setTitle("update()方法测试");
        temp.setPrice(550.2);
        temp.setInfo("testUpdate");
        temp.setPubDate(new Date());
        this.session.update(temp);
    }

在这里插入图片描述
2. 如何能让update方法不再盲目的触发update语句?在.hbm.xml文件的class节点设置一个select-before-update=“true”()但通常不使用该属性

<class name="News" table="NEWS" select-before-update="true">
  1. 如果数据表中没有对应的记录,但还调用了update方法,会抛出异常.
  • 当前数据库的内容

在这里插入图片描述

    @Test
    public void  testUpdate(){
        News temp =new News();
        
        temp.setId(12345);//当修改一个数据库之中不存在的数据,就会出现异常
        temp.setTitle("update()方法测试");
        temp.setPrice(550.2);
        temp.setInfo("testUpdate");
        temp.setPubDate(new Date());
        this.session.update(temp);
    }

在这里插入图片描述
4. 当update()方法关联一个游离对象时,如果在Session的缓存之中已经存在了相同的OID的持久化对象.会出现异常,因为在Session缓存中,不能够同时存在两个OID相同的对象.

   @Test
    public void  testUpdate(){
        //使用get()方法得到一个持久化对象,此时这个对象会保存在session的缓存之中
        News vo = (News) this.session.get(News.class,1);

        //创建一个于vo的id相同的对象
        News temp = new News();
        temp.setId(vo.getId());
        //使用update()方法关联这个temp对象
        //由于此时的temp对象和vo对象的id都是相同的
        //并且vo对象已经保存在了session之中
        //所以当使用update()方法的时候,就会出现在同一session之中存在两个id相同的对象
        this.session.update(temp);
    }

在这里插入图片描述

  • saveOrUpdate()方法

Session 的 saveOrUpdate() 方法同时包含了 save() 与 update() 方法的功能
在这里插入图片描述

  • 如果OID不为null,但数据表中还没有和其对应的记录,会抛出一个异常.
    @Test
    public void testSaveOrUpdate(){
        News news = new News();
        news.setId(123456);//此时数据库之中不存在此id的数据
        this.session.saveOrUpdate(news);//如果调用saveOrUpdate()方法就会出现异常

    }

在这里插入图片描述

判定对象为临时对象的标准

  1. Java 对象的 OID 为 null
  2. 映射文件中为 设置了 unsaved-value 属性, 并且 Java 对象的 OID 取值与这个 unsaved-value 属性值匹配
<id name="id" column="ID" unsaved-value="123456">
  • delete()方法
  • 只要OID和数据表中的一条记录对应,就执行delete操作
    @Test
    public void testDelete(){
        News news = new News();
        news.setId(1);//删除数据库之中,id为1的数据
        this.session.saveOrUpdate(news);

    }
  • 使用delete()方法删除后的数据是奖对应的id那一条数据的其他属性都设置为nnull
    在这里插入图片描述
  • 如果OID在数据表中没有对应的记录,抛出异常.
    @Test
    public void testDelete(){
        News news = new News();
        news.setId(123);//删除一个数据库之中不存在的数据
        this.session.saveOrUpdate(news);

    }

在这里插入图片描述

Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, 若把它设为 true, 将改变 delete() 方法的运行行为: delete() 方法会把持久化对象或游离对象的 OID 设置为 null, 使它们变为临时对象

<!--改变delet()方法的行为-->
<property name="use_identifier_rollback">true</property>
  • evict()方法
  • 作用:从session缓存中把指定的持久化对象移除.
    @Test
    public void testEvict(){
        //使用load()方法将数据库中的一个对象将在到缓存之中
        News vo1 = (News)this.session.load(News.class,4);
        
        //使用evict()方法将缓存中的对象清除
        this.session.evict(vo1);
        //当要使用该对象的时候,系统无法从缓存之中初始化该对象,就会出现异常
        System.out.println(vo1);

    }

在这里插入图片描述

  • doWork()方法

Work 接口: 直接通过 JDBC API 来访问数据库的操作
Session 的 doWork(Work) 方法用于执行 Work 对象指定的操作, 即调用 Work 对象的 execute() 方法. Session 会把当前使用的数据库连接传递给 execute() 方法.

    @Test
    public void testWork(){
        Work work = new Work() {
            @Override
            public void execute(Connection connection) throws SQLException {
                String sql = "SELECT id FROM news;";
                PreparedStatement ps = connection.prepareStatement(sql);
                ResultSet resultSet = ps.executeQuery();
                while(resultSet.next()){
                    System.out.println(resultSet.getInt(1));
                }
            }
        };

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值