JavaEE学习笔记之SSH—Hibernate

Posted minigeek

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEE学习笔记之SSH—Hibernate相关的知识,希望对你有一定的参考价值。

今天来讲讲 Hibernate 的灵魂所在——> Session 缓存

session缓存是由一系列的Java集合构成的。当一个对象被加入到Session缓存中,这个对象的引用就加入到了java的集合中,以后即使应用程序中的引用变量不再引用该对象,只要Session缓存不被清空,这个对象一直处于生命周期中。

Session缓存的作用:

1)减少访问数据库的频率。

2)保证缓存中的对象与数据库中的相关记录保持同步。

Session清理缓存的时机:

 1)当调用Transaction的commit()方法时,commit()方法先清理缓存(前提是FlushMode.COMMIT/AUTO),然后再向数据库提交事务。

 2)当应用程序调用Session的createQuery().list()或者createQuery().iterate()时,如果缓存中的持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能反映持久化对象的最新状态。

    hibernate2中Session.find()对应于3中的session.createQuery().list()
    hibernate2中Session.iterate()对应于3中的session.createQuery().iterate()

 3)当应用程序显示调用Session的flush()方法的时候。

Session的setFlushMode()方法用于设定清理缓存的时间点。FlushMode类定义了三种不同的清理模式:FlushMode.AUTO、FlushMode.COMMIT和FlushMode.NEVER。
FlushMode.AUTO是默认值:

session.setFlushMode(FlushMode.COMMIT);

Session清理模式执行清理缓存操作的时间点:

这里写图片描述

假设现在想把两个客户信息插入到数据库中:

        Session session = HibernateSessionFactory.getSession();
        Transaction transaction = session.beginTransaction();
        Customer customer1 = new Customer(null, "张三", "78784854", "南昌");
        Customer customer2 = new Customer(null, "李四", "45646463", "北京");
        session.save(customer1);
        session.save(customer2);
        transaction.commit();

首先创建两个客户对象,这时会在栈区有两个引用指向堆区的两个客户对象即

    Customer customer1 = new Customer(null, "张三", "78784854", "南昌");
    Customer customer2 = new Customer(null, "李四", "45646463", "北京");

然后执行save()方法,会在session缓存即session对象中 创建两个引用指向堆区的这两个客户对象。并且会生成两条 insert (插入)语句保留在session中。

最后 等执行到 commit() 的时候就将事务提交。 这时因为 session 的清理缓存的时机默认为 FlushMode.AUTO ,所以当 commit() 的时候会先调用 session.flush()—>是将刚刚保留的 insert 语句发送到 数据库中。等 commit() 完之后就将 insert 语句的数据永久保存在数据库中了。

这里写图片描述

在刚才的基础上,现在来看看这段代码:

Transaction transaction = session.beginTransaction();
Customer customer = (Customer) session.load(Customer.class, 1L);
        customer.setName("流川枫");
        transaction.commit();

这段代码是要将 id 为 1 的用户的用户名更新为“流川枫”。很奇怪这段代码并没有调用 update()方法,那能更新到吗?答案是肯定的。现在我们来看看内存图:

这里写图片描述

来分析下:

首先执行 load()方法,会将数据库的对应数据加载到内存中即地址B01所对应的内容。这时session 和 栈区都指向 地址B01。

因为bo1是持久态对象,而持久态对象发生变化时,session会监听到这种变化,并且产生出对应sql保证在事务提交之后让内存中的对象和数据库中的数据保持一致。执行到 coustomer.setName() 方法时,持久态对象发生变化,随即 session 产生一条 update 语句。当 commit 之后就将update 语句发送到数据库并更新。

所以这个对象并不需要调用 update()方法去更新也会更新。

那么我们就得来了解下什么是持久态对象了。

在 Hibernate 中判断对象的状态,可以从以下两个方面去判断:

a.对象与 session 之间的关系

b.对象与数据库数据之间的关系

Hibernate对象的状态:

1)瞬时态Transient

由new操作符创建,且尚未与Hibernate Session关联的对象。处于瞬时态的java对象成为临时对象。

特点

    不处于Session的缓存中,即不被任何一个Session实例关联。

    在数据库中没有对应的记录。

2)持久态Persistent

已经被持久化,加入到Session的缓存中,处于持久化状态的java对象被称为持久化对象。

特点

位于一个Session实例的缓存中。

持久化对象在数据库中有相应的记录

Session在清理缓存时,会根据持久化对象的属性变化来同步更新数据库。

当一个持久化对象关联一个临时对象,在允许级联保存的情况下,Session在清理缓存的时候会把这个临时对象也转变为持久化对象。

3)脱管态Detached

已经被持久化,但不再处于Session的缓存中,处于脱管状态的java对象称为游离对象。

比如在刚才的基础上,添加如下代码:

Customer customer = new Customer(1L, "", "", "");

因为 id 为 1 的用户存在,但又和 session 没有关联,所以是游离对象。

特点

不再位于Session的缓存中,即不被Session关联。

游离对象是由持久化对象转变过来的,因此在数据库存在与之对应的记录(前提是没有其他程序删除了这条记录)

临时对象VS游离对象

相同:都不被Session关联,Hibernate不会保证它们的属性变化与数据库保持同步。

不同:前者在数据库中没有与之对应的记录。后者由持久化对象转变而来,因此数据库中可能还存在与之对应的记录。

Hibernate 对象状态转换图

这里写图片描述

脱管状态
session中不维护脱管对象
数据库中有脱管对象对应的数据

save(obj)   
  obj 瞬态
    将瞬态转化为持久态对象
  obj 脱管
    重新保存一个新的对象
  obj 持久态
    没意义

update(obj)
  obj 脱管
    将脱管态转换为持久态

saveOrUpdate(obj)
  如果obj是瞬态对象,保存
  如果obj是脱管对象,更新

delete(obj)
  将持久态删除

Session API :

Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存,更新,删除和查询的方法。

save(): 把一个临时对象加入到缓存中,使它变成持久化对象

-->选用映射文件指定的主键生成器为持久化对象分配唯一的OID

-->计划一条insert语句,把参数对象当前的属性值组装到insert语句中,但是save()方法并不立即执行SQL insert语句,只有当Session清理缓存时候才会执行。

-->如果在save()方法之后,又修改了持久化对象的属性,会使得Session在清理缓存的时候额外执行SQL update语句。

注意:save()方法是用来持久化一个临时对象的!

如果将一个持久化对象传给save()方法将不会执行任何操作,多余的步骤

如果将一个游离态对象传给save()方法,session会将它当作临时对象来处理,再次向数据库中插入一条记录,不符合业务需求!

update():把Customer对象重新加入到Session缓存中,使之变为持久化对象。

--->计划一条update语句,只有在清理缓存的时候才会执行,并且在执行的时候才会把参数对象中的属性值组装到update语句中。

注意:update()是将一个游离对象转变为持久化对象的。

  只要通过update()方法使游离对象被一个session关联,即使没有修改参数对象的任何属性,Session在清理缓存的时候也会执行由update方法计划的Update语句。

saveOrUpdate():同时包含了save()与update()方法的功能,如果传入的参数是临时对象,调用save方法,如果参入参数是游离对象,调用update()方法,如果传入的是持久化对象,直接返回。

load()/get(): 都会根据给定的OID从数据库中加载一个持久化对象,区别在于,当数据库中不存在与OID对应的记录时,load()方法会抛出ObjectNotFoundException异常,而get()方法返回null.

delete():用于从数据库中删除与参数对象对应的记录,如果传入的参数是持久化对象,Session就计划执行一个delete语句,如果传入的参数是游离对象,先使游离对象被Session关联,使它变为持久化对象,然后计划一个delete语句,在清理缓存的时候执行。

evict():从缓存中清除参数指定的持久化对象。

  适用场合:不希望Session继续按照该对象的状态改变来同步更新数据库。
  在批量更新或批量删除的场合,当更新或者删除一个对象后,及时释放该对象占用的内存。当然批量操作优先考虑JDBC.

clear():清空缓存中所有持久化对象。

get VS load

1.get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库。默认延迟加载,查询当前对象的时候暂时先不查询与之关联的对象。

load方法,为延迟加载即先将查询语句放入缓存中,待到用时再将缓存中的查询语句发送到数据库进行查询。(lazy属性对它不影响)

2.get方法检索不到的话会返回null

load方式检索不到的话会抛出org.hibernate.ObjectNotFoundException异常

注意:

Java代码

Users user = (Users)session.load(Users.class, userId);  
System.out.println(user.getId());  

上面这2句代码,不会去执行数据库操作。因为load后会在hibernate的一级缓存里存放一个map对象,该map的key就是userId的值,但是当你getId()时,它会去一级缓存里拿map的key值,而不去执行数据库查询。所以不会报任何错。不会执行任何数据库操作。

补充点:

cascade

    级联操作,一般谁来维护关系,就谁来级联。如果主表来级联,而从表来维护关系,这时保存的时候就会报错。

    保存或更新(save-update)
            customer
            <set name="orders" cascade="save-update"/>
        这表示customer级联order
            维护关系
                customer 维护 order
                customer.getOrders().add(o1);
                customer.getOrders().add(o2);
                customer.getOrders().add(o3);

            session.save(customer);
                insert into tbl_customer values();
                if(级联){
                    os = customer.getOrders();
                    for(Order o : os){
                        save(o);
                    }
                }
    删除(delete)
    删除保存更新(all)
    解除关系即删除(delete-orphan):必须双方接触关系,删除的是从表的数据
        Customer c = (Customer) session.get(Customer.class, 4L);
        Order order = (Order) session.get(Order.class, 8L);
        //接触关系
        c.getOrders().remove(order);
        order.setCustomer(null);
    删除保存更新,解除关系即删除(all-delete-orphan)

lazy

默认true  延迟加载,查询当前对象的时候暂时先不查询与之关联的对象

false   立即加载,查询当前对象的时候查询所有与之关联的对象。

inverse(维护关系的权利的反转)

一般谁来维护关系谁就来产生外键,不能权利颠倒。如果主表维护从表,而主表的维护关系的权利反转了(即在主表中 inverse="true"),则插入数据的时候不会产生外键即从表中的外键为null。一般1:n关系由多的一方来维护即可。

一般写在set中

默认false 当前方可以维护关系(产生外键)

true        当前方不维护关系(不产生外键)

以上是关于JavaEE学习笔记之SSH—Hibernate的主要内容,如果未能解决你的问题,请参考以下文章

《JavaEE基础实用教程》笔记说明

框架学习笔记之Hibernate

JAVAEE框架技术之14SSM综合案例

JAVAEE学习笔记hibernate01:简介搭建配置文件详解API详解和CRM练习:保存客户

JAVAEE学习笔记hibernate03:多表操作,级联练习:添加联系人

JAVAEE学习笔记hibernate04:查询种类HQLCriteria查询优化和练习为客户列表增加查询条件