二级缓存
Hibernate有一个自己的二级缓存,不推荐使用,hibernate内部测试用的。
使用二级缓存需要在配置文件中指定缓存提供商。
<propertyname="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
一级缓存:session级别的缓存
二级缓存:SessionFactory级别的缓存,可以跨越session存在
缓存的类型
对象缓存、集合缓存、查询缓存
三种类型的打开方式
1. 二级缓存使用的对象:不敏感数据、经常访问、改动不大、数量有限。
2. 指定缓存对象的方式:
1) 在主配置文件中设置(推荐使用)
2) 在映射文件中配置
3. 指定要缓存的实体类:见上图。
对象缓存
1. 通过get(xxx.class,index)的方式获得的对象,会加入到一级和二级缓存中。
2. 当关闭session后,再次创建一个session,同样通过get方式获取对象时,首先查找二级缓存。若二级缓存有的话直接从二级缓存获得。若没有才查询数据库。
集合缓存
1. 开启集合缓存后,需要同时开启集合内存储对象的二级缓存。
2. 原因:若不开其集合中的二级缓存的后果是会产生跟多的数据库查询
Ø 首先加载Department对象,(Department类开启二级缓存、类中集合属性也开启二级缓存)
Ø Department对象缓存到二级缓存中。其中的集合由于也开启了二级缓存,故也被缓存到二级缓存中。但是由于集合存储的类没有设置开启二级缓存,故其内容没有缓存,实际在集合中缓存的是id值。
Ø 当重新session访问对象的一般属性时,由于Department对象被缓存,所以该对象的数据可以直接从二级缓存中获取。
Ø 当访问集合中对象时,由于集合中只缓存了Id,所以会根据id值依次查询数据库。故产生了更多的数据库操作。
查询缓存(List和Iterator的不同之处)
1. List:默认往二级缓存中加载数据,但是查询的时候不使用。
2. Iterator:在使用HQL方式查询时,如果用Iterator()方法,就会使用二级缓存。
Ø 因为这个方法是先查询所有符合条件的id集合。
即select id from Employee where id<10;
Ø 获得上面的id集合之后,在一个一个的按id查找数据,就使用上缓存了。但这个方法会有N+1次查询问题,提升性能有限,不太常用。
Ø 第二次查询时:首先还是要查询所有的id集合。
Ø 然后,根据id若有则从二级缓存中获取对象,若没有则查询数据库。
3. Load:默认使用二级缓存。即先从二级缓存中找,没有才查数据库。
4. 更专业的查询缓存:
Ø 使用list,并使用setCacheable(true),说明开启二级缓存。
Ø 同时在主配置文件中,开启查询缓存。见上图。
<propertyname="cache.use_query_cache">true</property>
Ø 原理:一条件作为id,将查询的所有结果作为值。故只有条件【一样】值才能从二级缓存中找出数据。
Update和Delete对二级缓存的影响(时间戳缓存)
1. Update和Delete对一级缓存的影响:数据库改变,一级缓存的内容没有发生改变。
2. 对二级缓存的影响:
第一次使用上述两个方法后,会让二级缓存中相关的数据失效,下次使用这些数据时会重新到数据库中加载。
Session管理方案
1. 要想使用SessionFactory.getCurrentSession()方法需要在Hibernate主配置文件中配置current_session_context_class项。
2. getCurrentSession()方法:
1) 去指定的上下文中(如thread)查找绑定的Session对象,如果有就返回。
2) 如果没有,就创建一个并绑定好,然后返回
3) 当使用getCurrentSession时,Hibernate会在提交或回滚后自动的关闭Session
3. openSession()只是开启一个新的Session,不会做绑定和查找操作。
什么是1+N问题(面试题:阐述+解决方案,背)
1. 画图解释
1) 典型的是one-to-many中:当获取一个部门对象的时候,hibernate会把跟这个部门相关的所有员工查出来。
2) Many-to-one:获取员工表中所有员工时(一条SQL语句就可以搞定),但hibernate会通过外键一个一个地把员工对应的部门也同时加载出来(N条SQL语句)。
2. 解决方案
1) one-to-many:在Department映射文件中,将员工属性lazy=”true”;
many-to-one:在Employee映射文件中,设置部门属性lazy=”true”;
置为lazy不是不加载,而是延迟到使用时在加载。
2) join fetch:
修改hql语句为--" from Employ e leftjoin fetch e.department "
3) QBC(Query By Criteria)
使用QBC的 createCriteria(*.class)执行查询 也可避免N+1问题
Hibernate中的事务隔离级别(面试重要:背)
1. 设定hibernate的事务隔离级别(使用hibernate.connection.isolation配置取值1、2、4、8)
1) hibernate.connection.isolation = 2(如果不设 默认依赖数据库本身的级别)
2. 用悲观锁解决repeatable read的问题(依赖于数据库的锁)
1) select ... for update
2) 使用另一种load方法--load(xx.class , i , LockMode.Upgrade)
a) LockMode.None无锁的机制,Transaction结束时,切换到此模式
b) LockMode.read在査询的时候hibernate会自动获取锁
c) LockMode.write insert updatehibernate 会自动获取锁
d) 以上3种锁的模式,是hibernate内部使用的(不需要设)
e) LockMode.UPGRADE_NOWAIT是 ORACLE 支持的锁的方式
l 悲观锁和乐观锁的使用场景
Ø 考虑效率隔离级别置为2(读以提交),但还要解决不可重复读的问题。
3. Hibernate(JPA)乐观锁定(ReadCommitted)
(详见项目hibernate_3200_Hibernate_Concurrency_Optimistic_Lock)
1) 实体类中增加version属性(数据库也会对应生成该字段,初始值为0),并在其get方法前加
2) @Version注解,则在操作过程中没更新一次该行数据则version值加1,即可在事务提交前判断该数据是否被其他事务修改过.
OpenSession和GetCurrentSession区别
1. 用来产生和管理Session
2. 通常情况下每个应用只需要一个SessionFactory
3. 除非要访间多个数据库的情况
4. 关注两个方法即:openSessiongetCurrentsession
1) open session每次都是新的,需要close
2) getCurrentsession从上下文找,如果有,用旧的,如果没有,建新的
a) 用途,界定事务边界
b) 事务提交自动close
c) 上下文配置可参见xml文件中
<property name="current_session_context_classs">thread</property>
d) current_session_context_class(jta、thread常用 managed、custom.Class少用)
thread 使用connection 但数据库连接管理事务
jta (全称java transaction api)-java分布式事务管理(多数据库访问)
jta由中间件提供(jboss WebLogic等,tomcat不支持)