hibernate二级缓存(一)一级缓存与二级缓存
hibernate二級(jí)緩存(一)一級(jí)緩存與二級(jí)緩存
1.hibernate一級(jí)緩存
hibernate的一級(jí)緩存是session級(jí)別的緩存,一級(jí)緩存hibernate默認(rèn)啟用且不能被卸載,一個(gè)事務(wù)內(nèi)有效。
特點(diǎn):
使用一級(jí)緩存的目的是為了減少對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)次數(shù),從而提升hibernate的執(zhí)行效率;(當(dāng)執(zhí)行一次查詢操作的時(shí)候,執(zhí)行第二次查詢操作,先檢查緩存中是否有數(shù)據(jù),如果有數(shù)據(jù)就不查詢數(shù)據(jù)庫(kù),直接從緩存中獲取數(shù)據(jù));
Hibernate中的一級(jí)緩存,也叫做session的緩存,它可以在session范圍內(nèi)減少數(shù)據(jù)庫(kù)的訪問(wèn)次數(shù),只在session范圍內(nèi)有效,session關(guān)閉,一級(jí)緩存失敗;
一級(jí)緩存的特點(diǎn),只在session范圍有效,作用時(shí)間短,效果不是特別明顯,在短時(shí)間內(nèi)多次操作數(shù)據(jù)庫(kù),效果比較明顯。
當(dāng)調(diào)用session的save/saveOrUpdate/get/load/list/iterator方法的時(shí)候,都會(huì)把對(duì)象放入session緩存中;
session的緩存是由hibernate維護(hù)的,用戶不能操作緩存內(nèi)容;如果想操作緩存內(nèi)容,必須通過(guò)hibernate提供的evict/clear方法操作
緩存相關(guān)的方法(在什么情況下使用上面方法呢?批量操作情況下使用,如Session.flush();先與數(shù)據(jù)庫(kù)同步,Session.clear();再清空一級(jí)緩存內(nèi)容):
session.flush();讓一級(jí)緩存與數(shù)據(jù)庫(kù)同步;session.evict();清空一級(jí)緩存中指定的對(duì)象;session.clear();清空一級(jí)緩存中所有的對(duì)象;綜上: 一級(jí)緩存的生命周期和session的生命周期一致,當(dāng)前session一旦關(guān)閉,一級(jí)緩存就消失了,因此一級(jí)緩存也叫session級(jí)的緩存或事務(wù)級(jí)緩存,一級(jí)緩存只存實(shí)體對(duì)象,它不會(huì)緩存一般的對(duì)象屬性(查詢緩存可以),即當(dāng)獲得對(duì)象后,就將該對(duì)象緩存起來(lái),如果在同一session中再去獲取這個(gè)對(duì)象時(shí),它會(huì)先判斷在緩存中有沒(méi)有該對(duì)象的id,如果有則直接從緩存中獲取此對(duì)象,反之才去數(shù)據(jù)庫(kù)中取,取的同時(shí)再將此對(duì)象作為一級(jí)緩存處理。
2.二級(jí)緩存
Hibernate的二級(jí)緩存又稱(chēng)為"SessionFactory的緩存",由于SessionFactory對(duì)象的生命周期和應(yīng)用的整個(gè)過(guò)程對(duì)應(yīng),他是可選的,是一個(gè)可配置的插件,默認(rèn)情況下SessionFactory不會(huì)啟用這個(gè)插件。
由于二級(jí)緩存是被各session共享的,那么多個(gè)事務(wù)或者說(shuō)線程同時(shí)訪問(wèn)修改二級(jí)緩存可能會(huì)會(huì)造成數(shù)據(jù)不一致問(wèn)題。所以二級(jí)緩存只適合多讀少寫(xiě)的場(chǎng)景。
那么什么樣的數(shù)據(jù)適合放在二級(jí)緩存中呢?
- 多讀少寫(xiě)的數(shù)據(jù)
- 不是很重要的數(shù)據(jù)
- 常量數(shù)據(jù)
什么樣的數(shù)據(jù)不適合放在二級(jí)緩存中呢?
- 經(jīng)常被修改的數(shù)據(jù)
- 絕對(duì)不允許出現(xiàn)并發(fā)訪問(wèn)的數(shù)據(jù)。如財(cái)務(wù)數(shù)據(jù),絕對(duì)不允許出現(xiàn)并發(fā)
- 與其他應(yīng)用共享的數(shù)據(jù)
3. 二級(jí)緩存的配置
這里只展示純hibernate的二級(jí)緩存配置,如果要如spring結(jié)合,請(qǐng)參考spring sessionFactory配置里面的hibernate二級(jí)緩存參數(shù)。
下面的配置是基于hibernate5.3.7.Final版本
3.1 SessionFactory的配置
該版本的SessionFactory獲取的最新方式如下:
public class SessionFactoryUtil {private SessionFactory sessionFactory;public SessionFactoryUtil() {setUp();}private void setUp() {// A SessionFactory is set up once for an application!final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure() // configures settings from hibernate.cfg.xml.build();try {sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();} catch (Exception e) {e.printStackTrace();// The registry would be destroyed by the SessionFactory, but we had trouble building the SessionFactory// so destroy it manually.StandardServiceRegistryBuilder.destroy(registry);}}public SessionFactory getSessionFactory() {return sessionFactory;} }registry 會(huì)自動(dòng)加載resources路徑下的hibernate.cfg.xml配置文件。hibernate.cfg.xml配置如下:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration><session-factory><!-- 數(shù)據(jù)庫(kù)連接配置 --><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost:3306/test</property><property name="connection.username">root</property><property name="connection.password">123456</property><!-- 數(shù)據(jù)庫(kù)連接池的大小 --><property name="connection.pool_size">5</property><!-- 每次從數(shù)據(jù)庫(kù)中取出并放到JDBC的Statement中的記錄條數(shù)。Fetch Size設(shè)的越大,讀數(shù)據(jù)庫(kù)的次數(shù)越少,速度越快,Fetch Size越小,讀數(shù)據(jù)庫(kù)的次數(shù)越多,速度越慢--><property name="jdbc.fetch_size">50</property><!--批量插入,刪除和更新時(shí)每次操作的記錄數(shù)。Batch Size越大,批量操作的向數(shù)據(jù)庫(kù)發(fā)送Sql的次數(shù)越少,速度就越快,同樣耗用內(nèi)存就越大--><property name="jdbc.batch_size">23</property><!-- SQL 方言 --><property name="dialect">org.hibernate.dialect.MySQLDialect</property><!-- Enable Hibernate's automatic session context management --><property name="current_session_context_class">thread</property><!-- 在控制臺(tái)輸出sql語(yǔ)句 --><property name="show_sql">true</property><!-- 在啟動(dòng)時(shí)根據(jù)配置更新數(shù)據(jù)庫(kù) --><property name="hbm2ddl.auto">update</property><!-- 二級(jí)緩存配置 --><property name="hibernate.cache.use_second_level_cache">true</property><property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property><property name="hibernate.cache.use_minimal_puts">true</property><property name="hibernate.cache.use_query_cache">true</property><property name="hibernate.cache.region_prefix">customer-hibernate-cache</property><property name="hibernate.cache.default_cache_concurrency_strategy">nonstrict-read-write</property><mapping class="com.foo.model.Event"/><!-- 注冊(cè)我們的實(shí)體映射類(lèi)--></session-factory> </hibernate-configuration>3.2 基本的代碼示例
有了SessionFactory后通過(guò)session進(jìn)行增刪改查等操作
public class Main {public static void main(String[] args) {SessionFactoryUtil sessionFactoryUtil = new SessionFactoryUtil();SessionFactory sessionFactory = sessionFactoryUtil.getSessionFactory();//1.查詢單個(gè)實(shí)體doExecute(sessionFactory, new HibernateExecuteCallBack() {@Overridepublic void execute(Session session) {Event event = session.get(Event.class, 7L);System.out.println(event);}});//2.查詢單個(gè)實(shí)體doExecute(sessionFactory, new HibernateExecuteCallBack() {@Overridepublic void execute(Session session) {Event event = session.get(Event.class, 7L);System.out.println(event);}});}public static void doExecute(SessionFactory sessionFactory, HibernateExecuteCallBack executeCallBack) {Session session = sessionFactory.openSession();session.beginTransaction();executeCallBack.execute(session);session.getTransaction().commit();session.close();}interface HibernateExecuteCallBack {void execute(Session session);}上面的代碼中首先得到一個(gè)sessionFactory ,然后通過(guò)doExecute封裝session的整個(gè)執(zhí)行流程的模版代碼,HibernateExecuteCallBack 回調(diào)接口將實(shí)際執(zhí)行操作分離,整個(gè)過(guò)程類(lèi)似jdbcTemplate。
package com.foo.model;import org.hibernate.annotations.CacheConcurrencyStrategy;import javax.persistence.*; import java.util.Date;/*** @author JasonLin* @version V1.0* @date 2019/3/11*/ @Entity @Table(name = "event") @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class Event {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Columnprivate String title;@Column(name = "_date")private Date date;public Event(){}public Event(String title, Date date) {this.title = title;this.date = date;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public Date getDate() {return date;}public void setDate(Date date) {this.date = date;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}@Overridepublic String toString() {return "Event{" +"id=" + id +", title='" + title + '\'' +", date=" + date +'}';} }Event的代碼隨意,是通過(guò)注解配置實(shí)體,但是必須注意在hibernate.cfg.xml里面必須配置:
<mapping class="com.foo.model.Event"/><!-- 注冊(cè)我們的實(shí)體映射類(lèi)-->3.2 二級(jí)緩存的配置
在上面的配置里面其實(shí)已經(jīng)加上了二級(jí)緩存
<!--是否啟用二級(jí)緩存--><property name="hibernate.cache.use_second_level_cache">true</property><!--緩存的具體實(shí)現(xiàn)--><property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhcacheRegionFactory</property><!--以頻繁的讀操作為代價(jià), 優(yōu)化二級(jí)緩存來(lái)最小化寫(xiě)操作--><property name="hibernate.cache.use_minimal_puts">true</property><!--是否使用查詢緩存--><property name="hibernate.cache.use_query_cache">true</property><!--緩存的前綴--><property name="hibernate.cache.region_prefix">customer-hibernate-cache</property><!--緩存的策略--><property name="hibernate.cache.default_cache_concurrency_strategy">nonstrict-read-write</property>這里我們使用的是EhcacheRegionFactory來(lái)作為二級(jí)緩存的具體實(shí)現(xiàn)。當(dāng)然也可以自己實(shí)現(xiàn)RegionFactory,比如通過(guò)redis來(lái)作為hibernate的二級(jí)緩存。
hibernate.cache.default_cache_concurrency_strategy指定hibernate二級(jí)緩存策略,hibernate共有五種緩存策略
public enum CacheConcurrencyStrategy {/*** Indicates no concurrency strategy should be applied.*/NONE( null ),/*** Indicates that read-only strategy should be applied.** @see AccessType#READ_ONLY*/READ_ONLY( AccessType.READ_ONLY ),/*** Indicates that the non-strict read-write strategy should be applied.** @see AccessType#NONSTRICT_READ_WRITE*/NONSTRICT_READ_WRITE( AccessType.NONSTRICT_READ_WRITE ),/*** Indicates that the read-write strategy should be applied.** @see AccessType#READ_WRITE*/READ_WRITE( AccessType.READ_WRITE ),/*** Indicates that the transaction strategy should be applied.** @see AccessType#TRANSACTIONAL*/TRANSACTIONAL( AccessType.TRANSACTIONAL );- CacheConcurrencyStrategy.None 不使用緩存
- CacheConcurrencyStrategy.READ_ONLY:只讀模式,在此模式下,如果對(duì)數(shù)據(jù)進(jìn)行更新操作,會(huì)拋出異常。
- CacheConcurrencyStrategy.READ_WRITE:讀寫(xiě)模式在更新緩存的時(shí)候會(huì)對(duì)緩存里的數(shù)據(jù)加鎖,其他事物如果去取相應(yīng)緩存中的數(shù)據(jù),發(fā)現(xiàn)被鎖了,直接去數(shù)據(jù)庫(kù)中取。
- CacheConcurrencyStrategy.NONSTRICT_READ_WRITE:不嚴(yán)格的讀寫(xiě)模式則不會(huì)對(duì)緩存數(shù)據(jù)加鎖
- CacheConcurrencyStrategy.TRANSACTIONAL:事務(wù)模式指緩存支持事務(wù),當(dāng)事務(wù)回滾,緩存也回滾,只支持JTA環(huán)境
一切配置完畢,下面來(lái)看下具體的執(zhí)行結(jié)果,
Hibernate: select event0_.id as id1_0_0_, event0_._date as _date2_0_0_, event0_.title as title3_0_0_ from event event0_ where event0_.id=? Event{id=7, title='Our very first event!', date=2019-03-11 13:45:21.0} Event{id=7, title='Our very first event!', date=2019-03-11 13:45:21.0}由于我們的第一次操作是在不同的session里面,我們看到配置了緩存之后只發(fā)送了一條sql語(yǔ)句。代表緩存配置成功。在后面我們將具體講解hibernate二級(jí)緩存的實(shí)現(xiàn)原理并自己用map實(shí)現(xiàn)一個(gè)簡(jiǎn)單的RegionFactory。
總結(jié)
以上是生活随笔為你收集整理的hibernate二级缓存(一)一级缓存与二级缓存的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机组成原理试题解析答案,计算机组成原
- 下一篇: 矩阵分析与应用(二)——内积与范数