Hibernate中的三种数据持久状态和缓存机制
Hibernate中的三種狀態(tài)
??瞬時(shí)狀態(tài):剛創(chuàng)建的對(duì)象還沒(méi)有被Session持久化、緩存中不存在這個(gè)對(duì)象的數(shù)據(jù)并且數(shù)據(jù)庫(kù)中沒(méi)有這個(gè)對(duì)象對(duì)應(yīng)的數(shù)據(jù)為瞬時(shí)狀態(tài)這個(gè)時(shí)候是沒(méi)有OID。
持久狀態(tài):對(duì)象經(jīng)過(guò)Session持久化操作,緩存中存在這個(gè)對(duì)象的數(shù)據(jù)為持久狀態(tài)并且數(shù)據(jù)庫(kù)中存在這個(gè)對(duì)象對(duì)應(yīng)的數(shù)據(jù)為持久狀態(tài)這個(gè)時(shí)候有OID。
游離狀態(tài):當(dāng)Session關(guān)閉,緩存中不存在這個(gè)對(duì)象數(shù)據(jù)而數(shù)據(jù)庫(kù)中有這個(gè)對(duì)象的數(shù)據(jù)并且有OID為游離狀態(tài)。
注:OID為了在系統(tǒng)中能夠找到所需對(duì)象,我們需要為每一個(gè)對(duì)象分配一個(gè)唯一的表示號(hào)。在關(guān)系數(shù)據(jù)庫(kù)中我們稱(chēng)之為關(guān)鍵字,而在對(duì)象術(shù)語(yǔ)中,則叫做對(duì)象標(biāo)識(shí)
(Object identifier-OID).通常OID在內(nèi)部都使用一個(gè)或多個(gè)大整數(shù)表示,而在應(yīng)用程序中則提供一個(gè)完整的類(lèi)為其他類(lèi)提供獲取、操作。
Hibernate數(shù)據(jù)狀態(tài)圖:
?
需要注意的是:
?
? ? 當(dāng)對(duì)象的臨時(shí)狀態(tài)將變?yōu)槌志没癄顟B(tài)。當(dāng)對(duì)象在持久化狀態(tài)時(shí),它一直位于?Session?的緩存中,對(duì)它的任何操作在事務(wù)提交時(shí)都將同步到數(shù)據(jù)庫(kù),因此,對(duì)一個(gè)已經(jīng)持久的對(duì)象調(diào)用?save()?或?update()?方法是沒(méi)有意義的。
Student stu = new Strudnet();stu.setCarId(“200234567”);stu.setId(“100”);// 打開(kāi) Session, 開(kāi)啟事務(wù)//將stu對(duì)象持久化操作 session.save(stu);stu.setCardId(“20076548”);//再次對(duì)stu對(duì)象進(jìn)行持久化操作 session.save(stu); // 無(wú)效 session.update(stu); // 無(wú)效// 提交事務(wù),關(guān)閉 Session?
?
?
Hibernate緩存機(jī)制
?
什么是緩存?
緩存是介于應(yīng)用程序和物理數(shù)據(jù)源之間,其作用是為了降低應(yīng)用程序?qū)ξ锢頂?shù)據(jù)源訪問(wèn)的頻次,從而提高了應(yīng)用的運(yùn)行性能。緩存內(nèi)的數(shù)據(jù)是對(duì)物理數(shù)據(jù)源中的數(shù)據(jù)的復(fù)制,應(yīng)用程序在運(yùn)行時(shí)從緩存讀寫(xiě)數(shù)據(jù),在特定的時(shí)刻或事件會(huì)同步緩存和物理數(shù)據(jù)源的數(shù)據(jù)。
?
緩存有什么好處?
緩存的好處是降低了數(shù)據(jù)庫(kù)的訪問(wèn)次數(shù),提高應(yīng)用性能,減少了讀寫(xiě)數(shù)據(jù)的時(shí)間
?
什么時(shí)候適合用緩存?
程序中經(jīng)常用到一些不變的數(shù)據(jù)內(nèi)容,從數(shù)據(jù)庫(kù)查出來(lái)以后不會(huì)去經(jīng)常修改它而又經(jīng)常要用到的就可以考慮做一個(gè)緩存,以后讀取就從緩存來(lái)讀取,而不必每次都去查詢(xún)數(shù)據(jù)庫(kù)。因?yàn)橛脖P(pán)的速度比內(nèi)存的速度慢的多。從而提高了程序的性能,緩存的出現(xiàn)就會(huì)為了解決這個(gè)問(wèn)題
Hibernate中的緩存
Hibernate中的緩存包括一級(jí)緩存(Session緩存)、二級(jí)緩存(SessionFactory緩存)和查詢(xún)緩存。
?
一級(jí)緩存(Session緩存)
由于Session對(duì)象的生命周期通常對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)事務(wù)或者一個(gè)應(yīng)用事務(wù),因此它的緩存是事務(wù)范圍的緩存。Session級(jí)緩存是必需的,不允許而且事實(shí)上也無(wú)法卸除。在Session級(jí)緩存中,持久化類(lèi)的每個(gè)實(shí)例都具有唯一的OID。
當(dāng)應(yīng)用程序調(diào)用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調(diào)用查詢(xún)接口的list()、iterate()或filter()方法時(shí),如果在Session緩存中還不存在相應(yīng)的對(duì)象,Hibernate就會(huì)把該對(duì)象加入到第一級(jí)緩存中。
當(dāng)清理緩存時(shí),Hibernate會(huì)根據(jù)緩存中對(duì)象的狀態(tài)變化來(lái)同步更新數(shù)據(jù)庫(kù)。
Session為應(yīng)用程序提供了兩個(gè)管理緩存的方法:evict(Object obj):從緩存中清除參數(shù)指定的持久化對(duì)象。clear():清空緩存中所有持久化對(duì)象。
?
public static void testOneLeveCache(){Session session=HibernateUtil.getSession();//獲取持久化對(duì)象DeptDept d1=(Dept)session.get(Dept.class, 10);//再次獲取持久化對(duì)象DeptDept d2=(Dept)session.get(Dept.class, 10); session.close();}?
?
通過(guò)Session的get()方法獲取到了Dept對(duì)象默認(rèn)會(huì)將Dept對(duì)象保存到一級(jí)緩存中(Session緩存) 當(dāng)?shù)诙潍@取的時(shí)候會(huì)先從一級(jí)緩存中查詢(xún)對(duì)應(yīng)的對(duì)象(前提是不能清空或關(guān)閉Session否則一級(jí)緩存會(huì)清空或銷(xiāo)毀)如果一級(jí)緩存中存在相應(yīng)的對(duì)象就不會(huì)到數(shù)據(jù)庫(kù)中查詢(xún)??所以只執(zhí)行一次查詢(xún)的查詢(xún)代碼如下:
Hibernate: selectdept0_.DEPTNO as DEPTNO0_0_,dept0_.DNAME as DNAME0_0_,dept0_.LOC as LOC0_0_ fromSCOTT.DEPT dept0_ wheredept0_.DEPTNO=??
?
如何清除一級(jí)緩存?
通過(guò)session.clear();//清除所有緩存
session.evict();//清除指定緩存
public static void testOneLeveCache(){Session session=HibernateUtil.getSession();SessionFactory sf = HibernateUtil.getSessionFactory();//獲取持久化對(duì)象DeptDept d1=(Dept)session.get(Dept.class, 10);session.clear();//清除所有緩存session.evict(d1);//清除指定緩存//再次獲取持久化對(duì)象DeptDept d2=(Dept)session.get(Dept.class, 10); session.close();}?
使用session.evict(Object obj)會(huì)刪除指定的Bean所以當(dāng)你查詢(xún)被你刪除二級(jí)緩存的Bean時(shí)也會(huì)執(zhí)行兩條SQL語(yǔ)句
使用Session.clear()清除后會(huì)發(fā)現(xiàn)執(zhí)行了兩條SQL語(yǔ)句:
Hibernate: selectdept0_.DEPTNO as DEPTNO0_0_,dept0_.DNAME as DNAME0_0_,dept0_.LOC as LOC0_0_ fromSCOTT.DEPT dept0_ wheredept0_.DEPTNO=? Hibernate: selectdept0_.DEPTNO as DEPTNO0_0_,dept0_.DNAME as DNAME0_0_,dept0_.LOC as LOC0_0_ fromSCOTT.DEPT dept0_ wheredept0_.DEPTNO=??
??二級(jí)緩存(SessionFactory緩存)
由于SessionFactory對(duì)象的生命周期和應(yīng)用程序的整個(gè)過(guò)程對(duì)應(yīng),因此Hibernate二級(jí)緩存是進(jìn)程范圍或者集群范圍的緩存,有可能出現(xiàn)并發(fā)問(wèn)題,因此需要采用適當(dāng)?shù)牟l(fā)訪問(wèn)策略,該策略為被緩存的數(shù)據(jù)提供了事務(wù)隔離級(jí)別。
save、update、saveOrupdate、load、get、list、query、Criteria方法都會(huì)填充二級(jí)緩存
get、load、iterate會(huì)從二級(jí)緩存中取數(shù)據(jù)session.save(user)???
如果user主鍵使用“native”生成,則不放入二級(jí)緩存.
第二級(jí)緩存是可選的,是一個(gè)可配置的插件,默認(rèn)下SessionFactory不會(huì)啟用這個(gè)插件。Hibernate提供了org.hibernate.cache.CacheProvider接口,它充當(dāng)緩存插件與Hibernate之間的適配器。
?
Hibernate的二級(jí)緩存策略的一般過(guò)程如下:
? ??1) 條件查詢(xún)的時(shí)候,總是發(fā)出一條select * from table_name where …. (選擇所有字段)這樣的SQL語(yǔ)句查詢(xún)數(shù)據(jù)庫(kù),一次獲得所有的數(shù)據(jù)對(duì)象。
? ? 2) 把獲得的所有數(shù)據(jù)對(duì)象根據(jù)ID放入到第二級(jí)緩存中。
? ? 3) 當(dāng)Hibernate根據(jù)ID訪問(wèn)數(shù)據(jù)對(duì)象的時(shí)候,首先從Session一級(jí)緩存中查;查不到,如果配置了二級(jí)緩存,那么從二級(jí)緩存中查;查不到,再查詢(xún)數(shù)據(jù)庫(kù),把結(jié)果按照ID放入到緩存。
? ? 4) 刪除、更新、增加數(shù)據(jù)的時(shí)候,同時(shí)更新緩存。
?Hibernate的二級(jí)緩存策略,是針對(duì)于ID查詢(xún)的緩存策略,對(duì)于條件查詢(xún)則毫無(wú)作用。為此,Hibernate提供了針對(duì)條件查詢(xún)的查詢(xún)緩存(Query Cache)。
?
配置二級(jí)緩存(SessionFactory緩存):
在hibernate.cfg.xml中配置以下代碼
<!-- 開(kāi)啟二級(jí)緩存 --><property name="hibernate.cache.use_second_level_cache">true</property><!-- 為hibernate指定二級(jí)緩存的實(shí)現(xiàn)類(lèi) --><property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>?
指明哪些類(lèi)需要放入二級(jí)緩存,需要長(zhǎng)期使用到的對(duì)象才有必要放入二級(jí)緩存放入二級(jí)緩存的方式有兩種:
1.在hibernate.cfg.xml中配置
<class-cache class="entity.PetInfo" usage="read-only" /> //不允許更新緩存中的對(duì)象 <class-cache class="entity.PetInfo" usage="read-write" /> //允許更新緩存中的對(duì)象?
2.在Bean.hbm文件中配置
<hibernate-mapping><class name="com.bdqn.entity.Dept" table="DEPT" schema="SCOTT" ><cache usage="read-only"/>//將這個(gè)類(lèi)放入二級(jí)緩存<id name="deptno" type="java.lang.Integer"><column name="DEPTNO" precision="2" scale="0" /><generator class="assigned"></generator></id><property name="dname" type="java.lang.String"><column name="DNAME" length="14" /></property></class> </hibernate-mapping>?
在ehcache.xml配置文件中可以設(shè)置緩存的最大數(shù)量、是否永久有效、時(shí)間等
<defaultCachemaxElementsInMemory="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="true"/>?
?
如何清除二級(jí)緩存?
需要用SessionFactory來(lái)管理二級(jí)緩存代碼如下:
sessionFactory.evict(Entity.class);//清除所有Entity sessionFactory.evict(Entity.class,id);//清除指定Entity 比如: //用SessionFacotry管理二級(jí)緩存 SessionFactory factory=HibernateUtils.getSessionFactory();//evict()把id為1的Student對(duì)象從二級(jí)緩存中清除. factory.evict(Student.class, 1); //evict()清除所有二級(jí)緩存. factory.evict(Student.class);?
?
什么樣的數(shù)據(jù)適合存放到第二級(jí)緩存中??
1) 很少被修改的數(shù)據(jù)
2) 不是很重要的數(shù)據(jù),允許出現(xiàn)偶爾并發(fā)的數(shù)據(jù)
3) 不會(huì)被并發(fā)訪問(wèn)的數(shù)據(jù)
4) 常量數(shù)據(jù)
不適合存放到第二級(jí)緩存的數(shù)據(jù)??
1) 經(jīng)常被修改的數(shù)據(jù)
2) 絕對(duì)不允許出現(xiàn)并發(fā)訪問(wèn)的數(shù)據(jù),如財(cái)務(wù)數(shù)據(jù),絕對(duì)不允許出現(xiàn)并發(fā)
3) 與其他應(yīng)用共享的數(shù)據(jù)。
?
?
查詢(xún)緩存(Query?Cache)
? hibernate的查詢(xún)緩存是主要是針對(duì)普通屬性結(jié)果集的緩存, 而對(duì)于實(shí)體對(duì)象的結(jié)果集只緩存id。
?在一級(jí)緩存,二級(jí)緩存和查詢(xún)緩存都打開(kāi)的情況下作查詢(xún)操作時(shí)這樣的:
查詢(xún)普通屬性,會(huì)先到查詢(xún)緩存中取,如果沒(méi)有,則查詢(xún)數(shù)據(jù)庫(kù);查詢(xún)實(shí)體,會(huì)先到查詢(xún)緩存中取id,如果有,則根據(jù)id到緩存(一級(jí)/二級(jí))中取實(shí)體,如果緩存中取不到實(shí)體,再查詢(xún)數(shù)據(jù)庫(kù)。
?
??????? 在hibernate.cfg.xml配置文件中,開(kāi)啟查詢(xún)緩存
<!-- 是否開(kāi)啟查詢(xún)緩存,true開(kāi)啟查詢(xún)緩存,false關(guān)閉查詢(xún)緩存 --> <property name="cache.use_query_cache">true</property>?
開(kāi)啟查詢(xún)緩存后還需要在程序中進(jìn)行啟用查詢(xún)緩存
public static void testQueryCache(){Session session=HibernateUtil.getSession();String hql="from Emp as e";Query query=session.createQuery(hql);query.setCacheable(true);//啟用查詢(xún)緩存(二級(jí)緩存)List<Emp> empList=query.list();session.close(); }?
查詢(xún)緩存是基于二級(jí)緩存機(jī)制如果根據(jù)Bean的屬性查詢(xún)可以不開(kāi)啟二級(jí)緩存代碼如下:
session = HibernateUtils.getSession(); t = session.beginTransaction(); Query query = session.createQuery("select s.name from Student s"); //啟用查詢(xún)緩存 query.setCacheable(true); List<String> names = query.list(); for (Iterator<String> it = names.iterator(); it.hasNext();) { String name = it.next(); System.out.println(name); } System.out.println("================================"); query = session.createQuery("select s.name from Student s"); //啟用查詢(xún)緩存 query.setCacheable(true); //沒(méi)有發(fā)出查詢(xún)語(yǔ)句,因?yàn)檫@里使用的查詢(xún)緩存 names = query.list(); for (Iterator<String> it = names.iterator(); it.hasNext();) { String name = it.next(); System.out.println(name); } t.commit();總結(jié)
以上是生活随笔為你收集整理的Hibernate中的三种数据持久状态和缓存机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: STM32开发 -- ADC详解
- 下一篇: 日常生活小技巧 -- Notepad++