缓存相关

来源:互联网 发布:淘宝给予警告会降权吗 编辑:程序博客网 时间:2024/06/15 02:54

主要的缓存有:一级缓存,二级缓存,查询缓存

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方法起作用