查询JOIN FETCH性能问题

我有hibernate查询性能的问题,我无法弄清楚。 在下面的代码片段中,我需要选择具有至少一个映射和过滤映射的实体。 我正在使用FETCH JOIN来加载仅过滤的映射。 但在这种情况下,我的查询存在性能问题。 Hibernate说警告日志:

org.hibernate.hql.ast.QueryTranslatorImpl – 使用collection fetch指定的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 mappings = new ArrayList(); ... } @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; ... } 

谢谢

来自JPA规范

将setMaxResults或setFirstResult应用于涉及对集合进行提取连接的查询的效果未定义。 (JPA“Enterprise JavaBeans 3.0,Final Release”,Kapitel 3.6.1查询接口)

Hibernate做正确的事情,但在内存中执行查询的一部分,这是非常慢的。 在我的情况下,差异在3-5毫秒到400-500毫秒之间。

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

在为JVM增加内存之后事情变得更好了。 毕竟我在查询中不使用FETCH结束。

如果您需要带有“fetch”的firstResult / maxResults,则可以在2个查询中拆分查询:

  1. 使用firstResult / maxResults查询实体ID,但子表上没有“fetch”:

     select entity.id from entity (without fetch) where .... (with firstResult/maxResults) 
  2. 使用第一个查询返回的ID上的“fetch”查询您的实体:

     select entity from entity fetch ... where id in  

原因很慢是因为Hibernate根本没有分页执行SQL查询,而且限制是在内存中完成的。

但是,如果连接必须扫描并获取100k记录,而您只对100个结果感兴趣,那么提取器完成的99.9%的工作以及通过网络完成的所有I / O都是浪费。

正如我在本文中解释的那样,您可以轻松地转换使用JOIN FETCH和分页的JPQL查询:

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

而已!