詳解Java的Hibernate框架中的緩存與二級(jí)緩存
緩存
今天我們就來講一下hibernate中實(shí)體狀態(tài)和hibernate緩存。
1)首先我們先來看一下實(shí)體狀態(tài):
實(shí)體狀態(tài)主要分三種:transient,persitent,detached。
看英文應(yīng)該就大概明白了吧。
transient:是指數(shù)據(jù)還沒跟數(shù)據(jù)庫(kù)中的數(shù)據(jù)相對(duì)應(yīng)。
persistent:是指數(shù)據(jù)跟數(shù)據(jù)庫(kù)中的數(shù)據(jù)相對(duì)應(yīng),它的任何改變都會(huì)反映到數(shù)據(jù)庫(kù)中。
detached:是指數(shù)據(jù)跟數(shù)據(jù)庫(kù)中的數(shù)據(jù)相對(duì)應(yīng),但由于session被關(guān)閉,它所做的修改不會(huì)對(duì)數(shù)據(jù)庫(kù)的記錄造成影響。
下面我們直接代碼來:
Transaction tx = session.beginTransaction();
User user = new User();
user.setName("shun");
//這里的user還未保存到數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)表中并沒有與之對(duì)應(yīng)的記錄,它為transient狀態(tài)
session.save(user);
tx.commit();
//提交之后user變?yōu)閜ersistent狀態(tài)
session.close();
//由于session關(guān)閉,此時(shí)的user為detached狀態(tài),它的所有修改都不會(huì)反映到數(shù)據(jù)庫(kù)中。
Session session2 = sessionFactory.openSession();
tx = session2.beginTransaction();
user.setName("shun123");
session2.saveOrUpdate(user);
tx.commit();
//當(dāng)我們調(diào)用了saveOrUpdate之后,user重新變?yōu)閜ersistent狀態(tài),它的所有修改都會(huì)反映到數(shù)據(jù)庫(kù)中。
session2.close();
我們看到代碼,首先我們定義了一個(gè)對(duì)象user,在未保存之前,它就是transient狀態(tài),在數(shù)據(jù)庫(kù)中并沒有與它相應(yīng)的記錄。而當(dāng)我們進(jìn)行保存并提交修改后,user成為persistent狀態(tài),在數(shù)據(jù)庫(kù)中有相應(yīng)的一條記錄。而當(dāng)我們把session關(guān)閉后,user就變成了detached狀態(tài)了,它的更改并不會(huì)反映到數(shù)據(jù)庫(kù)中,除非我們手動(dòng)調(diào)用saveOrUpdate等相應(yīng)的更新和添加方法。而當(dāng)我們直接想讓它從persistent到transient狀態(tài),怎么辦呢?直接刪除就可以了,刪除后對(duì)象就在數(shù)據(jù)庫(kù)中沒有對(duì)應(yīng)的記錄,也就成transient狀態(tài)了。
hibernate的狀態(tài)轉(zhuǎn)換還是比較簡(jiǎn)單的,當(dāng)是transient狀態(tài)時(shí),數(shù)據(jù)庫(kù)沒有記錄對(duì)應(yīng),而persistent和detached時(shí)都有對(duì)應(yīng)的記錄,但唯一的區(qū)別是detached是在session關(guān)閉之后才有的狀態(tài)。那么transient和detached的區(qū)別又是什么呢?就是有沒有數(shù)據(jù)庫(kù)表記錄對(duì)應(yīng)的問題。
2)看完了狀態(tài)我們來看一下hibernate的緩存
hibernate的緩存分兩種,一級(jí)緩存和二級(jí)緩存。
一級(jí)緩存:所謂的一級(jí)緩存也就是內(nèi)部緩存。
二級(jí)緩存:它包括應(yīng)用級(jí)緩存,在hibernate就是所謂的SessionFactory緩存,另外一個(gè)是分布式緩存,這個(gè)是最安全的緩存方式。
直接來看程序:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
User user = (User)session.load(User.class,new Long(29));
System.out.println(user.getName());
User user2 = (User)session.load(User.class,new Long(29));
System.out.println(user2.getName());
session.close();
}
看結(jié)果:
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123123 shun123123
例子中我們用了兩次load,但結(jié)果中只有一句SQL語句,這表明它只查詢了一次。
為什么呢?這也就是hibernate的緩存起作用了。第一次查詢完畢后,hibernate后把查出來的實(shí)體放在緩存中,下一次查的時(shí)候首先會(huì)查緩存,看有沒有對(duì)應(yīng)ID的實(shí)體存在,如果有則直接取出,否則則進(jìn)行數(shù)據(jù)庫(kù)的查詢。
下面我們把代碼修改成:
User user = (User)session.load(User.class,new Long(29));
System.out.println(user.getName());
session.evict(user);//把user從緩存中刪掉
User user2 = (User)session.load(User.class,new Long(29));
System.out.println(user2.getName());
session.close();
看到結(jié)果:
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123123 Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123123
自己我們把user從緩存中刪除后,第二次的查詢也直接從數(shù)據(jù)庫(kù)中取出。
二級(jí)緩存小談
先看實(shí)體類:
public class User implements Serializable{
public Long id;
private String name;
private int age;
}
映射文件就省略啦,大家應(yīng)該都會(huì)寫的。
再來看看hibernate配置文件:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property>
我們看到provider_class中我們指定了ehcache這個(gè)提供類,所以我們也需要ehcache.xml放在classpath中:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.path"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
</ehcache>
接下來,我們直接看一下測(cè)試方法:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Query query = session.createQuery("from User user where name = 'shun123'");
Iterator iter = query.iterate();
while(iter.hasNext()) {
System.out.println(((User)iter.next()).getName());
}
session.close();
Session session2 = sessionFactory.openSession();
Query query2 = session2.createQuery("from User user where name='shun123'");
Iterator iter2 = query2.iterate();
while(iter2.hasNext()) {
System.out.println(((User)iter2.next()).getName());
}
session2.close();
}
運(yùn)行后可以看到:
Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME='shun123' Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123 Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME='shun123' shun123
我們可以看到它只執(zhí)行了一句搜索,而在第二次查詢時(shí)并沒有取出ID進(jìn)行搜索,這主要?dú)w功于二級(jí)緩存。
下面我們先分析一下測(cè)試方法中的代碼。測(cè)試方法中我們分別打開了兩個(gè)Session并且分別創(chuàng)建兩個(gè)Query進(jìn)行相同的查詢。但兩次Session可以共用緩存,這也就是二級(jí)緩存,SessionFactory級(jí)的緩存。只要我們的Session由同一個(gè)SessionFactory創(chuàng)建,那么我們就可以共用二級(jí)緩存減少與數(shù)據(jù)庫(kù)的交互。
我們?cè)賮砜匆幌屡渲梦募械囊馑迹?br />
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property>
如果我們需要使用二級(jí)緩存,首先需要配置:
<property name="hibernate.cache.use_second_level_cache">true</property>
進(jìn)行開戶二級(jí)緩存,然后通過:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
指定二級(jí)緩存的提供類,一般情況下我們都用ehcache,其他我的暫時(shí)沒用到,也不太清楚,所以暫時(shí)不講了。
像我們剛才的例子,我們只需要配置上面兩個(gè),完全可以正常運(yùn)行,利用二級(jí)緩存。
那么第三句是干什么用的呢?
<property name="hibernate.cache.use_query_cache">true</property>
這個(gè)配置指明了我們?cè)诓樵儠r(shí)需要利用緩存,如果我們需要用到這個(gè)要事先調(diào)用query.setCacheable(true)這個(gè)方法來進(jìn)行啟用。
我們一起來看代碼(我們先不啟用緩存):
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Query query = session.createQuery("from User user where name = 'shun123'");
List list = query.list();
for (int i = 0; i < list.size(); i++){
System.out.println(((User)list.get(i)).getName());
}
session.close();
Session session2 = sessionFactory.openSession();
Query query2 = session2.createQuery("from User user where name='shun123'");
List list2 = query2.list();
for (int i = 0; i < list2.size(); i++){
System.out.println(((User)list.get(i)).getName());
}
session2.close();
}
這里輸出的結(jié)果是:
Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME='shun123' shun123 Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME='shun123' shun123
我們看到,它并沒有利用緩存,因?yàn)槲覀冞@里用了list,而list對(duì)緩存是只寫不讀的。所以這里會(huì)進(jìn)行兩次查詢。
那么我們來修改一下:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Query query = session.createQuery("from User user where name = 'shun123'");
<span style="background-color: #ffffff;"><span style="color: #ff0000;">query.setCacheable(true);</span></span>
List list = query.list();
for (int i = 0; i < list.size(); i++){
System.out.println(((User)list.get(i)).getName());
}
session.close();
Session session2 = sessionFactory.openSession();
Query query2 = session2.createQuery("from User user where name='shun123'");
<span style="color: #ff0000;">query2.setCacheable(true);</span>
List list2 = query2.list();
for (int i = 0; i < list2.size(); i++){
System.out.println(((User)list.get(i)).getName());
}
session2.close();
}
看到紅色的兩句代碼,這是我們進(jìn)行添加的兩個(gè)開啟查詢緩存的代碼,現(xiàn)在我們看到結(jié)果:
Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME='shun123' shun123 shun123
只剩一次查詢了,為什么呢?就在那兩句紅色代碼處,我們開啟了緩存,記住,需要使用兩次。把兩個(gè)query都設(shè)成可緩存的才能使用查詢緩存。
Criteria也是類似的做法,為免有些童鞋忘記了Criteria怎么寫了,我還是放一下代碼:
public static void main(String[] args) {
Configuration cfg = new Configuration().configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Criteria criteria1 = session.createCriteria(User.class);
criteria1.setCacheable(true);
criteria1.add(Restrictions.eq("name","shun123"));
List list = criteria1.list();
for (int i = 0; i < list.size(); i++){
System.out.println(((User)list.get(i)).getName());
}
session.close();
Session session2 = sessionFactory.openSession();
Criteria criteria2 = session2.createCriteria(User.class);
criteria2.setCacheable(true);
criteria2.add(Restrictions.eq("name","shun123"));
List list2 = criteria2.list();
for (int i = 0; i < list2.size(); i++){
System.out.println(((User)list.get(i)).getName());
}
session2.close();
}
我們看結(jié)果:
Hibernate: select this_.USER_ID as USER1_0_0_, this_.USER_NAME as USER2_0_0_, this_.age as age0_0_ from USER this_ where this_.USER_NAME=? shun123 shun123
相關(guān)文章
使用Java把文本內(nèi)容轉(zhuǎn)換成網(wǎng)頁(yè)的實(shí)現(xiàn)方法分享
這篇文章主要介紹了使用Java把文本內(nèi)容轉(zhuǎn)換成網(wǎng)頁(yè)的實(shí)現(xiàn)方法分享,利用到了Java中的文件io包,需要的朋友可以參考下2015-11-11
mybatis3.4.0不支持LocalDateTime的解決方法(No typehandler f
本文主要介紹了mybatis3.4.0不支持LocalDateTime的解決方法(No typehandler found for property time),具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案
這篇文章主要介紹了關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
最新IDEA?2022基于JVM極致優(yōu)化?IDEA啟動(dòng)速度的方法
這篇文章主要介紹了IDEA?2022最新版?基于?JVM極致優(yōu)化?IDEA?啟動(dòng)速度,需要的朋友可以參考下2022-08-08
Javaweb-HttpServletResponse的sendRedirectch重定向方式
這篇文章主要介紹了Javaweb-HttpServletResponse的sendRedirectch重定向方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09

