7

エンティティ属性値ベースの永続化メカニズムを実装しています。すべての DB アクセスは Hibernate 経由で行われます。ノードのパスを含むテーブルがあります。これは非常に単純で、ID とパス (文字列) だけです。パスの数は少なく、数千程度です。

メイン テーブルには何百万もの行があり、パスを繰り返すのではなく、独自のテーブルへのパスを正規化しました。以下は、メインテーブルに挿入するときに必要な動作です

1)パステーブルにパスが存在するかどうかを確認します(パス値をパラメーターとして使用して、エンティティマネージャーを介してクエリを実行します)

2) 存在しない場合は、ID を挿入して取得します (エンティティ マネージャーを介して永続化します)。

3) id を外部キー値としてメイン テーブルの行に配置し、これをメイン テーブルに挿入します。

これは、メイン テーブルと他のいくつかのテーブルの多数の行に対応する一連のドメイン オブジェクトに対して何千回も発生します。したがって、上記の手順は、次のような単一のトランザクションを使用して繰り返されます。

    EntityTransaction t = entityManager.getTransaction();
    t.begin();
    //perform steps given above, check, and then persist etc..
    t.commit();

ステップ 2 を実行すると、操作全体のパフォーマンスが大幅に低下します。しばらくすると、そのテーブルは非常にまれな新しい挿入を含む最大 10 ~ 20k のエントリになるため、キャッシュが必要です。私はHibernateでこれをやろうとしましたが、ほぼ2日を失いました.

JPAアノテーションとECacheを備えたHibernate 4.1を使用しています。以下に示すように、挿入全体で同じクエリ オブジェクトを使用しても、クエリ キャッシュを有効にしようとしました。

Query call = entityManager.createQuery("select pt from NodePath pt " +
                "where pt.path = :pathStr)");
        call.setHint("org.hibernate.cacheable", true);  
        call.setParameter("pathStr", pPath);
        List<NodePath> paths = call.getResultList();
        if(paths.size() > 1)
            throw new Exception("path table should have unique paths");
        else if (paths.size() == 1){
            NodePath path = paths.get(0);
            return path.getId();
        }
        else {//paths null or has zero size
            NodePath newPath = new NodePath();
            newPath.setPath(pPath);
            entityManager.persist(newPath);
            return newPath.getId();
        }

NodePath エンティティには、次のように注釈が付けられます。

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "node_path", schema = "public")
public class NodePath implements java.io.Serializable {

統計からわかる限り、クエリ キャッシュが使用されていますが、第 2 レベル キャッシュの使用は報告されていません。

queries executed to database=1
query cache puts=1
query cache hits=689
query cache misses=1
....
second level cache puts=0
second level cache hits=0
second level cache misses=0
entities loaded=1
....

単純な手書きのハッシュテーブルをキャッシュとして使用すると、期待どおりに動作し、合計時間が大幅に短縮されます。操作の性質上、Hibernate のキャッシュをトリガーできていないと思います。

このセットアップで休止状態の二次キャッシュを使用するにはどうすればよいですか? 記録のために、これは私の持続性 xml です。

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">

<provider>org.hibernate.ejb.HibernatePersistence</provider> 
<class>...</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

  <properties>
   <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
    <property name="hibernate.connection.password" value="zyx" />
    <property name="hibernate.connection.url" value="jdbc:postgresql://192.168.0.194:5432/testdbforml" />
    <property name="hibernate.connection.username" value="postgres"/>
    <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
    <property name="hibernate.search.autoregister_listeners" value="false"/>
    <property name="hibernate.jdbc.batch_size" value="200"/>
     <property name="hibernate.connection.autocommit" value="false"/> 
     <property name="hibernate.generate_statistics" value="true"/>
    <property name="hibernate.cache.use_structured_entries" value="true"/>

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

     <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>              

  </properties>

4

1 に答える 1

2

わかりました、見つけました。私の問題は、キャッシュされたクエリがキャッシュにクエリ結果のIDのみを保持していて、実際の値を取得するために、第2レベルのキャッシュから取得するのではなく、(おそらく)dbに戻っていたということでした。

もちろん、問題は、これらの値がプライマリIDによって選択されていないため、クエリがこれらの値を第2レベルのキャッシュに配置しなかったことです。したがって、解決策は、値を2番目のレベルのキャッシュに配置するメソッドを使用することです。Hibernate4.1では、これを自然IDで実行できます。これは、他の誰かを助ける場合に備えて、キャッシュから値を挿入または返す関数です。

private UUID persistPath(String pPath) throws Exception{
        org.hibernate.Session session = (Session) entityManager.getDelegate();
        NodePath np = (NodePath) session.byNaturalId(NodePath.class).using("path", pPath).load();
        if(np != null)
            return np.getId();
        else {//no such path entry, so let's create one
            NodePath newPath = new NodePath();
            newPath.setPath(pPath);
            entityManager.persist(newPath);
            return newPath.getId();
        }


    }
于 2012-05-15T15:32:44.797 に答える