hibernate缓存机制分析

本文详细解析了Hibernate的一级缓存、二级缓存及查询缓存机制,包括配置方法、工作原理及其应用策略。


在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别)、二级缓存(sessionFactory级别)以及查询缓存。

一.一级缓存(session级别)

我们来看看hibernate提供的一级缓存:

		/**
		 * 此时会发出一条sql,将30个用户全部查询出来,并放到session的一级缓存当中
		 * 当再次查询用户信息时,会首先去缓存中看是否存在,如果不存在,再去数据库中查询
		 * 这就是hibernate的一级缓存(session缓存)
		 */
        Session session = sessionFactory.getCurrentSession();
        List<User> ls = (List<User>)session.createQuery("from User order by name").setFirstResult(0).setMaxResults(30).list();
        User user1 = (User)session.load(User.class, "5a4566e09d8d4c5a84c2f0bff1f66c44");//session存在此用户对象,不用再去数据库中查询
        System.out.println("user1:"+user1.getName());
        User user2 = (User)session.load(User.class, "f5e449538b0f4e508b9fdf454b9d0645");//session不存在此用户对象,要去数据库中查询
        System.out.println("u1:"+user2.getName());
我们来看看控制台输出:

Hibernate: 
    select
        top 30 user0_.ID as ID0_,
        user0_.CREATEDATETIME as CREATEDA2_0_,
        user0_.MODIFYDATETIME as MODIFYDA3_0_,
        user0_.NAME as NAME0_,
        user0_.PWD as PWD0_ 
    from
        dbo.BD_TestUser user0_ 
    order by
        user0_.NAME
user1:哈宇华帝0
Hibernate: 
    select
        user0_.ID as ID0_0_,
        user0_.CREATEDATETIME as CREATEDA2_0_0_,
        user0_.MODIFYDATETIME as MODIFYDA3_0_0_,
        user0_.NAME as NAME0_0_,
        user0_.PWD as PWD0_0_ 
    from
        dbo.BD_TestUser user0_ 
    where
        user0_.ID=?
u1:哈宇华帝10057

我们看到此时hibernate发出两条 sql 语句,因为第一个sql会将30个User的对象查询出来,放到session的一级缓存中去,当如果需要再次查询User对象时,此时首先会去缓存中看是否存在该对象,如果存在,则直接从缓存中取出,就不会再发sql了,如user1。如果不存在,则会再发sql去数据库查询。但是要注意一点:hibernate的一级缓存是session级别的,所以如果session关闭后,缓存就没了,此时就会再次发sql去查数据库

二.二级缓存(sessionFactory级别)

使用hibernate二级缓存,我们首先需要对其进行配置,配置步骤如下:

1.hibernate并没有提供相应的二级缓存的组件,所以需要加入额外的二级缓存包,常用的二级缓存包是EHcache。添加jar包

1.ehcache-2.10.3.jar

2.slf4j-api-1.7.7.jar

3.hibernate-ehcache-4.1.7.Final.jar

4.ehcache-core-2.4.3.jar

  在pom.xml添加加载包

        <!-- hibernate二级缓存组件 -->
		<dependency>
		    <groupId>net.sf.ehcache</groupId>
		    <artifactId>ehcache</artifactId>
		    <version>2.10.3</version>
		</dependency>
        <dependency>  
            <groupId>org.hibernate</groupId>  
            <artifactId>hibernate-ehcache</artifactId>  
            <version>4.1.7.Final</version>  <!-- 版本要与 hibernate的版本一致-->
        </dependency> 
2.这里是在spring与hibernate的整合环境里,所在spring.xml的sessionFactory里配置二级缓存信息

	<!-- 配置sessionFactory -->
	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="hibernateProperties">
			<props>
				<!-- web项目启动时是否更新表结构 -->
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<!-- 系统使用的数据库方言,也就是使用的数据库类型 -->
				<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
				<!-- 是否打印Hibernate生成的SQL到控制台 -->
				<prop key="hibernate.show_sql">true</prop>
				<!-- 是否格式化打印出来的SQL -->
				<prop key="hibernate.format_sql">true</prop>
				<prop key="current_session_context_class">thread</prop>
                <!-- 开启二级缓存 -->
				<prop key="hibernate.cache.use_second_level_cache">true</prop>
				 <!--启动查询缓存 -->
				<prop key="hibernate.cache.use_query_cache">true</prop>
				<!-- 二级缓存的提供类 在hibernate4.0版本以后我们都是配置这个属性来指定二级缓存的提供类-->
				<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
				<!-- 二级缓存配置文件的位置 -->
				<prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</prop>
			</props>
		</property>
		<!-- 自动扫描注解方式配置的hibernate类文件 -->
		<property name="packagesToScan">
			<list>
				<value>com.cosconet.model</value>
			</list>
		</property>
	</bean>
我这里使用的是hibernate4.1.7版本,如果是使用hibernate3的版本的话,那么二级缓存的提供类则要配置成这个:
<!--这个类在4.0版本以后已经不建议被使用了-->
<property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>
3.配置hibernate的二级缓存是通过使用 ehcache的缓存包,所以我们需要创建一个 ehcache.xml 的配置文件,来配置我们的缓存信息,将其放到项目根目录下

<ehcache>
       <!--指定二级缓存存放在磁盘上的位置-->
    <diskStore path="user.dir"/>  

       <!--我们可以给每个实体类指定一个对应的缓存,如果没有匹配到该类,则使用这个默认的缓存配置-->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"/>
  
      <!--可以给每个实体类指定一个配置文件,通过name属性指定,要使用类的全名-->
    <cache name="com.cosconet.model.User"
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"/>
</ehcache>
    1、timeToLiveSeconds的定义是:以创建时间为基准开始计算的超时时长;
    2、timeToIdleSeconds的定义是:在创建时间和最近访问时间中取出离现在最近的时间作为基准计算的超时时长;
    3、如果仅设置了timeToLiveSeconds,则该对象的超时时间=创建时间+timeToLiveSeconds,假设为A;
    4、如果没设置timeToLiveSeconds,则该对象的超时时间=max(创建时间,最近访问时间)+timeToIdleSeconds,假设为B;
    5、如果两者都设置了,则取出A、B最少的值,即min(A,B),表示只要有一个超时成立即算超时。

           6、maxElementsInMemory="10000"在内存中存放的最大对象数

           7、eternal="false"是否永久保存缓存,设置成false

           8、overflowToDisk="true"如果对象数量超过内存中最大的数,是否将其保存到磁盘中,设置成true
4.配置实体类使用二级缓存

①如果使用xml配置,我们需要在 Student.hbm.xml 中加上一下配置,例如:

<hibernate-mapping package="com.xiaoluo.bean">
    <class name="Student" table="t_student">
        <!-- 二级缓存一般设置为只读的 -->
        <cache usage="read-only"/>
        <id name="id" type="int" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name" type="string"></property>
        <property name="sex" column="sex" type="string"></property>
        <many-to-one name="room" column="rid" fetch="join"></many-to-one>
    </class>
</hibernate-mapping>

二级缓存的使用策略一般有这几种:read-only、nonstrict-read-write、read-write、transactional。注意:我们通常使用二级缓存都是将其配置成 read-only ,即我们应当在那些不需要进行修改的实体类上使用二级缓存,否则如果对缓存进行读写的话,性能会变差,这样设置缓存就失去了意义。

②如果使用annotation配置,我们需要在实体类上加上这样一个注解:

@Entity
@Table(name = "BD_TestUser", schema = "dbo")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)//  表示开启二级缓存,并使用read-only策略
public class User implements java.io.Serializable {

    // Fields
    private String id;
    private String name;
    private String pwd;
    private Date createdatetime;
    private Date modifydatetime;
    
    .............
}
这样我们的二级缓存配置就算完成了,接下来我们来通过测试用例测试下我们的二级缓存是否起作用。

5.测试二级缓存

测试代码:

        Session session = sessionFactory.getCurrentSession();
        List<User> ls = (List<User>)session.createQuery("from User order by name").setFirstResult(0).setMaxResults(30).list();
        Session session1 = sessionFactory.openSession();//开启一个新的session
        User user1 = (User)session1.load(User.class, "5a4566e09d8d4c5a84c2f0bff1f66c44");//session存在此用户对象,不用再去数据库中查询
        System.out.println("user1:"+user1.getName());
        session1.close();
开启二级缓存时,运行时控制台:

Hibernate: 
    select
        top 30 user0_.ID as ID0_,
        user0_.CREATEDATETIME as CREATEDA2_0_,
        user0_.MODIFYDATETIME as MODIFYDA3_0_,
        user0_.NAME as NAME0_,
        user0_.PWD as PWD0_ 
    from
        dbo.BD_TestUser user0_ 
    order by
        user0_.NAME
user1:哈宇华帝0
只有一个sql,说明在一个新的session里查询user1,没有再去数据查询。二级缓存里存在,就从二级缓存里取。

把二级缓存关闭,

				<prop key="hibernate.cache.use_second_level_cache">false</prop>
再运行,结果如下:

Hibernate: 
    select
        top 30 user0_.ID as ID0_,
        user0_.CREATEDATETIME as CREATEDA2_0_,
        user0_.MODIFYDATETIME as MODIFYDA3_0_,
        user0_.NAME as NAME0_,
        user0_.PWD as PWD0_ 
    from
        dbo.BD_TestUser user0_ 
    order by
        user0_.NAME
Hibernate: 
    select
        user0_.ID as ID0_0_,
        user0_.CREATEDATETIME as CREATEDA2_0_0_,
        user0_.MODIFYDATETIME as MODIFYDA3_0_0_,
        user0_.NAME as NAME0_0_,
        user0_.PWD as PWD0_0_ 
    from
        dbo.BD_TestUser user0_ 
    where
        user0_.ID=?
user1:哈宇华帝0
执行了两个sql,虽然在第一个session里已查询出user1对象,在新开的session1再去查询user1,仍然再去数据为查询user1对象。

6.二级缓存缓存的仅仅是对象,如果查询出来的是对象的一些属性,则不会被加到缓存中去

代码如下:

        Session session = sessionFactory.getCurrentSession();
        List<Object[]> ls = (List<Object[]>)session.createQuery("select u.name from User u order by name").setFirstResult(0).setMaxResults(30).list();
        User user1 = (User)session.load(User.class, "5a4566e09d8d4c5a84c2f0bff1f66c44");//session存在此用户对象,不用再去数据库中查询
        System.out.println("user1:"+user1.getName());
结果:

Hibernate: 
    select
        top 30 user0_.NAME as col_0_0_ 
    from
        dbo.BD_TestUser user0_ 
    order by
        user0_.NAME
Hibernate: 
    select
        user0_.ID as ID0_0_,
        user0_.CREATEDATETIME as CREATEDA2_0_0_,
        user0_.MODIFYDATETIME as MODIFYDA3_0_0_,
        user0_.NAME as NAME0_0_,
        user0_.PWD as PWD0_0_ 
    from
        dbo.BD_TestUser user0_ 
    where
        user0_.ID=?
user1:哈宇华帝0
我们看到这个测试用例,如果我们只是取出对象的一些属性的话,则不会将其保存到二级缓存中去,因为二级缓存缓存的仅仅是对象

三.查询缓存(sessionFactory级别)

我们如果要配置查询缓存,只需要在hibernate.cfg.xml中加入一条配置即可:

     <!-- 开启查询缓存 -->
        <property name="hibernate.cache.use_query_cache">true</property>
然后我们如果在查询hql语句时要使用查询缓存,就需要在查询语句后面设置这样一个方法:
List<Student> ls = session.createQuery("from Student where name like ?")
                    .setCacheable(true)  //开启查询缓存,查询缓存也是SessionFactory级别的缓存
                    .setParameter(0, "%王%")
                    .setFirstResult(0).setMaxResults(50).list();
如果是在annotation中,我们还需要在这个类上加上这样一个注解:@Cacheable

查询缓存也是sessionFactory级别的缓存,只有当 HQL 查询语句完全相同时,连参数设置都要相同,此时查询缓存才有效













内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值