精通Hibernate——Hibernate缓存详解

Session的缓存时内置的,不能被卸载,称为Hibernate的一级缓存,SessionFactory有一个内置缓存和一个外置缓存,其中外置缓存时可插拔的缓存插件,级为Hibernate二级缓存。第二级缓存本身实现很复杂,必须实现并发访问策略以及数据过期策略。
持久化层缓存的范围
持久化层的缓存范围决定了缓存的生命周期以及可以被谁访问。缓存的范围可以被分为三类
1、事务范围
缓存只被当前事务访问,缓存的生命周期依赖于缓存的生命周期。在同一个事务的缓存中,持久化类的每一个对象具有唯一OID
2、进程范围
缓存被进程内所有事务共享,这些事务有可能并发访问缓存,因此必须对缓存采取必要的事务隔离机制,缓存的生命周期依赖于进程的生命周期。
在进程范围的缓存中,持久化类每一个对象都具有唯一的OID,不同事务到缓存中查询OID为1的对象时,将获得相同的Customer对象,这种存放数据的优点是节省了内存,缺点是不同事务各个线程同时长时间操纵同一个OID为1的数据对象时,必须对这些线程进行同步,而同步会影响并发性能,并且很容易造成死锁。一般不提倡这种数据存放形式。
如果缓存中的数据采用对象散装数据形式,不同事务到缓存中查询OID为1的数据对象时,获得的对象时散装的,每个事务都必须分别根据散装数据重新 构造出对象实例,也就是说每一个事务都会获得不同的对象实例,即每个事务拥有独立的对象,提高了并发性能,无需同步操作。
3、集群范围
在集群环境中,缓存被同一个机器或多个机器上的多个进程共享。缓存中的数据被复制到集群环境中的每一个进程节点,进程间通过远程通信来保证缓存中数据的一致性,缓存中的数据采用散装的形式存放。
进程范围或集群范围缓存,即第二级缓存,会出现并发问题
Hibernate的一级缓存
Hibernate的一级缓存是Session的缓存,二级缓存由SessionFactory管理。
当应用程序调用Session的save、update、saveOrUpdate、load、get或find,以及查询接口list、iterate或filter方法时,如果在Session的缓存中还不存在相应的对象,Hibernate就会该对象加入到第一级缓存中,Hibernate会根据缓存中对象状态变化来同步更新数据库。
Session提供了两个管理缓存的方法:
evict(bject o):从缓存中清除参数指定的持久化对象
clear();清空缓存中所有持久化对象
批量更新或删除的最佳方式是直接调用JDBC API
下面是直接调用Hibernate API批量更新Customer对象

tx  = session.beginTransaction();
Iterator customers = session.find("from Customer c where c.age > 0 ").iterator();
while(customers.hasNext()){
    Customer customer = (Customer)customers.next();
    customer.setAge(customer.getAge()+1);
}
tx.commit();
session.close();

这个批量更新的两个缺点是:
1、占用大量内存,如果有100000Customer对象,必须先把100000个Customer对象加载到内存中,然后遍历更新
2、执行的update语句过多,每次更新只能更新一个Customer对象,频繁访问数据库,会大大降低性能
为了迅速释放100000个Customer对象占用的内存,可以在更新Customer对象后调用Session的evict方法立即释放他的内存。

tx  = session.beginTransaction();
Iterator customers = session.find("from Customer c where c.age > 0 ").iterator();
while(customers.hasNext()){
    Customer customer = (Customer)customers.next();
    customer.setAge(customer.getAge()+1);
    session.flush();// 使Hibernate根据Customer对象状态变化同步更新数据
    session.evict();
}
tx.commit();
session.close();

这种方法稍微了提高了性能,但是还要执行100000条update语句,假如能直接执行

update Customers set age = age+1 where age > 10;

100000条数据只需执行一个update语句,应用程序绕过Hibernate,直接调用JDBC API:

tx = session.beginTransaction();
Connection conn = session.connection();
PreparedStatement stmt = conn.preparedStatement("update Customers set age = age+1 where age > 10");
stmt.executeUpdate();
tx.commit();

如果底层数据库支持存储过程也可(Mysql不支持存储过程):

create or replace procedure batchUpdateCustomer(p_age in number) as 
begin
   update Customers set age = age+1 where age > p_age;
end;

tx = session.beginTransaction();
Connection conn = session.connection();
String procedure = "{call batchUpdateCustomer(?)}";
CallableStatement cstmt = conn.prepareCall(procedure);
cstmt.setInt(10,0);
cstmt.executeUpdate();
tx.commit();

Hibernate的二级缓存
Hibernate的二级缓存是进程或集群范围内的缓存,缓存中存放的是对象的散装数据,Hibernate允许选用以下类型的缓存插件
1、EHCache:可作为进程内缓存,存放数据的物理介质可以是内存或硬盘,支持Hibernate查询缓存
2、OpenSymphony OSCache:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,支持Hibernate查询缓存
3、SwarmCache:可作为集群范围内的缓存,但不支持Hibernate查询缓存
4、JBossCache:可作为集群范围内缓存,支持事务并发访问策略,支持Hibernate查询缓存
Hibernate允许在类和集合的粒度上设置第二级缓存,在映射文件中,class和set元素都有一个cache子元素配置如下:

<class name="com.Category" table="category">
    <cache usage="read-write" />
    <id name="id" type="long" column="ID">
        <generator class="increment" />
    </id>
</class>

使用了读写的并发策略,当多个事务并发执行时,该策略能保证Read Committed事务隔离级别
如果既要缓存Category又要缓存Category对象的items属性配置如下:

<class name="com.Category" table="category">
    <cache usage="read-write" />
    <id name="id" type="long" column="ID">
        <generator class="increment" />
    </id>
    <set name="itmes" inverse="true" lazy="true">
        <cache usage="read-wirte" />
    </set>
</class>

当应用程序调用category.getItems().iterator()方法时,Hibernate会把items集合的元素放在缓存中,此时,Hibernate仅仅把与Category关联的Item对象的OID放到缓存中,如果把整个Item对象放到缓存中,应该在Item.hbm.xml配置如下:

<class name="com.Item" table="item">
    <cache usage="read-write" />
    <id name="id" type="long" column="ID">
        <generator class="increment" />
    </id>
</class>

Session的evict方法用于从一级缓存中清除一个对象,SessionFactory也有evict,用于从二级缓存中清除对象的散装数据,例如:

sessionFactory.evict(Category.class,new Long(1));//清除二级缓存中OID为1的Category对象
sessionFactory.evict("com.Category");//清除二级缓存中所有Category对象
session.Factory.evictCollection("com.Category.items");//清除二级缓存中Category对象的Items集合
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值