您的位置:首页 > 其它

Hibernate:二级缓存

2013-11-10 16:11 148 查看
一级缓存:session级别的缓存,hibernate提供的。内置

二级缓存:sessionFactory级别的缓存,hibernate没有提供。需要第三方

准备工作

配置时的参考文件:

hibernate-distribution-3.6.10.Final\project\etc\hibernate.properties

* 1 集成c3p0连接池

* hibernate默认使用的自己的连接池,DriverManagerConnectionProvider,性能不佳

* 1.1导入c3p0jar包,必须是hibernate提供的jar

* 位置:%h%/lib/optional/c3p0/c3p0-0.9.1.jar

* 1.2在cfg.xml进行配置(注册) , hibernate链接提供实现类

* key:hibernate.connection.provider_class

value:org.hibernate.connection.C3P0ConnectionProvider

* 1.3配置c3p0(可选)

#hibernate.c3p0.max_size 2

#hibernate.c3p0.min_size 2

#hibernate.c3p0.timeout 5000

#hibernate.c3p0.max_statements 100

#hibernate.c3p0.idle_test_period 3000

#hibernate.c3p0.acquire_increment 2

#hibernate.c3p0.validate false

* 2 事务

* 回顾

* 特性:ACID (原子性、一致性、隔离性、持久性)

* 数据库隔离性产生隔离问题:

* 脏读:一个事务读到另一个事务没有提交的数据

* 不可重复读:一个事务读到另一个事务已经提交的数据(insert)

* 幻读:一个事务读到另一个事务已经提交的数据(update)

* 数据库事务的隔离级别

* 读未提交:read uncommitted

* 读已提交:read committed

* 可重复读:repeatable read

* 串行化:serializable

* hibernate可以对事务的隔离级别进行设置

* cfg.xml配置:hibernate.connection.isolation4

* hibernate对隔离级别的映射关系

* 读未提交:read uncommitted 1

* 读已提交:read committed 2

* 可重复读:repeatable read 4

* 串行化:serializable 8

* 3 管理session

* 管理方式(3中)

* 1 session生命周期与本地线程绑定(ThreadLocal线程本地遍历) --> ActionContext.getContext()

* 2 session生命周期与 JTA 事务绑定

* 3 委托管理

* 配置session与本地线程绑定

* 配置cfg.xml

*hibernate.current_session_context_class thread

* SessionFactory.getCurrentSession() 获得当前线程中的session对象

*默认不能使用

* 事务提交时,hibernate 从本地线程获得session,并关闭

需要配置项汇总:

1,配置c3p0,

2,配置hibernate事务的隔离级别

3,配置session线程与本地线程绑定

* 二级缓存入门

* 缓存:数据库中数据库的拷贝,物理介质通常是内存。如果内存不足,将会把内存中的数据暂存在硬盘中。

* 并发访问策略

transactional(事务型)

read-write(读写型)

nonstrict-read-write(非严格读写型)

read-only(只读型)

* 提供商

* EHCache 进程范围内的缓存,存放数据的物理介质可以是内存或硬盘

* OpenSymphony 进程范围内的缓存

* SwarmCache:集群范围内的缓存

* JBossCache:集群范围内的缓存

* 二级缓存的配置

* 1 导入jar包

* 核心jar : ehcache-1.5.0.jar

* 依赖jar

* backport-util-concurrent-2.1.jar

* commons-logging-1.1.1.jar

* 2 通知hibernate开启二级缓存

* 在cfg.xml配置 :hibernate.cache.use_second_level_cache = true

* 3 通知hibernate 需要使用二级缓存的供应商

* 在cfg.xml配置 : key:hibernate.cache.provider_class

value: org.hibernate.cache.EhCacheProvider

* 4 指定使用二级缓存类

* 第一种方式:hbm.xml文件进行配置(了解) <class><cache>

* 第二种方式:cfg.xml文件配置

* PO类:<class-cacheusage="read-write" class=""/>

* 集合对象:<collection-cacheusage="read-write" collection=""/>

* 5 添加ehcache配置文件

* 第一种

* 文件名称:ehcache-1.5.0.jar根目录,ehcache-failsafe.xml ,将此文件的名称修改为:ehcache.xml

* 使用位置:src

* 第二种

* 参考位置:%h%/project/etc/ehcache.xml

* 二级缓存细节描述

* 缓存数据区域

*类级别

* 集合级别

* 时间戳

* 查询缓存

* 二级缓存,保存类级别数据时,将以散装数据形式保存。(只缓存数据,不缓存对象)

* query.list() 可以将数据放置到一级缓存和二级缓存,但不能从一级缓存和二级缓存中获取。

* 集合级别,只保存OID

* 时间戳:用于记录数据是否已经变更,如果变更将更新缓存。

* 查询缓存:将查询语句 与 查询数据 对应缓存。使用Query语句进行查询(Criteria语句)

* 注意:hibernate默认查询缓存不使用

* 1 cfg.xml配置查询缓存 :hibernate.cache.use_query_cache true

* 2 每一次查询时都需要注明,需要使用查询缓存。

query.setCacheable(true);

* 二级缓存配置文件:ehcache.xml

* <diskStore 用于配置缓存文件位置

* <defaultCache 默认缓存配置

* <cache 自定义缓存配置

二级缓存细节详解:

* 类级别



1,session1调用get方法获取Person对象实例,先从缓存中找,

2,缓存中没有实例,就去数据库找,

3,从数据库获取数据后,先将数据存放到缓存,如果设定缓存为类级别,则将数据存放在类级别缓存区,注意,类级别缓存区存放的不是对象,而只是对象里的数据,

4,将缓存的数据封装成对象返还给用户,相当于new了一个Person,这时session1调用close方法释放资源。一级缓存这时已经不存在了。

5,再开启一个session2链接,调用这个链接的get方法获取Person数据,这时会先到一级缓存中获取,一级缓存中没有,则到二级缓存中获取,

6,发现二级缓存中有数据,就直接取出,不再查询数据库(通过debug可以发现,在这里不再生成sql语句,也就说明了没有再次查询数据库)。

注意:

(1),先建立一个session,获取数据后将数据存放到了一级缓存和二级缓存,再将这个链接关闭。之后再次开启一个session链接,获取同样的数据,在没有查询数据库的情况下获取到了数据,说明了二级缓存的存在,因为此时一级缓存中已经没有相关数据了。

(2),获取的两个Person对象,会发现散列码不同,说明内存地址不同,不是同一个对象,为什么呢?因为类级别缓存只是存储对象中的数据,并不存对象,所以get只能获取Person里的数据,要变成对象,需要使用数据new一个,所以这里相当于new了两个新的person对象,所以获取到的不是同一个对象。

* 集合级别

在本例中,Person对象的PO中保存了一个set集合,存储这个Person的所有订单项,比如有3个订单。

为Person和Order设置类级别的缓存,为orderSet设置集合级别的缓存。测试!

然后再将Order的类级别缓存取消掉,再测试!

程序部分代码:

Sessionsession = SessionUtils.openSession();//获取session

Transactiontransaction = session.beginTransaction();//开启事务

Personp1 = (Person)session.get(Person.class,1);//获取id为1的Person对象,

Set<Order>orderSet = p1.getOrderSet();

Sop(orderSet.size());//查询所有的订单

for(Orderorder : orderSet) {

sop(order.getPrice());

}

transaction.commit();

session.close();

***************************************

Sessionsession2 = SessionUtils.openSession();//再次获取一个session,

Transactiontransaction2 = session2.beginTransaction();开启新的事务

Personp2 = (Person)session.get(Person.class,1);//获取id为1的Person对象,缓存获取

Set<Order>orderSet2 = p2.getOrderSet();

Sop(orderSet2.size());//查询所有的订单

for(Orderorder : orderSet2) {

sop(order.getPrice());

}

transaction.commit();

session2.close();



1,session调用get方法从缓存中获取数据,缓存中没有,就去DB中获取,获取以后存放到二级缓存的类级别缓存中,存储的只是对象中的数据,并将Person对象生成返还给用户。

2,调用person的getOrderSet().size()方法,会自动去集合级别缓存中获取集合中的对象,

3,集合级别缓存中没有数据,hibernate就生成了一条sql语句,到数据库中获取,因为Order设置了类级别缓存,而且orderSet设置了集合级别缓存,所以从数据库返回的数据会把所有的这3个Order对象的中的数据存放在类级别缓存中,

4,并在集合级别缓存中存储orderSet集合中每个元素的OID,session关闭连接。

5,由于获取p2时获取的是与p1相同的对象,所以直接从缓存中获取到p2对象,不去数据库查询,new了一个新的p2对象之后,调用了p2的getOrderSet().size()方法,与之前一样,会从集合级别缓存中获取数据,由于获取的是相同的集合,所以可以拿到所有的OID,

6,hibernate会根据这些OID去类级别缓存中查找对应的对象数据,如果查找到,则直接生成对象返回给用户集合,不再生成查询sql语句去数据库查询,用户就可以进行集合操作了,比如调用size()方法。

7,这时在hibernate.cfg.xml文件中取消掉Order的类级别缓存。再进行这个流程的操作,通过debug观察。之前的1-4步都是相同结果。

8,第5步时,p2要去集合级别缓存获取数据,同样能获取到集合中元素的OID,但是第6步拿着OID去类级别缓存中查询时会查不到数据,因为Order的类级别缓存已经取消。这时hibernate就会根据这些OID再次去数据库查询,但这次要根据所有的OID查询,现在有三个对象的OID,所以要生成3条sql语句查询所以,缓存级别要设置得当,不然反而会造成效率的降低。

* 时间戳

依然是Person的例子,这次通过query的HQL语句更新了数据库的Person的数据,然后再次查询Person的数据,观察结果,发现查到的是更新后的数据。但在此查询的时候应该是去缓存取数据,可是缓存中的数据并没有改变,改变的只是数据库的数据,那么hibernate是如何确保获取的数据是正确的呢?就用到了时间戳。

Sessionsession = SessionUtils.openSession();

Transactiontransaction = session.beginTransaction();

Personp1 = (Person)session.get(Person.class,1);//获取id为1的person数据,执行查询

Sop(p1.getName());//打印数据

session.createQuery(“updatePerson set name=:name where id=:id”)

.setString(“name”,“小强”)

.setInteger(“id”,1).executeUpdate();

transaction.commit();

session.close();

**********************************

Sessionsession2 = SessionUtils.openSession();

Transactiontransaction2 = session2.beginTransaction();

Personp2 = (Person)session2.get(Person.class,1);//一级缓存已经不存在,从二级缓存获取

Sop(p2.getName());

transaction2.commit();

session2.close();

图示说明:



1,session.get(Person.class,1)从缓存中获取Person数据,缓存中没有,去DB中获取数据,并将数据返回到二级缓存的类级别缓存中,只保留数据,不保存对象,

2,同时会在类缓存区的这个对象的数据上生成一个时间戳,T=t1,并在时间戳缓存区中生成这个对象相同的时间戳,再根据数据new一个对象返回给p1,用户获取到实例。

3,通过session.createQuery建立更新语句,直接更新了数据库中的数据,将name修改为小强,4,同时会修改时间戳区的数据,将T=t1更新为T=t2,

5,关闭了session,再次开启一个session2,通过session2再次查询这个person对象,

6,这时hibernate会先比较时间戳区域的T(t2)与类级别缓存区的时间戳T(t1),比较t2是否大于t1,如果大于,则说明肯定修改了数据,hibernate就会再次进行查询,将类级别区的数据修改为更新后的数据,并根据数据生成对象,返回给用户。

* 查询缓存

将查询语句与对应的查询数据进行缓存(使用Query)。也就是说,本次执行了一条HQL查询,

如Query query = session.createQuery(“from Person”)

并设置使用查询缓存:query.setCacheable(true),则下次再通过query执行“from Person”时,就不再生成查询的sql语句了,直接从缓存中获取,有时也称为三级缓存。

注意:hibernate默认查询缓存不使用

1cfg.xml配置查询缓存 : hibernate.cache.use_query_cache true

2 每一次查询时都需要注明,需要使用查询缓存。

示例:

@Test

public void demo10(){

/* 默认Query
每次都查询,如果不添加setCacheable(true),则每次都生成sql查询

* 查询缓存

*/

Session session =SessionUtils.openSession();

Transaction transaction =session.beginTransaction();

Query query =session.createQuery("from Person");

query.setCacheable(true); //将查询语句 与 结果 缓存到 查询缓存中

List<Person> all = query.list();

System.out.println(all);

transaction.commit();

session.close();

/**************************************************************/

Session session2 =SessionUtils.openSession();

Transaction transaction2 = session2.beginTransaction();

Query query2 =session2.createQuery("from Person");

query2.setCacheable(true); //从查询缓存中 获取数据

List<Person> all2 = query2.list();

System.out.println(all2);

transaction2.commit();

session2.close();

}

二级缓存的配置文件:ehcache.xml文件

位置:src下

<diskStore>默认值为:java.io.tmpdir,用于配置缓存文件位置,系统临时文件的位置,

使用System.getProperty("java.io.tmpdir")可以获取, 可以直接指定自己的目录

<defaultCache>默认缓存配置

maxElementsInMemory:设置在内存中可以缓存对象的最大数量

<cache>自定义缓存配置

在hibernate的ehcache.xml文件中可以看到更多的信息,在hibernate解压目录的project\etc\ehcache.xml中

向系统目录写缓存文件的时候会比较慢,一旦jvm停止,就不再继续写入,所以在java项目下可能看到很多0kb的缓存文件,但在javaweb项目中一般就不会有事了,因为服务器启动的时间一般足够长。一般要留一些时间给jvm去写缓存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: