1. Session缓存
为了让对象A一直处于生命周期中,要么对象A被显示引用,要么对对象A的引用保存在始终处于生命周期中的对象B中,直到对象B的生命周期结束而结束。在Session接口的实现中包含了一系列的Java集合,这些集合用于保存对持久化对象的引用,因此这些Java集合就构成了Session的缓存。
当调用Session接口的save()方法持久化一个对象时,该对象被加入到Session缓存中,以后只要Session缓存没有被清空,该对象将一直处于生命周期中。
当调用Session接口的get()方法试图装载一个持久化对象时,Session首先判断缓存中是否存在这个对象,若存在则返回,若不存在则从数据库中检索。
2. Session缓存的作用
(1) 减少访问数据库的频率。应用程序从缓存中读取持久化对象的速度显然优于从数据库中检索数据的速度。
(2) 当缓存中的持久化对象之间存在循环关联关系时,Session会保证不出现访问对象图的死循环,以及由死循环引发的JVM堆栈溢出。
(3) 保证数据库中的相关记录与缓存中的记录同步。Session在清理缓存的时,会自动进行脏数据检查(dirty-check),如果发现Session缓存中的对象与数据库中相应记录不一致,则会按最新的对象属性更新数据库。
3. 清理缓存的机制
为减少访问数据库的次数,当Session缓存中对象的属性发生变化时,并不会及时清理缓存及执行相关的SQL Update语句,而是在特定的时间点把相关的SQL合并为一条SQL语句才清理缓存。
Transaction transaction = hibernateSession.beginTransaction();
Customers c = (Customers)hibernateSession.get(Customers.class, new Long(3));
c.setName("Mike");
c.setName("Jone");
transaction.commit();//自动清理缓存,执行一条Update语句,提交事务
在默认情况下,Session会在以下时间点清理缓存(保证Session缓存中对象与数据库记录数据的一致性。):
(1) org.hibernate.Transaction.commit():先清理缓存再提交事务;
(2) 执行持久化查询操作时,如果缓存中的持久化对象的属性已发生变化,则先清理缓存以便与数据库中的记录保持同步;
(3) 显示调用Session.flush()时,会清空缓存。
Transaction transaction = hibernateSession.beginTransaction();
Customers c = (Customers)hibernateSession.get(Customers.class, new Long(3));
c.setName("ChengDong");
hibernateSession.flush();// 显示清理缓存,执行update语句
c.setName("Mike");
transaction.commit();// 自动清理缓存,执行update语句,提交事务
注意:commit()和flush()方法区别在于:后者进行清理缓存的操作,执行一系列SQL语句,但不会提交事务;前者会先调用flush()方法,然后提交事务。提交事务也就意味着对数据库所做的
更新将永久保存下来。
除了默认的清理缓存的方式外,还可以通过Session.setFlushMode()方法显示设定清理缓存的时间点,包括FlushMode.AUTO、FlushMode.NEVER、FlushMode.COMMIT,具体用法详查资料。
4. Java对象在持久化层的状态:
(1) Transient(临时状态):new语句创建、没有被持久化且不在Session缓存中。处于这种状态的对象被称为临时对象;
(2) Persistent(持久化状态):已被持久化且已加入到Session缓存中。处于这种状态的对象被称为持久化对象;
(3) Detached(游离状态):已被持久化,但不处于Session的缓存中。处于这种状态的对象被称为游离对象。
持久化对象的特征:
(1) OID不为NULL;
(2) 位于一个Session实例的缓存中;
(3) 持久化对象与数据库中的相关记录对应;
(4) Session在清理缓存时,会根据持久化对象的属性变化来同步更新数据库。
Session的许多方法都能使Java对象进入持久化状态:
(1) save():临时对象---> 持久化对象;
(2) load()/get():返回的对象总是处于持久化状态;
(3) list():返回的list对象都是持久化对象;
(4) update()/saveOrUpdate()/lock():游离对象--->持久化对象。
Hibernate保证同一个Session实例的缓存中,数据库中的每条记录只对应唯一的持久化对象,因此来自于同一个session实例缓存中的持久化对象的引用变量必然相同,即:
Session hibernateSession = sessionFactory.openSession();
Session hibernateSession2 = sessionFactory.openSession();
Transaction transaction = hibernateSession.beginTransaction();
Transaction transaction2 = hibernateSession2.beginTransaction();
Customers a = (Customers)hibernateSession.get(Customers.class, new Long(3));
Customers b = (Customers)hibernateSession.get(Customers.class, new Long(3));
Customers c = (Customers)hibernateSession2.get(Customers.class, new Long(3));
// 同一Session实例的缓存中仅存在唯一一个与数据库记录对应的持久化对象
System.out.println(a == b);// true
System.out.println(a == c);// false
transaction.commit();
transaction2.commit();
hibernateSession.close();
hibernateSession2.close();
在实际应用中,应避免一个Java对象同时位于多个Session实例中缓存中,否则这会导致重复执行SQL语句,并且极易出现高并发问题。
5. Session的一些持久化操作
(1) save
该操作首先将临时对象转化为持久化对象,并加入到Session缓存中;其次根据映射文件中给定的标识符生成器,为持久化对象分配唯一一个OID。save()方法仅用来持久化临时对象,在程序中不应该把持久化对象和游离对象传给save()。
注:save()操作仅仅是将Java对象的临时状态转化为持久化状态,并未立即执行insert语句,待需要清理缓存时才会执行insert语句。如果在save()方法改变了持久化对象的属性,则在清理缓存时不仅要执行insert语句,还要执行update语句,例如:
Transaction trasaction = session.beginTransaction();
Customers c = new Customers();
c.setName("testSave");
session.save(c);// 此步完成对象持久化和持久化对象OID的分配
c.setName("testCache");
trasaction.commit();// 自动清理缓存
控制台打印的SQL为:
Hibernate: insert into customers (name, ID) values (?, ?)
Hibernate: update customers set name=? where ID=?
(2) Session的get()和load()方法
这两个操作都是根据OID单数据加载持久化对象。它们的不同之处在于:当数据库中不存在与OID对应的记录时,load()会直接抛出org.hibernate.ObjectNotFoundException,而get()则返回null。