ホームページ  >  記事  >  Java  >  Java の Hibernate フレームワークにおけるキャッシュと 2 次キャッシュの詳細な説明

Java の Hibernate フレームワークにおけるキャッシュと 2 次キャッシュの詳細な説明

高洛峰
高洛峰オリジナル
2017-01-23 09:57:091292ブラウズ

キャッシュ

今日は、エンティティのステータスと休止状態での休止状態キャッシュについて説明します。
1) まず、エンティティのステータスを見てみましょう:
エンティティのステータスには、主に一時的、永続的、分離の 3 つのタイプがあります。
英語で読めばおそらく理解できるはずです。
一時的: データがまだデータベース内のデータに対応していないことを意味します。
永続的: データがデータベース内のデータに対応し、変更がデータベースに反映されることを意味します。
分離: データはデータベース内のデータに対応しますが、セッションが閉じられているため、セッションによる変更はデータベース内のレコードに影響を与えないことを意味します。
コードに直接進みましょう:

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

コードを見ると、まずオブジェクト ユーザーを定義します。保存される前は一時的な状態であり、データベースには対応するレコードがありません。変更を保存して送信すると、ユーザーは永続化され、データベースに対応するレコードが保持されます。セッションを閉じるとユーザーは切り離され、saveOrUpdate やその他の対応する更新および追加メソッドを手動で呼び出さない限り、その変更はデータベースに反映されません。また、永続状態から一時状態に直接変更したい場合はどうすればよいでしょうか?直接削除してください。削除後、オブジェクトはデータベース内に対応するレコードを持たなくなり、一時的な状態になります。

Hibernate の状態遷移は比較的単純です。一時状態の場合、データベースに対応するレコードはありませんが、永続化とデタッチには対応するレコードがあります。ただし、唯一の違いは、デタッチはその後にのみ存在する状態であることです。セッションは終了します。それでは、一時的なものと孤立したものの違いは何でしょうか?データベーステーブルに対応するレコードがあるかどうかという問題があります。

2) ステータスを読み込んだら、Hibernate のキャッシュを見てみましょう
Hibernate のキャッシュは、一次キャッシュと二次キャッシュの 2 種類に分かれています。
一次キャッシュ: いわゆる一次キャッシュは内部キャッシュでもあります。
第 2 レベルのキャッシュ: 休止状態では、いわゆる 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 を 2 回使用しましたが、結果には SQL ステートメントが 1 つだけあり、クエリが 1 回だけ行われたことがわかります。
なぜですか?ここで 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

キャッシュからユーザーを削除した後、2 番目のクエリもデータベースから直接取得されます。

2次キャッシュの話
まずエンティティクラスを見てください:

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 を置く必要もあります:

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

検索のみが実行され、2 番目のクエリ中に検索用の ID が取得されないことがわかります。二次キャッシュ。

まず、テストメソッドのコードを分析しましょう。テスト メソッドでは、2 つのセッションを開き、同じクエリを実行する 2 つのクエリを作成しました。ただし、2 つのセッションはキャッシュ (2 次キャッシュと SessionFactory レベルのキャッシュ) を共有できます。セッションが同じ SessionFactory によって作成されている限り、2 次キャッシュを共有してデータベースとの対話を減らすことができます。
設定ファイルの意味を見てみましょう:

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

2 次キャッシュを使用する必要がある場合は、まず 2 次キャッシュ用のアカウントを開くように

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

を設定する必要があります。 :

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

は、2次キャッシュのプロバイダークラスを指定します。この場合、私たちは皆ehcacheを使用しますが、他のものは今のところ使用したことがなく、あまり詳しくありません。今はそれらについては話しません。
先ほどの例と同様に、上記の 2 つを設定するだけで、正常に実行され、2 次キャッシュを使用できます。
では、3 番目の文は何のためにあるのでしょうか?

<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

ここではリストを使用しているため、キャッシュが使用されていないことがわかります。リストはキャッシュ専用 書き込み読み取り不可。したがって、ここには 2 つのクエリがあります。
次に、それを変更しましょう:

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 までご連絡ください。