2011-02-09 9 views
3

私が把握できないhibernate問合せのパフォーマンスに問題があります。以下のコードスニペットでは、少なくとも1つのマッピングとフィルタリングされたマッピングを持つ選択エンティティが必要です。私はこれをフィルタリングされたマッピングだけをロードするためにFETCH JOINを使用しています。 しかし、その場合、私はクエリでパフォーマンスの問題があります。 Hibernateは、警告ログ言う:JOIN FETCHパフォーマンス問合せでの問合せ

org.hibernate.hql.ast.QueryTranslatorImpl - firstResultコレクションで指定された/ maxResultsフェッチ。メモリは でお申し込みください!

私がFETCH JOINを省略して残すのは、JOINクエリのみが高速です。しかし、結果として、すべてのマッピングがエンティティにロードされていますが、これは私には受け入れられない状態です。クエリのパフォーマンスを向上させる方法はありますか?マッピングテーブルには多くの行があります。

HQLクエリー:

select distinct e from Entity 
    join fetch e.mappings as mapping 
where e.deleted = 0 and e.mappings is not empty 
    and e = mapping.e and mapping.approval in (:approvals) 

エンティティ:

@Entity 
@Table(name="entity") 
class Entity { 

    ... 

    @OneToMany(mappedBy="entity", cascade=CascadeType.REMOVE, fetch=FetchType.LAZY) 
    @OrderBy("created") 
    private List<Mapping> mappings = new ArrayList<Mapping>(); 

    ... 
} 

@Entity 
@Table(name="mapping") 
class Mapping { 

public static enum MappingApproval { 
    WAITING, // mapping is waiting for approval 
    APPROVED, // mapping was approved 
    DECLINED; // mapping was declined 
} 

... 

    @ManyToOne(fetch=FetchType.EAGER) 
    @JoinColumn(name="entity_id", nullable=false) 
    private Entity entity; 

    @Enumerated(EnumType.STRING) 
    @Column(name="approval", length=20) 
    private MappingApproval approval; 

... 

} 

おかげJVMのもののためにメモリを増加させた後

+0

Hibernateが発行したSQLクエリを表示できますか? – axtavt

+0

@axtavt私はHibenateからSQLクエリを追加しました。御時間ありがとうございます。 –

+1

'JOIN'または' FETCH JOIN'のクエリですか?別の場所はどこですか? – axtavt

答えて

0

は、はるかに優れて行きます。結局、私はクエリでFETCHを使用しないで終了します。

+4

奇妙な状況では、ちょうどヒープサイズを増やしてください:) –

+3

これは良い答えではありません。 – Amalgovinus

5
JPA-仕様から

フェッチ含むクエリ にはsetMaxResultsまたはsetFirstResultメソッドを適用する効果は、コレクションの上に加わり定義されていません。 (JPA「エンタープライズJavaBeansの 3.0、最終リリース」、Kapitel 3.6.1クエリーインターフェイス)

Hibernateは正しいことを行いますが、途方もなく遅いメモリ内のクエリの一部を実行します。私の場合、差は3〜5ミリ秒から400〜500ミリ秒です。

私の解決策は、クエリ自体の中でページングを実装することでした。 JOIN FETCHで高速に動作します。

0

あなたは2つのクエリにクエリを分割することができ、「フェッチ」とfirstResult/maxResults必要がある場合:

  1. クエリfirstResult/maxResultsでなく、サブテーブルの上に「フェッチ」なしのエンティティIDを:

    select entity.id from entity (without fetch) where .... (with firstResult/maxResults) 
    
  2. クエリを使用してエンティティあなたの最初のクエリによって返されたIDに "フェッチ":

    select entity from entity fetch ... where id in <previous ids> 
    
0

なぜなら、HibernateはページングなしでSQLクエリを実行し、メモリ内で制限が実行されるためです。

ただし、参加者が100kのレコードをスキャンして取得する必要がある場合は、100個の結果だけに興味がありますが、エクストラクタで実行されている作業の99.9%とネットワーキングで実行されたすべてのI/Oは無駄です。

私はthis articleで説明したように、あなたは簡単に両方を取得し、ページネーションJOINを使用していますJPQLクエリ回すことができます。

:親識別子によってDENSE_RANKを使用して結果を制限するSQLクエリに

List<Post> posts = entityManager.createQuery(
    "select p " + 
    "from Post p " + 
    "left join fetch p.comments " + 
    "where p.title like :title " + 
    "order by p.id", Post.class) 
.setParameter("title", titlePattern) 
.setMaxResults(maxResults) 
.getResultList(); 

List<Post> posts = entityManager.createNativeQuery(
    "select p_pc_r.* " + 
    "from ( " + 
    " select *, dense_rank() OVER (ORDER BY post_id) rank " + 
    " from ( " + 
    "  select p.*, pc.* " + 
    "  from post p " + 
    "  left join post_comment pc on p.id = pc.post_id " + 
    "  where p.title like :title " + 
    "  order by p.id " + 
    " ) p_pc " + 
    ") p_pc_r " + 
    "where p_pc_r.rank <= :rank", Post.class) 
.setParameter("title", titlePattern) 
.setParameter("rank", maxResults) 
.unwrap(NativeQuery.class) 
.addEntity("p", Post.class) 
.addEntity("pc", PostComment.class) 
.setResultTransformer(DistinctPostResultTransformer.INSTANCE) 
.getResultList(); 
はエンティティグラフに戻す設定表形式の結果を変換するには、次のようになります ResultTransformer必要があります。

public class DistinctPostResultTransformer 
     extends BasicTransformerAdapter { 

    private static final DistinctPostResultTransformer INSTANCE = 
      new DistinctPostResultTransformer(); 

    @Override 
    public List transformList(List list) { 
     Map<Serializable, Identifiable> identifiableMap = 
       new LinkedHashMap<>(list.size()); 

     for (Object entityArray : list) { 
      if (Object[].class.isAssignableFrom( 
        entityArray.getClass())) { 
       Post post = null; 
       PostComment comment = null; 

       Object[] tuples = (Object[]) entityArray; 

       for (Object tuple : tuples) { 
        if(tuple instanceof Post) { 
         post = (Post) tuple; 
        } 
        else if(tuple instanceof PostComment) { 
         comment = (PostComment) tuple; 
        } 
        else { 
         throw new UnsupportedOperationException(
          "Tuple " + tuple.getClass() + " is not supported!" 
         ); 
        } 
       } 
       Objects.requireNonNull(post); 
       Objects.requireNonNull(comment); 

       if (!identifiableMap.containsKey(post.getId())) { 
        identifiableMap.put(post.getId(), post); 
        post.setComments(new ArrayList<>()); 
       } 
       post.addComment(comment); 
      } 
     } 
     return new ArrayList<>(identifiableMap.values()); 
    } 
} 

それだけです!