2011-02-09 63 views
3

我有问题,我无法弄清楚hibernate查询性能。在下面的代码片段中,我需要选择至少有一个映射和过滤映射的实体。我正在使用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; 

... 

} 

感谢

+0

你可以显示由Hibernate发出的SQL查询吗? – axtavt 2011-02-09 13:05:44

+0

@axtavt我已经添加了来自Hibenate的SQL查询。谢谢你的时间。 – 2011-02-09 13:35:35

回答

0

为JVM的东西增加内存后去好得多。毕竟我最终没有在查询中使用FETCH。

5

从JPA的规范

应用setMaxResults或setFirstResult到查询 涉及的效果获取加入对集合是不确定的。 (JPA“企业 的JavaBeans 3.0,最终版本”,Kapitel 3.6.1查询界面)

休眠做正确的事,但执行记忆查询,这是极其慢的一部分。在我的情况下,差异在3-5毫秒到400-500毫秒之间。

我的解决方案是在查询本身内实现分页。 JOIN FETCH可以快速工作。

0

如果你需要一个FIRSTRESULT /带的maxResults“取”,你可以在2个查询拆分查询:

  1. 查询你的实体ID与FIRSTRESULT /的maxResults但没有在子表的“取”:

    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 FETCH和分页JPQL查询:

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

到SQL查询,使用DENSE_RANK由父标识符限制了结果:

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

就是这样!