session概述
Session 接口是 Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载Java 对象的方法.
理解session的缓存
l 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
l 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图load()对象时,会判断缓存中是否存在该对象,有则返回。没有在查询数据库。
清理session的缓存
l Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应.Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为清理缓存(flush)
l 默认情况下 Session 在以下时间点清理缓存:
• 当应用程序调用 Transaction 的 commit()方法的时, 该方法先清理缓存(session.flush()),然后在向数据库提交事务(tx.commit())
• 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先清理缓存,以保证查询结果能够反映持久化对象的最新状态
• 显式调用 Session 的 flush() 方法.
l 区别:
flush:进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一些列sql语句,但不提交事务,;
commit:先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。
reresh:刷新,让session和数据库同步,执行查询,把数据库的最新信息显示出来,更新本地缓存的对象状态.
clear:清空缓存,等价于list.removeAll();
利用Session缓存读取持久化对象的数据
1 Customer c = new Customer(“TOM”,new HashSet());
2 session.save(c); //customer对象被持久化,并且加入到session的缓存中
3 Long id = c.getId();
4 c= null; //c变量不再引用customer对象
5 //从session缓存中读取customer对象,使c2变量引用customer对象
6 Customer c2 = (Customer)session.load(Customer.class,id);
7 tx.commit(); //缓存中的对象和数据库同步
8 session.close(); //关闭session 清空缓存
9 System.out.println(c2.getName());//访问customer对象
10 C2 = null; //C2对象不再引用customer对象,customer对象结束生命周期
---------------------------------------------------------------------------------------------
缓存的作用:
1。减少访问数据库的频率。
2。保证缓存中的对象与数据库中的相关记录保持同步。
Session执行批量操作
可以写一个for循环,Session可以批量插入上万条数据。如下面的代码:
For(int i=0;i<10000;i++){
Session.save(object);
}
这样写的代码会产生一个什么问题?会使一万个对象的缓存全部存在于内存中,这样做加大了内存的压力。所以应该定期清理session的缓存,也就是flush和clear一下,这样内存才能保证足够的空间。
Session.load与session.get方法
不同场合的不同解决方案
场合一:当用户要取数据库的一张表的一个字段,这个字段很可能就是一个字符,总而言之长度是比较短的。(get)
场合二:当用户要取数据库的一张表的一个字段的值,而这个值很可能是blob类型,也许存取的是一个很大的视频文件。(load)
get和load主要的区别在于数据库中没有数据的时候,get返回null,而load抛出异常;另外一个就是对lzay-load的不同反应,get不支持lazy-load,load支持。
延迟加载
也叫懒加载,不是在执行获取操作时马上生成SQL,而是在第一次使用时生成SQL。
分成两种:
类级别的:
<class ... lazy="true/false">
属性级别的:
<set/list/map/bag ... lazy="...">
<many-to-one ... lazy="...">
<one-to-one ... lazy="...">
在使用懒加载特性时,可能会有LazyInitializationException异常:
原因:
真正的去获取数据时,Session已经没有了。
解决办法:
方式一:让Session在真正加载后再关闭。
方式二:或是在Sessoin关闭前执行Hibernate.initialize(department.getEmployees());
集合的延迟加载
True false extra
extra为更进一步的延迟加载策略。
当调用getStudents()时不会加载hql语句,当加载student的属性的时候才会发出SQL语句
调用getStudents().size()方法的时候,会触发类似于:Hibernate: select count(id) fromT_Student where classid =? 这样的SQL查询语句(这是一种很聪明的做法,如果lazy=”true”,getStudents().size()将会使得hibernate加载所有集合的数据到内存中)。
调用getStudents().contains()方法的时候(即判断是否包含某个对象),会触发类似于:select 1 fromT_Student where classid =? and id =? 这样的SQL查询语句。
默认的检索策略是立即检索。在Hibernate映射文件中,通过在<class>上配置 lazy属性来确定检索策略。对于Session的检索方式,类级别检索策略仅适用于load方法;也就说,对于get、qurey检索,持久化对象都会被立即加载而不管lazy是false还是true.一般来说,我们检索对象就是要访问它,因此立即检索是通常的选择。由于load方法在检索不到对象时会抛出异常(立即检索的情况下),因此我个人并不建议使用load检索;而由于<class>中的lazy属性还影响到多对一及一对一的检索策略,因此使用load方法就更没必要了。
关联级别检索策略
fetch (默认值select) | Lazy (默认值是true) | 策略 |
Join | False | 采用迫切左外联接检索。 |
Join | True | 采用迫切左外联接检索。 |
join | Extra | 采用迫切左外联接检索。 |
select | False | 采用立即检索 |
select | True | 采用延迟检索 |
select | Extra | 采用延迟检索 c.getOrders().size() 执行 select count(id) from orders where customer_id =? for(Order o:set){ o.getOrderNumber();} 将执行: select customer_id , id,order_number ,price from orders where customer_id=? |
subselect | false/true/extra 也分为3中情况 | 嵌套子查询(检索多个customer对象时) Lazy属性决定检索策略) select customer_id,order_number,price from orders where customer_id in (select id from customers) |
检索策略 | 优点 | 缺点 | 优先考虑使用的场合 |
立即检索 | 对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象 | (1)select语句多 (2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。 | (1)类级别 (2)应用程序需要立即访问的对象 (3)使用了二级缓存 |
延迟检索 | 由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。 | 应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。 | (1)一对多或者多对多关联 (2)应用程序不需要立即访问或者根本不会访问的对象 |
迫切左外连接检索 | (1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。 (2)使用了外连接,select语句少 | (1)可能会加载应用程序不需要访问的对象,浪费内存。 (2)复杂的数据库表连接也会影响检索性能。 | (1)多对一 (2)需要立即访问的对象 (3)数据库有良好的表连接性能。 |