Hibernate緩存機(jī)制實(shí)例代碼解析
本文研究的主要是Hibernate緩存機(jī)制的相關(guān)內(nèi)容,具體如下。
演示項(xiàng)目:
Student.java:
public class Student {
/*學(xué)生ID*/
private int id;
/*學(xué)生姓名*/
private String name;
/*學(xué)生和班級(jí)的關(guān)系*/
private Classes classes;
//省略setter和getter方法
}
Classes.java:
public class Classes {
/*班級(jí)ID*/
private int id;
/*班級(jí)名稱*/
private String name;
/*班級(jí)和學(xué)生的關(guān)系*/
private Set<Student> students;
//省略setter和getter方法
}
Student.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lixue.bean">
<class name="Student" table="t_student">
<id name="id">
<generator class="native"/>
</id>
<!-- 映射普通屬性 -->
<property name="name"/>
<!-- 多對(duì)一 映射,在多的一端加上外鍵-->
<many-to-one name="classes" column="classesid"/>
</class>
</hibernate-mapping>
Classes.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.lixue.bean">
<!-- 設(shè)置lazy為false -->
<class name="Classes" table="t_classes" lazy="false">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!-- 一對(duì)多映射 ,inverse="true"表示交給對(duì)端維護(hù)關(guān)系-->
<set name="students" inverse="true">
<key column="classesid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
一級(jí)緩存:
一級(jí)緩存聲明周期很短和session的生命周期一致,一級(jí)緩存也叫session級(jí)的緩存或事物級(jí)緩存,一級(jí)緩存是緩存對(duì)象的,并不能緩存屬性。
測(cè)試方法(在同一個(gè)session中使用load()查詢兩次):
/*取出來之后會(huì)放在緩存中,第二次取的時(shí)候會(huì)直接從緩存取出*/
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
/*不會(huì)發(fā)出查詢語句,load使用緩存*/
student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
注:我們會(huì)發(fā)現(xiàn),當(dāng)我們第一次查詢的時(shí)候,查出來的結(jié)果是會(huì)放在session即緩存即一級(jí)緩存中的。第二次load()后及時(shí)去獲取值的時(shí)候也沒有在發(fā)出語句到數(shù)據(jù)庫中查詢了,而是直接從緩存中取值了(必須是在同一session中)。
測(cè)試方法二(在同一session中):
Student student = new Student();
student.setName("張三");
Serializable id = session.save(student);
student = (Student)session.load(Student.class, id);
//不會(huì)發(fā)出查詢語句,因?yàn)閟ave支持緩存
System.out.println("student.name=" + student.getName());
注:調(diào)用了save()方法再使用load()去加載對(duì)象,然后真正獲取name屬性,但是此時(shí)并不會(huì)發(fā)出語句去查詢數(shù)據(jù)庫。因?yàn)閟ave()方法也是支持緩存的。
測(cè)試大批量數(shù)據(jù)的添加:
public void testCache7() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
for (int i=0; i<100; i++) {
Student student = new Student();
student.setName("張三" + i);
session.save(student);
//每20條更新一次
if (i % 20 == 0) {
//清除緩存,調(diào)用flush之后數(shù)據(jù)就會(huì)保存到數(shù)據(jù)庫
session.flush();
//清除緩存的內(nèi)容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
注:
1.因?yàn)閟ave()方法支持緩存,那就存在一個(gè)問題,如果我要同時(shí)存1000萬條數(shù)據(jù),那緩存中豈不是有1000萬的緩存對(duì)象,那就很可能導(dǎo)致溢出。所以說Hibernate并不能很好的支持大批量數(shù)據(jù)的更新操作,但是我們也可以很靈活的處理這個(gè)問題,比如使用循環(huán)每20條數(shù)據(jù)清除一次緩存。
2.save,update,saveOrUpdate,load,get,list,iterate,lock這些方法都會(huì)將對(duì)象放在一級(jí)緩存中,一級(jí)緩存不能控制緩存的數(shù)量,所以要注意大批量操作數(shù)據(jù)時(shí)可能造成內(nèi)存溢出;可以用evict,clear方法清除緩存中的內(nèi)容。
二級(jí)緩存:
二級(jí)緩存也稱為進(jìn)程級(jí)緩存或SessionFactory級(jí)緩存,二級(jí)緩存可以被所有的session緩存共享。二級(jí)緩存的生命周期和SessionFactory的生命周期一致,SessionFactory可以管理二級(jí)緩存,二級(jí)緩存的原則是當(dāng)讀遠(yuǎn)大于寫的時(shí)候使用,二級(jí)緩存也主要是緩存實(shí)體對(duì)象的。
二級(jí)緩存的配置:
1.將ehcahe.xml文件拷貝到src目錄下。
2.在Hibernate.cfg.xml文件中加入緩存產(chǎn)品提供商,如下:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
3.啟用二級(jí)緩存(可以不顯示啟動(dòng),因?yàn)槟J(rèn)就是啟用的),如下:
<property name="hibernate.cache.use_second_level_cache">true</property>
4.指定哪些實(shí)體類使用二級(jí)緩存。
5.導(dǎo)入緩存使用的接口jar包:lib\optional\ehcache\ehcache-core-2.4.3.jar
ehcache.xml文件的內(nèi)容:
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
注:
1.maxElementsInMemory表示緩存中最多存放的對(duì)象。
2.eternal表示是否永遠(yuǎn)不過期(設(shè)置為false更有實(shí)際意義,如果為true的話表示永遠(yuǎn)不過期,那么下面的屬性都沒什么意義了)。
3.timeToIdleSecods表示一個(gè)對(duì)象第一次被訪問后經(jīng)過多長時(shí)間沒被訪問就清除。
4.timeToLiveSecods表示一個(gè)對(duì)象的存貨時(shí)間。
5.overflowToDisk為true表示緩存中超出了maxElementsInMemory指定的個(gè)數(shù)就存到磁盤中。
指定溢出時(shí)保存的磁盤路徑:
<diskStore path="java.io.tmpdir"/>
注:這個(gè)路徑可以改。
測(cè)試方法(一級(jí)緩存的前提是必須在同一個(gè)session,現(xiàn)在我們使用二級(jí)緩存來看看在兩個(gè)不同的session中是否存在緩存):
public void testCache1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}
catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
//不會(huì)發(fā)出查詢語句,因?yàn)榕渲枚?jí)緩存,session可以共享二級(jí)緩存中的數(shù)據(jù)
//二級(jí)緩存是進(jìn)程級(jí)的緩存
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}
catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
finally {
HibernateUtils.closeSession(session);
}
}
注:如果配置了二級(jí)緩存,我們會(huì)發(fā)現(xiàn),即使第一個(gè)session關(guān)閉了,再開啟另外一個(gè)session去加載數(shù)據(jù)也不會(huì)發(fā)出語句到數(shù)據(jù)庫中去查詢數(shù)據(jù),因?yàn)榕渲昧硕?jí)緩存,它是整個(gè)sessionFactory共享的。
禁用二級(jí)緩存實(shí)現(xiàn)大批量數(shù)據(jù)的添加:
public void testCache5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//禁止一級(jí)緩存和二級(jí)緩存交互
session.setCacheMode(CacheMode.IGNORE);
for (int i=0; i<100; i++) {
Student student = new Student();
student.setName("張三" + i);
session.save(student);
//每20條更新一次
if (i % 20 == 0) {
session.flush();
//清除緩存的內(nèi)容
session.clear();
}
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
注:session.flush()表示清除一級(jí)緩存,但是我們又開起了二級(jí)緩存,save()之后也保存到了二級(jí)緩存,還是存在緩存過大導(dǎo)致溢出的情況。所以這種情況下我們應(yīng)該禁用二級(jí)緩存:session.setCacheMode(CacheMode.IGNORE);
查詢緩存:一級(jí)緩存和二級(jí)緩存都是緩存實(shí)體對(duì)象的,但是有些時(shí)候我們希望獲取某些屬性的時(shí)候也不要頻繁的去訪問數(shù)據(jù)庫,而是從緩存中獲取,此時(shí)我們就可以使用查詢緩存。另外查詢緩存對(duì)實(shí)體對(duì)象的結(jié)果集會(huì)緩存ID。查詢緩存的生命周期,當(dāng)關(guān)聯(lián)的表發(fā)生修改,查詢緩存的聲明周期就結(jié)束,和session的生命周期無關(guān)。
配置查詢緩存:
1.修改hibernate.cfg.xml文件,來開啟查詢緩存,默認(rèn)是false即不開啟的,應(yīng)如下設(shè)置:
<property name="hibernate.cache.use_query_cache">true</property>
2.必須在程序中啟用,如:
query.setCacheable(true)
測(cè)試方法:
public void testCache2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}
catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
finally {
HibernateUtils.closeSession(session);
}
System.out.println("-------------------------------------------------------");
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不會(huì)發(fā)出查詢語句,因?yàn)椴樵兙彺婧蛃ession的生命周期沒有關(guān)系
List names = session.createQuery("select s.name from Student s")
.setCacheable(true)
.list();
for (int i=0; i<names.size(); i++) {
String name = (String)names.get(i);
System.out.println(name);
}
session.getTransaction().commit();
}
catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
finally {
HibernateUtils.closeSession(session);
}
}
注:上述代碼中,我們關(guān)閉了二級(jí)緩存,開啟了查詢緩存,然后查詢普通屬性。運(yùn)行測(cè)試代碼我們可以發(fā)現(xiàn),在第一個(gè)session中第一次查詢發(fā)出了一條語句,然后關(guān)閉了session,接著再第二個(gè)session中進(jìn)行查詢,我們會(huì)發(fā)現(xiàn)第二個(gè)session中的查詢并沒有發(fā)出語句,這說明查詢緩存和session的生命周期沒有什么關(guān)系。
hibernate.cfg.xml的緩存配置:
<!-- 設(shè)置指定二級(jí)緩存的實(shí)現(xiàn)接口 -->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>
<!-- 設(shè)置二級(jí)緩存所使用的配置文件 -->
<property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>
<!-- 設(shè)置使用QUERY查詢緩存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- 加載對(duì)象關(guān)系映射文件 -->
<mapping resource="com/lixue/bean/Classes.hbm.xml" />
<mapping resource="com/lixue/bean/Student.hbm.xml" />
<!-- 必須先引入資源映射文件(就是實(shí)體映射文件)后再設(shè)置有使用二級(jí)緩存的實(shí)體類 -->
<class-cache usage="read-only" class="com.lixue.bean.Student" />
總結(jié)
以上就是本文關(guān)于Hibernate緩存機(jī)制實(shí)例代碼解析的全部內(nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
spring中@Transactional?注解失效的原因及解決辦法
面試中經(jīng)常會(huì)被問到事務(wù)失效的場(chǎng)景有哪些,本文主要介紹了spring中@Transactional?注解失效的原因及解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
在SpringMVC框架下實(shí)現(xiàn)文件的上傳和下載示例
本篇文章主要介紹了在SpringMVC框架下實(shí)現(xiàn)文件的上傳和下載示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
Java對(duì)int[]數(shù)組做新增刪除去重操作代碼
這篇文章主要介紹了Java里面對(duì)int[]數(shù)組做新增刪除去重實(shí)現(xiàn),這里記錄下使用int[]數(shù)組對(duì)數(shù)組進(jìn)行新增刪除去重等操作,用來更加了解java里面的集合類思想,需要的朋友可以參考下2023-10-10
Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例
這篇文章主要介紹了Javaweb實(shí)現(xiàn)在線人數(shù)統(tǒng)計(jì)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Springboot整合WebSocket實(shí)戰(zhàn)教程
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù),這篇文章主要介紹了Springboot整合WebSocket實(shí)戰(zhàn)教程,需要的朋友可以參考下2023-05-05
Java類型轉(zhuǎn)換valueOf與parseInt區(qū)別探討解析
這篇文章主要為大家介紹了Java類型轉(zhuǎn)換valueOf與parseInt區(qū)別探討解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

