代码之家  ›  专栏  ›  技术社区  ›  Steve Claridge

使用JPA和Hibernate时,distinct如何工作

  •  44
  • Steve Claridge  · 技术社区  · 15 年前

    distinct在JPA中使用什么列,可以更改它吗?

    下面是一个使用distinct的JPA查询示例:

    select DISTINCT c from Customer c
    

    哪一列没有太大的意义-哪一列是基于不同的?是否因为找不到注释而在实体上指定为注释?

    我想指定要区分的列,如下所示:

    select DISTINCT(c.name) c from Customer c
    

    我在使用MySQL和Hibernate。

    6 回复  |  直到 6 年前
        1
  •  10
  •   Community CDub    8 年前

    最新消息:请看最热门的答案。

    我自己的现在已经过时了。因为历史原因才留在这里。


    在连接中通常需要使用hql中的distinct,而不是在像您自己的示例中。

    也见 How do you create a Distinct query in HQL

        2
  •  55
  •   agelbess    12 年前

    你很亲近。

    select DISTINCT(c.name) from Customer c
    
        3
  •  12
  •   Tomasz    12 年前
    @Entity
    @NamedQuery(name = "Customer.listUniqueNames", 
                query = "SELECT DISTINCT c.name FROM Customer c")
    public class Customer {
            ...
    
            private String name;
    
            public static List<String> listUniqueNames() {
                 return = getEntityManager().createNamedQuery(
                       "Customer.listUniqueNames", String.class)
                       .getResultList();
            }
    }
    
        4
  •  10
  •   Yan Khonski    8 年前

    我同意 崎崎 他的回答对我有帮助。 我想选择整个实体,所以我用

     select DISTINCT(c) from Customer c
    

    在我的例子中,我有多对多的关系,我希望在一个查询中加载带有集合的实体。

    我使用了左连接获取,最后我必须使结果与众不同。

        5
  •  3
  •   Vlad Mihalcea    6 年前

    正如我在中解释的那样 this article ,取决于基础的JPQL或条件API查询类型, DISTINCT 在JPA中有两个含义。

    标量查询

    对于返回标量投影的标量查询,如以下查询:

    List<Integer> publicationYears = entityManager
    .createQuery(
        "select distinct year(p.createdOn) " +
        "from Post p " +
        "order by year(p.createdOn)", Integer.class)
    .getResultList();
    
    LOGGER.info("Publication years: {}", publicationYears);
    

    这个 独特的 关键字应传递给基础SQL语句,因为我们希望DB引擎在返回结果集之前筛选重复项:

    SELECT DISTINCT
        extract(YEAR FROM p.created_on) AS col_0_0_
    FROM
        post p
    ORDER BY
        extract(YEAR FROM p.created_on)
    
    -- Publication years: [2016, 2018]
    

    实体查询

    对于实体查询, 独特的 有不同的含义。

    不使用 独特的 ,类似以下查询:

    List<Post> posts = entityManager
    .createQuery(
        "select p " +
        "from Post p " +
        "left join fetch p.comments " +
        "where p.title = :title", Post.class)
    .setParameter(
        "title", 
        "High-Performance Java Persistence eBook has been released!"
    )
    .getResultList();
    
    LOGGER.info(
        "Fetched the following Post entity identifiers: {}", 
        posts.stream().map(Post::getId).collect(Collectors.toList())
    );
    

    将要加入 post 以及 post_comment 这样的表格:

    SELECT p.id AS id1_0_0_,
           pc.id AS id1_1_1_,
           p.created_on AS created_2_0_0_,
           p.title AS title3_0_0_,
           pc.post_id AS post_id3_1_1_,
           pc.review AS review2_1_1_,
           pc.post_id AS post_id3_1_0__
    FROM   post p
    LEFT OUTER JOIN
           post_comment pc ON p.id=pc.post_id
    WHERE
           p.title='High-Performance Java Persistence eBook has been released!'
    
    -- Fetched the following Post entity identifiers: [1, 1]
    

    但父母 邮递 每个相关联的记录在结果集中重复 后注释 行。因此, List 属于 Post 实体将包含重复的 职务 实体引用。

    消除 职务 实体引用,我们需要使用 独特的 :

    List<Post> posts = entityManager
    .createQuery(
        "select distinct p " +
        "from Post p " +
        "left join fetch p.comments " +
        "where p.title = :title", Post.class)
    .setParameter(
        "title", 
        "High-Performance Java Persistence eBook has been released!"
    )
    .getResultList();
    
    LOGGER.info(
        "Fetched the following Post entity identifiers: {}", 
        posts.stream().map(Post::getId).collect(Collectors.toList())
    );
    

    但是然后 独特的 还传递给SQL查询,这一点也不可取:

    SELECT DISTINCT
           p.id AS id1_0_0_,
           pc.id AS id1_1_1_,
           p.created_on AS created_2_0_0_,
           p.title AS title3_0_0_,
           pc.post_id AS post_id3_1_1_,
           pc.review AS review2_1_1_,
           pc.post_id AS post_id3_1_0__
    FROM   post p
    LEFT OUTER JOIN
           post_comment pc ON p.id=pc.post_id
    WHERE
           p.title='High-Performance Java Persistence eBook has been released!'
    
    -- Fetched the following Post entity identifiers: [1]
    

    旁路 独特的 对于SQL查询,执行计划将执行一个额外的 排序 阶段,由于父子组合总是返回唯一的记录(因为子pk列),因此在不带来任何值的情况下增加了开销:

    Unique  (cost=23.71..23.72 rows=1 width=1068) (actual time=0.131..0.132 rows=2 loops=1)
      ->  Sort  (cost=23.71..23.71 rows=1 width=1068) (actual time=0.131..0.131 rows=2 loops=1)
            Sort Key: p.id, pc.id, p.created_on, pc.post_id, pc.review
            Sort Method: quicksort  Memory: 25kB
            ->  Hash Right Join  (cost=11.76..23.70 rows=1 width=1068) (actual time=0.054..0.058 rows=2 loops=1)
                  Hash Cond: (pc.post_id = p.id)
                  ->  Seq Scan on post_comment pc  (cost=0.00..11.40 rows=140 width=532) (actual time=0.010..0.010 rows=2 loops=1)
                  ->  Hash  (cost=11.75..11.75 rows=1 width=528) (actual time=0.027..0.027 rows=1 loops=1)
                        Buckets: 1024  Batches: 1  Memory Usage: 9kB
                        ->  Seq Scan on post p  (cost=0.00..11.75 rows=1 width=528) (actual time=0.017..0.018 rows=1 loops=1)
                              Filter: ((title)::text = 'High-Performance Java Persistence eBook has been released!'::text)
                              Rows Removed by Filter: 3
    Planning time: 0.227 ms
    Execution time: 0.179 ms
    

    带有提示的实体查询通过

    为了从执行计划中消除排序阶段,我们需要使用 HINT_PASS_DISTINCT_THROUGH JPA查询提示:

    List<Post> posts = entityManager
    .createQuery(
        "select distinct p " +
        "from Post p " +
        "left join fetch p.comments " +
        "where p.title = :title", Post.class)
    .setParameter(
        "title", 
        "High-Performance Java Persistence eBook has been released!"
    )
    .setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH, false)
    .getResultList();
    
    LOGGER.info(
        "Fetched the following Post entity identifiers: {}", 
        posts.stream().map(Post::getId).collect(Collectors.toList())
    );
    

    现在,SQL查询将不包含 独特的 但是 职务 将删除实体引用重复项:

    SELECT
           p.id AS id1_0_0_,
           pc.id AS id1_1_1_,
           p.created_on AS created_2_0_0_,
           p.title AS title3_0_0_,
           pc.post_id AS post_id3_1_1_,
           pc.review AS review2_1_1_,
           pc.post_id AS post_id3_1_0__
    FROM   post p
    LEFT OUTER JOIN
           post_comment pc ON p.id=pc.post_id
    WHERE
           p.title='High-Performance Java Persistence eBook has been released!'
    
    -- Fetched the following Post entity identifiers: [1]
    

    执行计划将确认我们这次不再有额外的排序阶段:

    Hash Right Join  (cost=11.76..23.70 rows=1 width=1068) (actual time=0.066..0.069 rows=2 loops=1)
      Hash Cond: (pc.post_id = p.id)
      ->  Seq Scan on post_comment pc  (cost=0.00..11.40 rows=140 width=532) (actual time=0.011..0.011 rows=2 loops=1)
      ->  Hash  (cost=11.75..11.75 rows=1 width=528) (actual time=0.041..0.041 rows=1 loops=1)
            Buckets: 1024  Batches: 1  Memory Usage: 9kB
            ->  Seq Scan on post p  (cost=0.00..11.75 rows=1 width=528) (actual time=0.036..0.037 rows=1 loops=1)
                  Filter: ((title)::text = 'High-Performance Java Persistence eBook has been released!'::text)
                  Rows Removed by Filter: 3
    Planning time: 1.184 ms
    Execution time: 0.160 ms
    
        6
  •  2
  •   finrod    7 年前

    我将使用JPA的构造函数表达式特性。另见以下答案:

    JPQL Constructor Expression - org.hibernate.hql.ast.QuerySyntaxException:Table is not mapped

    按照问题中的例子,应该是这样的。

    SELECT DISTINCT new com.mypackage.MyNameType(c.name) from Customer c