首頁 >Java >java教程 >詳解Java的Hibernate框架中的快取與二級快取

詳解Java的Hibernate框架中的快取與二級快取

高洛峰
高洛峰原創
2017-01-23 09:57:091328瀏覽

快取

今天我們就來講一下hibernate中實體狀態和hibernate快取。
 1)首先我們先來看看實體狀態:
 實體狀態主要分為三種:transient,persitent,detached。
 看英文應該就大概懂了吧。
 transient:是指資料還沒跟資料庫中的資料相對應。
 persistent:是指資料跟資料庫中的資料相對應,它的任何變更都會反映在資料庫中。
 detached:是指資料跟資料庫中的資料相對應,但由於session被關閉,它所做的修改不會對資料庫的記錄造成影響。
 下面我們直接程式碼來:

Transaction tx = session.beginTransaction(); 
User user = new User(); 
user.setName("shun"); 
//这里的user还未保存到数据库,数据库表中并没有与之对应的记录,它为transient状态 
session.save(user); 
tx.commit(); 
//提交之后user变为persistent状态 
session.close(); 
//由于session关闭,此时的user为detached状态,它的所有修改都不会反映到数据库中。 
      
Session session2 = sessionFactory.openSession(); 
tx = session2.beginTransaction(); 
user.setName("shun123"); 
session2.saveOrUpdate(user); 
tx.commit(); 
//当我们调用了saveOrUpdate之后,user重新变为persistent状态,它的所有修改都会反映到数据库中。 
session2.close();

  我們看到程式碼,首先我們定義了一個物件user,在未儲存之前,它就是transient狀態,在資料庫中並沒有與它對應的記錄。而當我們進行儲存並提交修改後,user成為persistent狀態,在資料庫中有對應的記錄。而當我們把session關閉後,user就變成了detached狀態了,它的更改並不會反映到資料庫中,除非我們手動呼叫saveOrUpdate等對應的更新和新增方法。而當我們直接想讓它從persistent到transient狀態,該怎麼辦呢?直接刪除就可以了,刪除後物件就在資料庫中沒有對應的記錄,也就變成transient狀態了。
 
 hibernate的狀態轉換還是比較簡單的,當是transient狀態時,資料庫沒有記錄對應,而persistent和detached時都有對應的記錄,但唯一的差別是detached是在session關閉之後才有的狀態。那麼transient和detached的差別又是什麼呢?就是有沒有資料庫表記錄對應的問題。
 
 2)看完了狀態我們來看看hibernate的快取
 hibernate的快取分成兩種,一級快取和二級快取。
 一級快取:所謂的一級快取也就是內部快取。
 二級緩存:它包括應用程式級緩存,在hibernate就是所謂的SessionFactory緩存,另外一個是分散式緩存,這個是最安全的快取方式。
 直接來看程式:

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(); 
}

 看結果:

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,但結果中只有一句SQL語句,這表示它只查詢了一次。
 為什麼呢?這也就是hibernate的快取運作了。第一次查詢完畢後,hibernate後把查出來的實體放在緩存中,下次查的時候首先會查緩存,看有沒有對應ID的實體存在,如果有則直接取出,否則則進行數據庫的查詢。
 
 下面我們把程式碼修改成:

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();

  看到結果:

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從快取中刪除後,第二次的查詢也直接從資料庫中取出。

二級快取小談
先看實體類別:

public class User implements Serializable{
  
  public Long id;
  private String name;
  private int age;
    
}

  映射檔就省略啦,大家應該都會寫的。
  再來看看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這個提供類,所以我們也需要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>
 接下來,我們直接看一下測試方法:我們直接看一下測試方法:
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 = &#39;shun123&#39;");
  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=&#39;shun123&#39;");
  Iterator iter2 = query2.iterate();
  while(iter2.hasNext()) {
    System.out.println(((User)iter2.next()).getName());
  }
    
  session2.close();
  
}

  


  運行後可以看到:

Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME=&#39;shun123&#39;
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=&#39;shun123&#39;
shun123

  我們可以看到它只執行了一句搜索,而在第二次查詢時並沒有取出ID進行搜索,這主要歸功於二級緩存。

 

 下面我們先分析測試方法中的程式碼。測試方法中我們分別開啟了兩個Session並且分別建立兩個Query進行相同的查詢。但兩次Session可以共用緩存,也就是二級緩存,SessionFactory級的快取。只要我們的Session由同一個SessionFactory創建,那麼我們就可以共用二級快取來減少與資料庫的互動。
  我們再看一下設定檔中的意思:

<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>

  如果我們需要使用二級緩存,首先需要設定:

<property name="hibernate.cache.use_second_level_cache">true</property>

  進行開戶二級緩存,然後透過:

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

 情況下我們都用ehcache,其他我的暫時沒用到,也不太清楚,所以暫時不講了。

 像我們剛才的例子,我們只需要配置上面兩個,完全可以正常運行,利用二級快取。

 那麼第三句是做什麼用的呢?

<property name="hibernate.cache.use_query_cache">true</property>

  這個配置指明了我們在查詢時需要利用緩存,如果我們需要用到這個要事先調用query.setCacheable(true)這個方法來進行啟用。

 

 我們一起來看程式碼(我們先不啟用快取):

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 = &#39;shun123&#39;");
  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=&#39;shun123&#39;");
  List list2 = query2.list();
  for (int i = 0; i < list2.size(); i++){
    System.out.println(((User)list.get(i)).getName());
  }
    
  session2.close();
  
}

  這裡輸出的結果是:

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=&#39;shun123&#39;
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=&#39;shun123&#39;
shun123

  我們看到,它並沒有利用緩存,因為我們這裡用了list,而list對快取是隻隻寫不讀的。所以這裡會進行兩次查詢。

 那我們來修改一下:

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 = &#39;shun123&#39;");
  <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=&#39;shun123&#39;");
  <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();
  
}

  看到红色的两句代码,这是我们进行添加的两个开启查询缓存的代码,现在我们看到结果:

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=&#39;shun123&#39;
shun123
shun123

  只剩一次查询了,为什么呢?就在那两句红色代码处,我们开启了缓存,记住,需要使用两次。把两个query都设成可缓存的才能使用查询缓存。
 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();
  
}

  我们看结果:

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

更多详解Java的Hibernate框架中的缓存与二级缓存相关文章请关注PHP中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn