hibernate学习之 Session

本文详细介绍了Hibernate的Session接口,包括Session的缓存机制、对象状态管理、常用方法如save(), update(), get(), load(), flush(), refresh()等的使用细节,以及与数据库交互的原理,强调了刷新缓存和更新操作的时机和影响。" 132153454,10617859,Matlab字符串数组在文本数据分析中的应用,"['matlab', '开发语言', '文本处理']
摘要由CSDN通过智能技术生成

Session概述

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

这里我们建立一个HibernateTest测试类。其中放置了Session和Transaction成员变量,这个在开发中不能放置为成员变量,会有并发问题的,这里我们只是测试可以放置一下。
我们通过单元测试的 @Before public void init()来初始化我们成员变量,然后通过@After public void destroy() 来关闭。

public class HibernateTest {
    private SessionFactory sessionFactory = null;
    private Session session = null;
    private Transaction transaction = null;


    @Before
    public void init() {
        System.out.println("init");
        //1.创建一个SessionFactory 对象,创建session的工厂的一个类
        //1.1创建一个Configuration对象,对应hibernate的基本配置信息和对象关系映射信息
        Configuration configuration = new Configuration().configure();
        //1.2创建一个ServiceRegistry对象,hibernate4.x新添加的对象,hibernate任何的配置和服务都要在该对象中注册才能有效。
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
        //1.3
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        //2.创建一个Session对象,这个和jdbc中的connection很类似
        session = sessionFactory.openSession();
        //3.开启事务
        transaction = session.beginTransaction();
    }
    //
    @After
    public void destroy() {
        System.out.println("destroy");
        //5.提交事务
        transaction.commit();

        //6.关闭session对象
        session.close();

        //7.关闭SessionFactory 对象
        sessionFactory.close();
    //

    }

    @Test
    public void testSession() {
        //4.执行保存操作
        News news = new News("title:java", "author: mamh", new Date(new java.util.Date().getTime()));
        session.save(news);

        Object o = session.get(News.class, 1);
        System.out.println(o);
        //

    }

session的缓存

    @Test
    public void testSession() {
        //4.执行保存操作
        //News news = new News("title:java", "author: mamh", new Date(new java.util.Date().getTime()));
        //session.save(news);

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

        Object news2 = session.get(News.class, 1);
        System.out.println(news2);

    }

这个代码的输出,从中我们可以看到news对象打印了两遍,但是sql语句只打印了一边,说明第二次没有去执行sql语句。这个是一级缓存。一级缓存是session级别的。

Hibernate: 
    select
        news0_.hb_id as hb1_0_0_,
        news0_.hb_title as hb2_0_0_,
        news0_.hb_author as hb3_0_0_,
        news0_.hb_date as hb4_0_0_ 
    from
        atguigu.hb_news news0_ 
    where
        news0_.hb_id=?
News{id=1, title='java', author='sun', date=2017-10-18}

News{id=1, title='java', author='sun', date=2017-10-18}

destroy

Process finished with exit code 0

session缓存的flush()方法

    @Test
    public void testSessionFlush(){
        News news = (News) session.get(News.class, 1);
        System.out.println(news);
        news.setAuthor("mamh");
    }
Hibernate: 
    select
        news0_.hb_id as hb1_0_0_,
        news0_.hb_title as hb2_0_0_,
        news0_.hb_author as hb3_0_0_,
        news0_.hb_date as hb4_0_0_ 
    from
        atguigu.hb_news news0_ 
    where
        news0_.hb_id=?
News{id=1, title='java', author='mamh', date=2017-10-18}

=destroy=
Hibernate: 
    update
        atguigu.hb_news 
    set
        hb_title=?,
        hb_author=?,
        hb_date=? 
    where
        hb_id=?

Process finished with exit code 0

在执行(News) session.get(News.class, 1) 会调用select查询数据库的,后面的news.setAuthor("mamh"); 设置对象一个新的属性,这个时候会调用update的更新数据库的方法的。
这个是因为session发现缓存中的对象被更改了,然后会调用flush()。修改对象属性的时候session的可以感知到的,在提交事务之前会执行update方法的。在调用commit()方法的时候会先进行flush,然后在执行commit。
flush()的作用:数据库表中的记录和session缓存中的对象保持一致。为了保持一致有可能会发送对应的sql语句。对象状态一模一样是不会发送sql语句的。
transaction.commit(); 中的commit()方法中先调用session的flush()然后在调用commit()。flush()方法可能会发送sql语句,但是不会提交事务的,只有提交事务之后数据库表中的数据才会变化。

flush()什么时候会被调用:
注意:在未提交事务或显示的调用flush()方法之前也有可能会进行flush操作的。

  • 执行HQL或QBC查询,会先进行flush操作,以得到数据表的最新的记录。
  • 若记录的ID是有底层数据库使用自增方式生成的,则调用save()之后就会立即发送insert语句,save之后必须保证对象的id是存在的。

session缓存的refresh()方法
refresh()方法会强制发送select语句,以使session缓存中对象的状态和数据库表中对应的保持一致。


session缓存的clear()方法
清理缓存


    @Test
    public void testClear(){
        News news0 = (News) session.get(News.class, 1);

        session.clear();


        News news1 = (News) session.get(News.class, 1);

    }
Hibernate: 
    select
        news0_.hb_id as hb1_0_0_,
        news0_.hb_title as hb2_0_0_,
        news0_.hb_author as hb3_0_0_,
        news0_.hb_date as hb4_0_0_ 
    from
        atguigu.hb_news news0_ 
    where
        news0_.hb_id=?
Hibernate: 
    select
        news0_.hb_id as hb1_0_0_,
        news0_.hb_title as hb2_0_0_,
        news0_.hb_author as hb3_0_0_,
        news0_.hb_date as hb4_0_0_ 
    from
        atguigu.hb_news news0_ 
    where
        news0_.hb_id=?

第一次调用获取new对象的时候会发送一条select查询语句的,然后调用了session的clear()方法,清空缓存了,第二次在调用获取new对象的时候会再次发送一条select查询语句。


session中的save()和perssit()方法

    @Test
    public void testPersist() {
        //和save()方法很类似,区别:
        //在调用persist方法之前,调用了setId(),对象已经有了ID了,则不会执行insert操作,会抛出异常
        News news = new News();
        news.setAuthor("mm");
        news.setTitle("ssssssssss");
        news.setDate(new Date(new java.util.Date().getTime()));
        news.setId(234234);
        session.persist(news);
    }

    @Test
    public void testSave() {
        //把临时对象变为持久化对象
        //为对象分配ID,在save()方法之前设置ID是无效的,save()之后也是不能改这个ID的
        //持久化的对象的ID是不能进行修改的
        //在flush缓存时候会发送一条insert语句
        //把临时对象保存到数据库中
        News news = new News();
        news.setAuthor("mm");
        news.setTitle("ssssssssss");
        news.setDate(new Date(new java.util.Date().getTime()));

        session.save(news);
    }

session中的get()和load()方法
load和get的区别:
get会立即加载对象,load方法若不用该对象则不会立即查询,而返回一个代理对象。
get立即检索,load是延迟检索。
若数据表中没有对应的记录,session也没有关闭,同时要使用该对象时候:get返回null,load抛出异常。
load方法可能会抛出懒加载异常:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
在需要初始化代理对象之前关闭了session就会抛出这个异常。

@Test
    public void testLoad(){
        //load和get的区别:
        //get会立即加载对象,load方法若不用该对象则不会立即查询,而返回一个代理对象。
        //get立即检索,load是延迟检索。
        //若数据表中没有对应的记录,session也没有关闭,同时要使用该对象时候:get返回null,load抛出异常。
        //load方法可能会抛出懒加载异常:
        // org.hibernate.LazyInitializationException: could not initialize proxy - no Session
        // 在需要初始化代理对象之前关闭了session就会抛出这个异常。
        News news1 = (News) session.load(News.class, 10);
        //session.close();
        System.out.println(news1);
    }

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

session的update()方法
若更新一个持久化对象,不需要显示的调用update()方法,因为在调用transaction的commit方法时候,会先执行flush方法的。
更新一个游离对象,需要显示的调用session的update()方法,可以把游离对象变为持久化对象。
无论要更新的对象 是否和数据库表中的记录是否一致都会发送update语句的。

    @Test
    public void testUpdate(){
        //若更新一个持久化对象,不需要显示的调用update()方法,因为在调用transactioncommit方法时候,会先执行flush方法的。
        //更新一个游离对象,需要显示的调用sessionupdate()方法,可以把游离对象变为持久化对象。
        //无论要更新的对象 是否和数据库表中的记录是否一致都会发送update语句的。因为是第二次开的一个新的session。
        News news = (News) session.get(News.class, 1);

        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        //这个时候news对象已经变成游离对象了,应为前面关闭了session,后续又重新打开了session。

        news.setAuthor("sun");
        System.out.println(news);
        //session.update(news);
    }
Hibernate: 
    select
        news0_.hb_id as hb1_0_0_,
        news0_.hb_title as hb2_0_0_,
        news0_.hb_author as hb3_0_0_,
        news0_.hb_date as hb4_0_0_ 
    from
        atguigu.hb_news news0_ 
    where
        news0_.hb_id=?
News{id=1, title='java', author='oracle', date=2017-10-18}

Hibernate: 
    update
        atguigu.hb_news 
    set
        hb_title=?,
        hb_author=?,
        hb_date=? 
    where
        hb_id=?

如何让update盲目的发送update语句呢?在.hbm.xml 中设置select-before-update=”true”(默认false),通常不设置为true,会影响效率。
若数据库表中没有对应的记录还继续调用update()方法,这时候会抛出异常。

    @Test
    public void testUpdate(){
        //若更新一个持久化对象,不需要显示的调用update()方法,因为在调用transactioncommit()方法时候,会先执行flush()方法的。
        //更新一个游离对象,需要显示的调用sessionupdate()方法,可以把游离对象变为持久化对象。
        //无论要更新的对象 是否和数据库表中的记录是否一致都会发送update语句的
        //如何让update盲目的发送update语句呢?在.hbm.xml 中设置select-before-update="true"(默认false),通常不设置为true,会影响效率。
        //若数据库表中没有对应的记录还继续调用update()方法,这时候会抛出异常。

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

        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        //这个时候news对象已经变成游离对象了,应为前面关闭了session,后续又重新打开了session。

        news.setAuthor("sun");
        news.setId(120);
        System.out.println(news);
        session.update(news);
    }

同一个session中不能有ID相同的对象,会抛出异常NonUniqueObjectException

    @Test
    public void testUpdate(){
        //若更新一个持久化对象,不需要显示的调用update()方法,因为在调用transactioncommit方法时候,会先执行flush方法的。
        //更新一个游离对象,需要显示的调用sessionupdate()方法,可以把游离对象变为持久化对象。
        //无论要更新的对象 是否和数据库表中的记录是否一致都会发送update语句的
        //如何让update盲目的发送update语句呢?在.hbm.xml 中设置select-before-update="true"(默认false),通常不设置为true,会影响效率。
        //若数据库表中没有对应的记录还继续调用update()方法,这时候会抛出异常。
        //同一个session中不能有ID相同的对象,会抛出异常NonUniqueObjectException
        News news = (News) session.get(News.class, 1);

        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
        //这个时候news对象已经变成游离对象了,应为前面关闭了session,后续又重新打开了session。

        News news2 = (News) session.get(News.class, 1);
        news.setAuthor("sun");
        session.update(news);
    }

session的saveOrUpdate()方法

    @Test
    public void testSaveOrUpdate() {
        News news = new News("ff", "fff", new Date(new java.util.Date().getTime()));
        session.saveOrUpdate(news);
    }

这个没有ID,这个时候会执行insert语句。

Hibernate: 
    insert 
    into
        atguigu.hb_news
        (hb_title, hb_author, hb_date) 
    values
        (?, ?, ?)

如果有ID:会执行update语句

    @Test
    public void testSaveOrUpdate() {
        News news = new News("ff", "fff", new Date(new java.util.Date().getTime()));
        news.setId(1);
        session.saveOrUpdate(news);
    }
Hibernate: 
    select
        news_.hb_id,
        news_.hb_title as hb2_0_,
        news_.hb_author as hb3_0_,
        news_.hb_date as hb4_0_ 
    from
        atguigu.hb_news news_ 
    where
        news_.hb_id=?
Hibernate: 
    update
        atguigu.hb_news 
    set
        hb_title=?,
        hb_author=?,
        hb_date=? 
    where
        hb_id=?

如果有id,但是没有对应的记录,这个时候会抛出异常


    @Test
    public void testSaveOrUpdate() {
        News news = new News("ff", "fff", new Date(new java.util.Date().getTime()));
        news.setId(23121);
        session.saveOrUpdate(news);
    }

session的delete方法
执行删除操作,只要OID和数据库表中的记录对应就会执行删除操作,如果没有对应的记录就抛出异常。

    @Test
    public void testDelete() {
        //执行删除操作,只要OID和数据库表中的记录对应就会执行删除操作,如果没有对应的记录就抛出异常。
        News news = new News("ff", "fff", new Date(new java.util.Date().getTime()));
        news.setId(1);
        session.delete(news);
    }
Hibernate: 
    delete 
    from
        atguigu.hb_news 
    where
        hb_id=?

可以通过设置hibernate配置文件use_identifier_rollback属性为true,是删除对象后,把其OID置为null

@Test
public void testDelete() {
    //执行删除操作,只要OID和数据库表中的记录对应就会执行删除操作,如果没有对应的记录就抛出异常。
    //可以通过设置hibernate配置文件use_identifier_rollback属性为true,是删除对象后,把其OID置为null
    News news = new News(8, "ff", "fff", new Date(new java.util.Date().getTime()));
    System.out.println(news);
    session.delete(news);
    System.out.println(news);
}

session的evict()方法,从缓存中移除某个对象

    @Test
    public void testEvict(){
        News news1 = (News) session.get(News.class, 1);
        news1.setAuthor("11");

        News news2 = (News) session.get(News.class, 2);
        news2.setAuthor("==============");

        // session.evict(news1);//加了这一句就不会更新到数据库中了
    }

new 语句 –> 临时对象
|
|
持久化对象
|
|
|
游离状态


通过hibernate调用存储过程

    @Test
    public void testDoWork(){
        session.doWork(new Work() {
            public void execute(Connection connection) throws SQLException {
                //在这个里面调用存储过程
                System.out.println(connection);
            }
        });
    }
输出:
org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler@7526515b[valid=true]

hibernate与触发器协同工作(了解)
hibernate与触发器协同工作时会造成两类问题

触发器使session的缓存中的持久化对象与数据库中对应的数据不一致,触发器运行在数据库中,它执行的操作对session是透明的。
session的update方法盲目的激发触发器,无论游离对象的属性是否发生变化,都会执行update语句,而update语句会激发数据库中相应的触发器

解决方法:

在执行完session的相关操作后,立即调用session的flush和refresh方法,迫使session的缓存与数据库同步
在映射文件的class元素中设置select-before-update属性,当session的update或saveOrUpdate方法更新一个游离对象时,会先执行select语句,获得当前游离对像在数据库中的最新数据,只有在不一致的情况下才会执行update语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值