Hibernate入门(10):缓存机制

来源:互联网 发布:自动化控制软件 编辑:程序博客网 时间:2024/06/03 19:03

Hibernate 缓存机制

一级缓存和二级缓存

Hibernate 的缓存包括2个级别的缓存:
  • 一级缓存:Session 级别,默认开启;
  • 二级缓存:SessionFactory 级别,可选;

1)一级缓存

Session 级别的缓存总是开启的,在开发过程中一般不需要进行控制,当应用保存持久化实体时,Session 不会立即把这种改变 flush 到数据库,而是缓存在 Session 一级缓存中,直到一级缓存区放满 ,或者调用Session.close()  Session.flush()时,缓存区中的数据才会一次性地 flush 到数据库。
Session 这种缓存方式方式可以减少程序对数据库的连接次数,已减少数据库连接资源的占用,提高数据库访问性能;

Session 还提供了以下的一系列方法用于一级缓存操作:
voidflush()将 Session 缓存中的对象强制 flush 到底层数据库voidevict(Object obj)将对象 obj 从 Session 缓存中剔除booleancontains(Object obj)返回 Session 缓存中是否含有某个对象void clear()清除 Session 缓存中的所有对象
更多方法参见 API;

在一些特殊情况下,比如处理一个大对象(占用很大内存)时,可以调用 Session.evict (Object obj)方法,将对象从一级缓存中剔除,如以下示例:

2)二级缓存
SessionFactory 级别的缓存是全局性的,所有的 Session 共享该缓存,默认关闭,需要显式开启;
一旦开启二级缓存,程序中 Session 要查询数据时,会先查找一级缓存,再查找二级缓存,只有但一级、二级缓存中都不存在需要的数据时,再查找底层数据库;

配置二级缓存
这里以EhCache为例,其他的还用如Radis等第三方实现;
① 将该第三方缓存 jar 包添加到项目依赖中;
② 开启缓存,配置缓存种类;
在 hibernate.cfg.xml 文件中添加以下属性:
1
<property name="hibernate.cache.use_second_level_cache">true</property>
配置第三方的缓存类,这里使用 EhCache:
③ 完成缓存实现库的自身配置
对于 Ehcache 缓存库,需要在src目录下添加一个xml配置文件,示例如下:
src/ehcahe.xml
1
<?xml version='1.0' encoding='UTF-8'?>
2
<ehcache>
3
    <diskStore path="java.io.tmpdir"/>   
4
    <defaultCache
5
        maxElementsInMemory="10240"     
6
        eternal="false"
7
        overflowToDisk="true"
8
        timeToIdleSeconds="120"
9
        timeToLiveSeconds="120"
10
        diskPersistent="false"
11
    />
12
</ehcache>
以上一些属性的对照:
maxElementsInMemory="10000" :缓存中放置对象的最大数量
eternal="false"  :缓存是否永久有效
timeToIdleSeconds="120":对象缓存在多少s没有被使用会被清除
timeToLiveSeconds="120":缓存对象在过期前可被缓存多少s
diskPersistent="false":设置缓存是否持久化到硬盘中,由<diskStore>设置路径


 Cache 类用于对二级缓存进行管理,该对象由 SessionFactory.getChache() 方法获取;
evictEntity(Class classType, Serializable id)在二级缓存中 消除指定的实体的指定id实例
evictEntity("User.class",id)evictEntityRegion(Class classType)在二级缓存中 消除指定实体的所有实例evictCollection(String role, Serializable ownerIdentifier)在二级缓存中 消除指定id的 实体关联实体实例,如:
evictEntity("User.class")evictCollection(String role)在二级缓存中 消除所有的指定的 实体关联实体实例,如:
evictCollection("User.Article")



查询缓存


一级、二级缓存是针对整个实体进行缓存的,它不会缓存普通属性,如果需要对普通属性进行缓存,可以使用查询缓存;

默认查询缓存是关闭的,开启查询缓存,需要在 hibernate.cfg.xml 中添加以下配置:
在程序中调用查询缓存的代码如下:
1
Session session = HibernateUtil.currentSession();
2
Transaction tran = session.beginTransaction();
3
4
List list = session.createQuery("select u.name,u.age from User u")
5
                    .setCacheable(true)    //对本次查询开启查询缓存
6
                    .list();
7
8
Iterator iterator = session.createQuery("select u.name,u.age from User u")
9
                   .setCacheable(true)    //对本次查询开启查询缓存
10
                   .iterate();



批量处理的内存溢出问题

当在一次事务操作中进行大量的实体插入、更新时(如一次Session事务中向数据库提交1000000个实体的插入),很有可能程序会抛出 OutOfMemoryException 栈溢出异常,如以下程序:
这是由于这些实体实例在事务提交之前,会缓存在Session的一级缓存中,这个一级缓存是有限的;
对于这种情况,一种解决思路是可以在Session保存的一定量的实体后,将Session一级缓存强制flush到数据库,同时清空Session缓存,如下:
1
public class Test {
2
    
3
    public static void main(String[] args){
4
        Session session = HibernateUtil.currentSession();
5
        Transaction tran = session.beginTransaction();
6
        
7
        for(int i=0;i<1000000;i++){
8
            User user = new User();
9
            user.setName("assad");
10
            user.setAge(new Random().nextInt(80));
11
            
12
            session.save(user);    //Session 保存实体
13
            if(i % 30 == 0){      //每隔保存30个实体,Session缓存强制写入,同时清空
14
                session.flush();
15
                session.clear();
16
            }
17
        }
18
        HibernateUtil.closeSession();
19
    }
20
}
同时还需要手动关闭 SessionFactory 的二级缓存,防止实体缓存到二级缓存造成二级缓存溢出;


对于批量更新,如果返回多行数据,可以使用 ScrollableResults 来储存返回结果,利用游标所在带来的新能优势,如下:
1
public class Test {
2
    
3
    public static void main(String[] args){
4
        Session session = HibernateUtil.currentSession();
5
        Transaction tran = session.beginTransaction();
6
        
7
        //使用ScrollableResults储存结果集
8
        ScrollableResults users = session.createQuery("from User")
9
                                        .setCacheMode(CacheMode.IGNORE)
10
                                        .scroll(ScrollMode.FORWARD_ONLY);
11
        int count = 0;
12
        while( users.next()){
13
            Users user = (Users)users.get(0);
14
            user.setName("new name"+ count++);
15
            if( count % 30 == 0){
16
                session.flush();
17
                session.clear();
18
            }
19
        }
20
        HibernateUtil.closeSession();
21
    }
22
}
23



原创粉丝点击