hibernate :
Hibernate是一个持久层框架,经常访问物理数据库。
为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。
缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据。
Hibernate缓存包括两大类:Hibernate一级缓存和L1Hibernate二级缓存 L2。
1.Hibernate一级缓存又称为“Session的缓存”。
Session内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命周期通常对应一个数据库事务或者一个应用事务)。
一级缓存中,持久化类的每个实例都具有唯一的OID。
注意:在进行ssh整合的过程中spring管理hibernate的一级缓存是无效的。
重点hibernate的二级缓存,搜了很多博客 结果很坑,概念理解解释都没错,关键就是配置,可能是我的hibernate的版本过高的原因,导致配置失败。
二级缓存的大致可以分为一下几类:
- 事务缓存:作用于事务范围,session结束则缓存清除,Hibernate的L1缓存为事务缓存,默认开启,我们在纯Hibernate项目中手写过回滚的代码,能够回滚就是因为事务缓存。
- 应用缓存:作用于应用范围,被所有事务共享,依赖于应用的生命周期。所以,非常适合使用一个同样依赖于应用生命周期的轻量级缓存来实现,ehcache几乎是最好的选择。
- 集群缓存:该缓存类似于真正的数据库被一个集群共享,典型的如Redis就很适合做集群缓存
二级缓存的工作原理;
Hibernate的L1,L2缓存均是通过id进行工作,当Hibernate根据id访问对象时会先在一级缓存中查找,如果查不到则在二级缓存中查找。
SessionFactory二级缓存根据功能和目的又可以划分为内置缓存和外置缓存,内置缓存存放映射元数据和预定义SQL语句,
前者为映射文件中数据的副本,后者为根据副本推导出的SQL语句。内置缓存是只读的,因此不需要与映射文件进行同步。
外置缓存是Hibernate的一个插件,默认不启用,即Hibernate的L2缓存。外置缓存的数据是数据库数据的副本,外置缓存的介质可以是内存或者硬盘。
2,废话不多说,使用外部插件Ehcache开启hibernate的二级缓存
Ehcache是一个健壮的简洁的轻量的纯Java进程的内存缓存框架,因此其存在与Java进程直接相关联。
通过在硬盘和内存里对数据进行拷贝,实现了数据库的缓存。由于Apache的支持,Ehcache非常稳健。
pom节点:
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-ehcache --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>5.1.7.Final</version> </dependency> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.3.1</version> </dependency>
编写:ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="D:/ehcache"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <cache name="sampleCache1" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> <cache name="sampleCache2" maxElementsInMemory="1000" eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false" /> </ehcache>
ehcache的各属性介绍如下:
- name:缓存名称。
- maxElementsInMemory:缓存最大个数。
- eternal:对象是否永久有效,一但设置了,timeout将不起作用。
- timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
- timeToLiveSeconds:设置对象在失效前允许存活时间,最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0,也就是对象存活时 间无穷大。
- overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
- diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
- maxElementsOnDisk:硬盘最大缓存个数。
- diskPersistent:是否缓存虚拟机重启期数据,默认false。
- diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
- memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU。你可以设置为 FIFO或是LFU。
- clearOnFlush:内存数量最大时是否清除。
在spring管理的sessionFactory中开启二级缓存
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan"> <list> <value>zy.entity</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <!-- 博客地址 :http://www.cnblogs.com/fsh1542115262/articles/6071470.html hibernate.current_session_context_class三种配置--> <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop> <!-- 避免这个错误信息Disabling contextual LOB creation as createClob() method threw error :java.lang.reflect.InvocationTargetException --> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> <!--开启hibernate的二级缓存--> <prop key="hibernate.cache.use_second_level_cache">true</prop> <prop key="hibernate.cache.use_query_cache">true</prop> <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop> <!--设置缓存的类型,设置缓存的提供商 EhCacheRegionFactory--> </props> </property> </bean>
其中
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>这个是关键所在,之前之所以配置不成功的原因是因为我的hibernate的版本过高,而高版本早就替换成
hibernate.cache.region.factory_class指定的ehcache的工厂。
- hibernate.cache.use_second_level_cache 是hibernate中L2缓存的开关,必须为true。
- hibernate.cache.use_query_cache 是hibernate的查询缓存的开关,可以自己决定是否开启。
- hibernate.cache.region.factory_class 承载L2缓存的方法,即选择L2缓存数据库。官方很坑的从hibernate4开始就存在文档问题,文档中仍为provider_class,实际上早已换为了这个方法(idea的默认提示中找不到,但运行后如果没添加,错误日志里可以显示出)。需要注意的是,需要使用Singleton模式的Factory,否则会有冲突问题。具体原因还不明了。
另外有几个可以开启的选项,包括
- hibernate.generate_statistics 生成统计日志,如果项目在调试,这是一个很好的开发选项。记得实际运行时关闭掉。
- hibernate.cache.provider_configuration_file_resource_path 提供配置文件的路径,如果你不想使用默认路径,那么需要在这里配置,其格式和web.xml中的路径一致。
另外一个就是需要在缓存的实体中设置缓存策略:
ehcache不支持事务,有三种模式:
- READ_ONLY: 适用于仅读取,如果有数据的更新操作则会异常。
- READ_WRITE: 用读写锁控制缓存
- NON_STRICT_READ_WRITE: 不加锁控制缓存,写写会有冲突,适用于很难发生写冲突的系统。
具体使用时,在hibernate持久化生成的Entity上使用类似这样的标签,即可为该数据库添加二级缓存。
注解方式:
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
xml方式:
<cache usage="read-write"/> 包括类级别和集合级别;
通常情况下,缓存用于多读少写的表,在这种表中,最高效,最符合缓存本身行为的应该是READ_ONLY模式,即,在读取时使用缓存,发生写操作时清空缓存。
测试:hibernate中默认使用 二级缓存的方法load()和iterator()方法
这里使用load()方法配置:
我dao层方法:
public Bill load() { Session session = this.sessionFactory.openSession(); Bill load = session.load(Bill.class, 24); /* if(!Hibernate.isInitialized(load)){ Hibernate.initialize(load); } session.close();*/ return load; }
/** * 测试hibernate的二级缓存 */ @Test public void TestTwoCache(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-hiberanate.xml"); BillService service= (BillService)context.getBean("billServiceImpl"); Bill load = service.load(); System.out.println(load); System.out.println(load.getBname()); System.out.println("----------------------------------"); //注意这不是调用的一级缓存,因为在ssh整合中hibernate的一级缓存是失效的包括ssm 就是mybatis BillService service1= (BillService)context.getBean("billServiceImpl"); Bill load1 = service.load(); System.out.println(load1.getBname()); }
运行结果:
Hibernate: select bill0_.bid as bid1_1_0_, bill0_.bmoney as bmoney2_1_0_, bill0_.bname as bname3_1_0_, bill0_.createtime as createti4_1_0_, bill0_.hscode as hscode5_1_0_, bill0_.paystate as paystate6_1_0_, bill0_.scount as scount7_1_0_, bill0_.sunit as sunit8_1_0_, bill0_.supId as supId9_1_0_ from bill bill0_ where bill0_.bid=? zy.entity.Bill@374ffe34 猕猴桃山西 ---------------------------------- 猕猴桃山西
很明显hibernate的二级缓存设置成功;
项目下载地址:算了吧。。太low 如果有需要,留评论;