Hibernate缓存的使用

Hibernate缓存的使用

使用缓存能使Hibernate的查询性能获得有效地提升,可以避免数据重复查询带来的性能开销,通过缓存可以实现数据的快速读取,但是并不意味着缓存一定能提升性能,相反,如果处理不当也可能造成致命伤害。
在项目开发之中有两种常见的缓存组件:OSCache、EHCache,其中在Hibernate之中主要使用EHCache,之所以使用此组件主要是考虑到简洁问题,在缓存之中至少需要考虑以下几种情况:缓存的个数、缓存数据如何保存、缓存数据如何同步。
在Hibernate之中主要支持两类缓存:

  1. Session级缓存(一级缓存、first_level-cache),在使用get()或load()方法的时候,对象实际就一直在持久态的状态,这实际上就是一级缓存的配置,但是如果Session关闭了,那么一级缓存的内容将消失,值得注意的是,一级缓存永远存在。
  2. SessionFactory级缓存(二级缓存、second_level-cache) ,指的是跨越Session的存在,可以在多个Session对象之中共享数据,二级缓存是需要额外配置的。

如果Hibernate项目之中需要使用缓存,那么必须在项目创建的时候准备好第三方组件包。

一级缓存

一级缓存是Session级缓存,永远都会存在,也就是说只要用户执行了save()或get()等方法,那么这个对象在Session关闭前会一直被保留下来。
一级缓存的控制操作都在Session接口里定义了,定义有如下几个方法:

方法描述
public void clear()清空所有缓存数据
public void flush() throws HibernateException清空刷新缓冲区
public void evict(Object object)清除一个缓存对象

【范例:观察如何清除一个对象】

import com.gub.dbc.HibernateSessionFactory;
import com.gub.vo.Dept;

public class SessionTest {
    public static void main(String[] args) {
        //查询操作
        Dept temp = (Dept)HibernateSessionFactory.getSession().get(Dept.class,4);
        //将之前查询出来的对象(保存在缓存中)进行缓存的清除
        HibernateSessionFactory.getSession().evict(temp);
        //此时没有关闭Session,再一次发出查询操作,此时缓存没有数据
        System.out.println(HibernateSessionFactory.getSession().get(Dept.class,4));
    }
}

运行后发现发出了两条查询指令,原因在于,第一个对象查询出来之后进行了删除缓存对象的操作,所以在第二次查询同样主键的时候,如果发现数据在缓存里面不存在,那么就将重新发出查询指令。
【范例:批量数据增加】

import com.gub.dbc.HibernateSessionFactory;
import com.gub.vo.Dept;

import java.util.Date;

public class SessionTest {
    public static void main(String[] args) {
        Dept dept = null;
        for(int x=10;x<100;x++){
            dept = new Dept();
            dept.setDeptno(x);
            dept.setDname("部门-"+x);
            dept.setCredate(new Date());
            dept.setAddress("****");
            dept.setTel("0471-48451256");
            HibernateSessionFactory.getSession().save(dept);
            if(x%10==0){//每10条记录进行一次缓存的刷新
                HibernateSessionFactory.getSession().flush();
                HibernateSessionFactory.getSession().clear();
                HibernateSessionFactory.getSession().beginTransaction().commit();
            }
        }
        HibernateSessionFactory.closeSession();
    }
}

Hibernate中的一级缓存会一直存在直到Session的关闭,如果缓存内容过多,程序可能会出现问题,可以使用Session中的flush()与clear()两个方法进行强制性的缓存清空,保证缓存的容量不会占用过多。

二级缓存

在Hibernate默认状态下,每一个Session缓存的数据是不能够进行共享的。如果希望在不同的Session上实现数据缓存的共享,那么就需要使用二级缓存,与一级缓存不同,二级缓存必须由用户单独配置(需要先导入一系列开发包)

  1. 定义缓存配置文件:ehcache.xml文件
<ehcache>
    <!--指的是缓存的临时保存目录-->
    <diskStore path="java.io.tmpdir"/>
    <!--此处进行默认的缓存配置-->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
    />
</ehcache>

通过defaultCache的属性配置缓存的默认信息

属性名描述
maxElementsInMemory可以缓存的最大的POJO对象的个数
eternal是否允许缓存自动失效
timeToIdleSeconds最小的失效时间
timeToLiveSeconds最大的保存时间
overflowToDisk如果保存过多则自动利用磁盘存储缓存
  1. 在hibernate.cfg.xml文件里面配置使用的缓存类型
<property name="hibernate.cache.region.factory_class">
    org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
  1. 并不是所有的POJO类都需要使用到二级缓存,所以应该在支持缓存的位置上进行启用,有以下三种启用的操作形式。

    1. 如果项目是基于*.hbm.xml文件配置的,则可以在*.hbm.xml的class节点内增加以下配置:
           <cache usege="read-only" />
    
    1. 如果项目使用的是Annotation,则可以利用以下注解在POJO类中配置:
           @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    
    1. 考虑到项目可能会修改,如果配置在每一个文件上会比较麻烦,那么可以直接在hibernate.cfg.xml文件里面进行综合的配置(优势:只需要维护一个文件):
         <class-cache class="com.gub.vo.Dept" usage="read-only" />
    

【范例:观察二级缓存】

import com.gub.dbc.HibernateSessionFactory;
import com.gub.vo.Dept;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class SecondCacheTest {
    public static void main(String[] args) {
        SessionFactory sessionFactory = HibernateSessionFactory.getSessionFactory();
        Session sessionA = sessionFactory.openSession();
        //由于二级缓存的存在,此时的查询结果会自动写入二级缓存之中
        System.out.println(sessionA.get(Dept.class,1));
        Session sessionB = sessionFactory.openSession();
        //此时默认先寻找二级缓存中的数据是否存在,如果存在则直接读取
        System.out.println(sessionB.get(Dept.class,1));
    }
}


此时发现两个不同的Session按相同的索引执行查询操作时,只执行了一条查询语句,说明二级缓存使用成功。
二级缓存操作的常用模式有以下几种:

模式说明
READ_ONLY(只读缓存)将数据查询结果保存在缓存之中,以供其他的Session使用
READ_WRITE(读写缓存)当数据库里的数据发生变化之后,那么可是进行及时的数据追踪,缓存性能最差
NONSTRICT_READ_WRITE(不严格的读写操作)在数据库发生变化之后不会立即进行缓存的更新,而是等待一段时间之后在进行数据的更新操作
TRANSACTIONAL(事物的处理缓存)如果数据库中的数据发生了回滚操作,内存中的数据也要一起发生回滚操作

缓存交互

项目中如果配置了二级缓存,只要是通过Session查询的数据都会默认保存在二级缓存之中以供其他Session使用,但这些都是默认的配置形式,用户也可以根据自己的需要来配置是否需要二级缓存,配置的方法主要是在Session接口中:

方法名描述
public void setCacheMode(CacheMode cacheMode)设置缓存模式

CacheMode 属于枚举类型,其定义了如下几种缓存模式:

名称说明
public static final CacheMode GET从缓存读取数据,但是不保存
public static final CacheMode PUT向缓存保存数据,但是不能取出
public static final CacheMode NORMAL正常进行缓存数据的读或写
public static final CacheMode REFRESH针对于缓存中的重复数据进行刷新操作

【范例:观察GET模式】

import com.gub.dbc.HibernateSessionFactory;
import com.gub.vo.Dept;
import org.hibernate.CacheMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class CacheInteraction {
    public static void main(String[] args) {
        SessionFactory sessionFactory = HibernateSessionFactory.getSessionFactory();
        Session sessionA = sessionFactory.openSession();
        sessionA.setCacheMode(CacheMode.GET);//此时不向二级缓存写入数据,值进行读取
        System.out.println(sessionA.get(Dept.class,1));
        Session sessionB = sessionFactory.openSession();
        //此时默认先寻找二级缓存中的数据是否存在,如果存在则直接读取
        System.out.println(sessionB.get(Dept.class,1));
    }
}

执行后发现数据库进行了两次查询操作,因为缓存模式为GET,所以内容不会像二级缓存中保存。

查询缓存

以上的一些列缓存操作都是基于Session完成的,但是在实际开发中,数据的查询一般都通过Query接口来完成,但是如果利用Query查询,配置的二级缓存无效,也就是说此时没有使用到缓存的配置,因为Query接口默认情况下并没有启动缓存的支持,在Query接口中定义了如下方法:

方法名描述
public Query setCacheable(boolean cacheable)配置是否支持缓存
public Query setCacheMode(CacheMode cacheMode)配置缓存模式

默认情况下Hibernate没有启动查询缓存的支持,所以如果想要使用查询缓存,还要再hibernate.cfg.xml文件中添加以下属性:

<property name="cache.use_query_cache">true</property>

【范例:Query接口使用二级缓存】

import com.gub.dbc.HibernateSessionFactory;
import com.gub.vo.Dept;
import org.hibernate.CacheMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class QueryCache {
    public static void main(String[] args) {
        String hql = "FROM Dept AS d WHERE d.deptno=?";
        SessionFactory sessionFactory = HibernateSessionFactory.getSessionFactory();
        //sessionA查询数据
        Session sessionA = sessionFactory.openSession();
        Query queryA = sessionA.createQuery(hql);
        queryA.setCacheable(true);
        queryA.setCacheMode(CacheMode.PUT);
        queryA.setParameter(0,1);
        Dept deptA = (Dept)queryA.uniqueResult();
        System.out.println("deptA = " + deptA);
        //session查询数据
        Session sessionB = sessionFactory.openSession();
        Query queryB = sessionB.createQuery(hql);
        queryB.setCacheable(true);
        queryA.setCacheMode(CacheMode.GET);
        queryB.setParameter(0,1);
        Dept deptB = (Dept)queryB.uniqueResult();
        System.out.println("deptB = " + deptB);
    }
}

此时发现数据库只执行了一条查询语句,缓存使用成功。

对于Criteria查询也有相应的方法支持,与Query查询的使用方法相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hibernate中的query 博客分类: • Hibernate HibernateSQLSQL ServerMySQLJDBC Hibernate中的query.setFirstResult(),query.setMaxResults(); 一、query.scroll()和query.setFirstResult(),query.setMaxResults();这两种方法都可以取到一定范围内的数据,用来数据分页显示。那么两者区别,以及两者的效率如何? 答:1.scroll是用JDBC2.0的可滚动结果集实现;query.setMaxResults();query.setFirstResult()是数据库SQL语句实现。 2.你说是在数据库就分页好呢?还是把结果集都取到内存再分页好呢?(应该是在数据库就分了好些吧,但是如果在内存分页的话,换页的时候是不是更快一些呢?) 3.在数据库进行分页是首选的方式。数据库分页实际上是利用数据库本身SQL扩展的功能进行分页,例如MySQL的 limit 0,50这样的SQL语句。不但速度快,而且非常节省内存。不过不是每种数据库的都有这种分页支持的SQL,例如SQL Server就不支持。 4.scroll是利用JDBC2.0的功能做分页的,那么就完全取决于特定数据库的JDBC Driver的实现了。事实上大部分JDBC Driver都是把所有的结果集都一次取到内存,然后再分页的。如果这个结果集非常大,例如几万条,不但程序执行速度会很慢,而且很容易导致out of memory。当然个别JDBC Driver使用了服务器端游标来实现,那么就不会导致这种问题,例如jTDS。 二、Hibernate可以使用Query.setMaxResults方法简单地设置需要查询的最大结果集。 然后Hibernate会自动根据所设置的数据库方言翻译成相应的SQL语句提交给数据库。比如如果数据库是Oracle,SQL Server等,则翻译为类似select ... top 10之类的SQL语句,若是MySQL,则翻译为select ... limit 10之类的SQL。 三、举例: Java代码 1. import org.hibernate.Query; 2. 3. query.setFirstResult(0),query.setMaxResults(4);相当于MySQL中的limit 0, 4; 4. public void testQuery() { 5. Session session = null; 6. try { 7. session = HibernateUtils.getSession(); 8. session.beginTransaction(); 9. Query query = session.createQuery("from User"); 10. query.setFirstResult(0);//从第一条记录开始 11. query.setMaxResults(4);//取出四条记录 12. List userList = query.list(); 13. for (Iterator iter=userList.iterator(); iter.hasNext();) { 14. User user = (User)iter.next(); 15. System.out.println(user.getId()); 16. System.out.println(user.getName()); 17. } 18. session.getTransaction().commit(); 19. }catch(Exception e) { 20. e.printStackTrace(); 21. session.getTransaction().rollback(); 22. }finally { 23. HibernateUtils.closeSession(session); 24. } 25. }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值