主要的缓存有:一级缓存,二级缓存,查询缓存
hibernate一级缓存
session的使用策略是每线程一session
一级缓存:session级别的缓存.session没有关闭的情况下是可以用这个缓存里面的东西的,如果跨session是不可以的.一级缓存生命周期很短和session的生命周期一致,一级缓存也叫session级的缓存或事务级缓存
哪些方法支持一级缓存:
对于 查询来说有: load/get/iterate查询实体对象,注意list方法他是不会利用缓存的
对于保存来说:save方法也是支持缓存的,他save的时候也会放缓存里面一份的.
一级缓存它只缓存实体对象的
假设我要存储一万条呢,那缓存里面就会有大量的对象数据在那里,内存迟早是会溢出的.所以如果要有大批量的处理数据的话,hibernate通常这样做:当达到某一条件的时假显式调用一下session.flush();session.clear()这样.如:
for (int i=0; i<100; i++) {
Student student = new Student();
student.setName("张三" + i);
session.save(student);
//每20条更新一次
if (i % 20 == 0) {
session.flush();
//清除缓存的内容
session.clear();
}
}
大批量数导入时一般不用hibernate,使用jdbc或者是数据库的相关工具比如说oracle有Oracle SQL Loader
1.在同一个session中发出两次load查询
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不会发出查询语句,load使用缓存
student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
2.在同一个session中发出两次get查询
Student student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
//不会发出查询语句,get使用缓存
student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
3.在同一个session中发出两次iterate查询,查询实体对象
Iterator iter = session.createQuery("from Student s where s.id<5").iterate();
while (iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
System.out.println("--------------------------------------");
//它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存
iter = session.createQuery("from Student s where s.id<5").iterate();
while (iter.hasNext()) {
Student student = (Student)iter.next();
System.out.println(student.getName());
}
4.在同一个session中发出两次iterate查询,查询普通属性
Iterator iter = session.createQuery("select s.name from Student s where s.id<5").iterate();
while (iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
System.out.println("--------------------------------------");
//iterate查询普通属性,一级缓存不会缓存,所以发出查询语句
//一级缓存是缓存实体对象的
iter = session.createQuery("select s.name from Student s where s.id<5").iterate();
while (iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
5.在同一个session中先调用save,再调用load查询刚刚save的数据
Student student = new Student();
student.setName("张三");
Serializable id = session.save(student);
student = (Student)session.load(Student.class, id);
//不会发出查询语句,因为save支持缓存
System.out.println("student.name=" + student.getName());
hibernate二级缓存
二级缓存也称为进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享
二级缓存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二级缓存
像类似于这种缓存一般是用第三方的,hibernate也实现了一个,商业用的话不过没有第三方的好.
第三方缓存产品有EhCache,jbossCache,oscache等.hibernate中一般使用EhCache的比较多.使用它们要有策略,一个是这个缓存可以放多少对象,第二就是在什么时间把里面的某个对象清掉.它有一个配置文件.它可以对每个实体类使用不同的策略.
注意点:二级缓存主要是缓存实体对象的,普通属性的查询是不会缓存的.该发语句还是会发的.
二级缓存的配置和使用:
* 将ehcache.xml文件拷贝到src下并设置它的相关特性,在这里可以指定二级缓存的相关策略,如:
<cache name="com.bjpowernode.hibernate.Student"
maxElementsInMemory="100"
eternal="false"
timeToIdleSeconds="10000"
timeToLiveSeconds="10000"
overflowToDisk="true"
/>
* 在hibernate.cfg.xml文件中加入缓存产品提供商<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
* 启用二级缓存,这也是它的默认配置<property name="hibernate.cache.use_second_level_cache">true</property>
除了上述这样配置以外还不能用二级缓存的,还必须指定哪些实体类使用二级缓存,有两种方式可以指定.
第一种方式:在实体类的映射文件中配置<cache usage="read-only"/>usage优先选用read-only,但是这可能出现的问题是会有可能出现脏数据,这依赖于缓存产品的超时
第二种方式:可以在hibernate.cfg.xml中进行集中配置采用:<class-cache class="com.bjpowernode.hibernate.Student" usage="read-only"/>
如何管理二级缓存:用SessionFactory来进行管理,把二级缓存的对象清除掉,evict方法即可.如:
/**
* 开启二级缓存
*
* 在两个session中发load查询,采用SessionFactory管理二级缓存
*/
public void testCache3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
//管理二级缓存
//HibernateUtils.getSessionFactory().evict(Student.class);
HibernateUtils.getSessionFactory().evict(Student.class, 1);
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//会发出查询语句,因为二级缓存中的数据被清除了
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
这次查询后不往二级缓存里放东西怎么做,即一级缓存和二级缓存的交互.采用session.setCacheMode()方法
/**
* 开启二级缓存
*
* 一级缓存和二级缓存的交互
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//禁止将一级缓存中的数据放到二级缓存中
session.setCacheMode(CacheMode.IGNORE);
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//会发出查询语句,因为禁止了一级缓存和二级缓存的交互
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
注意大批量数据更新时,如果配置了二级缓存建议禁用一级缓存和二级缓存的交互
/**
* 大批量的数据添加
*/
public void testCache5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//禁止一级缓存和二级缓存交互
session.setCacheMode(CacheMode.IGNORE);
for (int i=0; i<100; i++) {
Student student = new Student();
student.setName("张三" + i);
session.save(student);
//每20条更新一次
if (i % 20 == 0) {
session.flush();
//清除缓存的内容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
hibernate查询缓存
查询缓存缓存什么?
* 查询缓存是缓存普通属性结果集的
* 对实体对象的结果集会缓存id
查询缓存的生命周期,当关联的表发生修改,如又增加了,删除了,或者是update ,查询缓存的生命周期结束
对于查询缓存,不管是同一session还是跨session,那么都是可以缓存到查询缓存里面的.
查询缓存所用到的方法:由于查询缓存是查询普通属性的,所以他的方法是list和iterator,还有就是hql语句相关的.
查询缓存只对query.list()起作用,query.iterate()查询普通属性它不会使用查询缓存
查询缓存这样理解:key相当于是他的条件,如18-25的美女,和查询18-22的是不一样的,所以利用率并不高
map{
key="18,25"
value="sdfsdfsdf"
}
下一次查询的时候
map{
key="18,22"
value="sdgdfs";
}
查询缓存的配置和使用:
* 修改hibernate.cfg.xml文件,来开启查询缓存,默认是false是不起用的,不像二级缓存一样<property name="hibernate.cache.use_query_cache">true</property>
* 必须在程序启用,是用query来设置的query.setCacheable(true)
1.开启查询,关闭二级缓存,采用query.list()查询普通属性,在一个session中发query.list()查询
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
System.out.println("-------------------------------------------------------");
//不会发出查询语句,因为启用查询缓存
names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
2.开启查询,关闭二级缓存,采用query.list()查询普通属性,在两个session中发query.list()查询,在两个session中分别调用:
//不会发出查询语句,因为查询缓存和session的生命周期没有关系
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
3.开启查询,关闭二级缓存,采用query.iterate()查询普通属性,在两个session中发query.iterate()查询,
//会发出查询语句,query.iterate()查询普通属性它不会使用查询缓存
//查询缓存只对query.list()起作用
Iterator iter = session.createQuery("select s.name from Student s")
.setCacheable(true)
.iterate();
while(iter.hasNext()) {
String name = (String)iter.next();
System.out.println(name);
}
4.关闭查询,关闭二级缓存,采用query.list()查询实体,在两个session中发query.list()查询
//会发出查询语句,默认query.list()每次执行都会发出查询语句
List students = session.createQuery("select s from Student s")
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
5.开启查询,关闭二级缓存,采用query.list()查询实体,第二次调用时:
//会发出n条查询语句,因为开启了查询缓存,关闭了二级缓存,那么查询缓存就会缓存实体对象的id
//第二次执行query.list(),将查询缓存中的id依次取出,分别到一级缓存和二级缓存中查询相应的实体
//对象,如果存在就使用缓存中的实体对象,否则根据id发出查询学生的语句
List students = session.createQuery("select s from Student s")
.setCacheable(true)
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
6.开启查询,开启二级缓存,采用query.list()查询实体,在两个session中发query.list()查询
//不再发出查询语句,因为配置了二级缓存和查询缓存,第二次执行query.list(),将查询缓存中的id依次取出,分别到一级缓存和二级缓存中查询相应的实体,查到了
List students = session.createQuery("select s from Student s")
.setCacheable(true)
.list();
for (int i=0; i<students.size(); i++) {
Student studnet = (Student)students.get(i);
System.out.println(studnet.getName());
}
总结:在一级二级缓存中查询实体才会缓存,用的方法:
对于 查询来说有: load/get/iterate查询实体对象,注意list方法他是不会利用缓存的
对于保存来说:save方法也是支持缓存的,他save的时候也会放缓存里面一份的.
在查询缓存中查询普通属性的时候才会缓存,或者用list查询实体的时候缓存id到查询缓存里面去,只对list方法起作用