Hibernate入门(10):缓存机制

Hibernate 缓存机制

一级缓存和二级缓存

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

1)一级缓存

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

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

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

while(bigObjList.next()){
    BigObject obj = (BigObject)bigObjList.get(0);
    //use obj to do something
    .....
    session.evit(obj);    //将obj从Session一级缓存中剔除
}

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

配置二级缓存
这里以EhCache为例,其他的还用如Radis等第三方实现;
① 将该第三方缓存 jar 包添加到项目依赖中;
② 开启缓存,配置缓存种类;
在 hibernate.cfg.xml 文件中添加以下属性:
true
配置第三方的缓存类,这里使用 EhCache:
org.hibernate.cache.ehcache.EhCacheRegionFactory
③ 完成缓存实现库的自身配置
对于 Ehcache 缓存库,需要在src目录下添加一个xml配置文件,示例如下:
src/ehcahe.xml


       
    
以上一些属性的对照:
maxElementsInMemory="10000" :缓存中放置对象的最大数量
eternal="false"  :缓存是否永久有效
timeToIdleSeconds="120":对象缓存在多少s没有被使用会被清除
timeToLiveSeconds="120":缓存对象在过期前可被缓存多少s
diskPersistent="false":设置缓存是否持久化到硬盘中,由设置路径


 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 中添加以下配置:
true
在程序中调用查询缓存的代码如下:
Session session = HibernateUtil.currentSession();
Transaction tran = session.beginTransaction();

List list = session.createQuery("select u.name,u.age from User u")
                    .setCacheable(true)    //对本次查询开启查询缓存
                    .list();

Iterator iterator = session.createQuery("select u.name,u.age from User u")
                   .setCacheable(true)    //对本次查询开启查询缓存
                   .iterate();

批量处理的内存溢出问题

当在一次事务操作中进行大量的实体插入、更新时(如一次Session事务中向数据库提交1000000个实体的插入),很有可能程序会抛出 OutOfMemoryException 栈溢出异常,如以下程序:
public class test {

    public static void main(String[] args){
        Session session = HibernateUtil.currentSession();
        Transaction tran = session.beginTransaction();
        for(int i=0;i<1000000;i++){
            Users user = new Users();
            .....
            session.save(user);
        }
        HibernateUtil.closeSession();
    }
}
这是由于这些实体实例在事务提交之前,会缓存在Session的一级缓存中,这个一级缓存是有限的;
对于这种情况,一种解决思路是可以在Session保存的一定量的实体后,将Session一级缓存强制flush到数据库,同时清空Session缓存,如下:
public class Test {
    
    public static void main(String[] args){
        Session session = HibernateUtil.currentSession();
        Transaction tran = session.beginTransaction();
        
        for(int i=0;i<1000000;i++){
            User user = new User();
            user.setName("assad");
            user.setAge(new Random().nextInt(80));
            
            session.save(user);    //Session 保存实体
            if(i % 30 == 0){      //每隔保存30个实体,Session缓存强制写入,同时清空
                session.flush();
                session.clear();
            }
        }
        HibernateUtil.closeSession();
    }
}
同时还需要手动关闭 SessionFactory 的 二级缓存,防止实体缓存到二级缓存造成二级缓存溢出;

对于批量更新,如果返回多行数据,可以使用 ScrollableResults 来储存返回结果,利用游标所在带来的新能优势,如下:
public class Test {
    
    public static void main(String[] args){
        Session session = HibernateUtil.currentSession();
        Transaction tran = session.beginTransaction();
        
        //使用ScrollableResults储存结果集
        ScrollableResults users = session.createQuery("from User")
                                        .setCacheMode(CacheMode.IGNORE)
                                        .scroll(ScrollMode.FORWARD_ONLY);
        int count = 0;
        while( users.next()){
            Users user = (Users)users.get(0);
            user.setName("new name"+ count++);
            if( count % 30 == 0){
                session.flush();
                session.clear();
            }
        }
        HibernateUtil.closeSession();
    }
}




你可能感兴趣的:(Hibernate5,Hibernate5,入门教程)