【问题标题】:Efficient way to fetch list size获取列表大小的有效方法
【发布时间】:2020-12-19 03:00:04
【问题描述】:

我有一个像下面这样的实体。当我需要列出公司的评论大小时,我正在调用 totalComments() 方法。为此,hibernate 会转到数据库并获取整个评论数据还是仅使用 count(*) 进行查询?如果休眠获取整个评论,获取评论大小的有效方法是什么?


@Entity
@Table(name = "companies")
public class Company extends ItemEntity {

    @OneToMany(fetch = FetchType.LAZY)
    @JoinTable(name="companies_comments",
            joinColumns=@JoinColumn(name="company_id"),
            inverseJoinColumns=@JoinColumn(name="comment_id"))
    private Set<Comment> comments = new HashSet<>();

    public void addComment(Comment comment) {
        this.comments.add(comment);
    }

    public int totalComments() {
        return this.comments.size();
    }
}

【问题讨论】:

    标签: spring database hibernate jpa optimization


    【解决方案1】:

    您应该删除自己的方法计数器并创建特定(业务)查询以检索列表的大小,例如

    public long getCommentsCount(Company c) {
        String query = "SELECT COUNT(cm) FROM Company AS c JOIN c.comments AS cm WHERE c = :company";
        return entityManager.createQuery(q, Long.class).setParameter("company", c).getSingleResult();
    }
    

    当此类查询作为@NamedQuery 加载到实体上时,或使用 CriteriaQuery API 时,某些持久性提供程序可能会优化性能。 根据您的数据库,您可能需要将返回类更改为 Number.class 并转换为 long。

    如果您想进一步调整性能,请使用 createNativeQuery 方法并编写您自己的纯 SQL,但请记住,对 db 架构的更改需要查看这些查询。

    【讨论】:

      【解决方案2】:

      我找到了答案。如果我们不调整获取实体的集合大小,休眠会加载每条评论。我们可以通过两种方式解决这个性能问题。

      我们可以使用@LazyCollection(LazyCollectionOption.EXTRA),如下所示。 LazyCollectionOption.EXTRA .size().contains() 不会初始化整个集合。

      @OneToMany(fetch = FetchType.LAZY)
      @LazyCollection(LazyCollectionOption.EXTRA)
      @JoinTable(name="companies_comments",
              joinColumns=@JoinColumn(name="company_id"),
              inverseJoinColumns=@JoinColumn(name="comment_id"))
      private Set<Comment> comments = new HashSet<>();
      

      或者我们可以使用@Formula注解。

      @Formula(SELECT COUNT(*) FROM companies_comments cc WHERE cc.company_id = id)
      private int numberOfComments;
      

      8 个月后编辑:为了简单和性能,我们应该创建一个如下所示的 JPA 查询方法。

      @Repository
      public interface CommentRepository extends JpaRepository<Comment, Long> {
      
          int countAllByCompany(Company company);
      }
      

      我们不应该为此使用getComments().size(),因为这样所有的 cmets 都会加载到内存中,这可能会导致性能问题。

      将 cmets 添加到集合时也是如此。我们不应该使用getComments().add(newComment)。当我们有OneToMany关系时,我们只需将评论的公司字段设置为newComment.setCompany(company),并执行persist操作。因此,建议定义OneToMany双向关系。

      【讨论】:

      • 您的答案都使用了休眠的专有扩展,但我想这是您的初衷。请注意,第二个是用原生 SQL 编写的,在实体修改的情况下,与使用原生查询相同。
      • 是的,第二个是用原生 SQL 编写的,谢谢补充 :)
      猜你喜欢
      • 2012-04-07
      • 2022-11-19
      • 2011-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多