【问题标题】:Select top N rows, first max row from each group distinct by one column (Spring,Hibernate,JPQL)选择前 N 行,每个组中的第一个最大行由一列不同(Spring,Hibernate,JPQL)
【发布时间】:2018-11-19 05:59:22
【问题描述】:

实体:

  • 分数 [id, user_id, value]

  • 用户 [id, name]

我想快速加载加入用户的前 10 个不同分数(按 user_id)

本机查询有效,但休眠无法将用户映射到得分(再次延迟加载),

我尝试编写 JPQL 查询,但它生成了错误的查询

工作 natie 查询:

select * 
from scores 
join users 
    on scores.user_id = users.id and 
    user_id in (
        select distinct user_id 
        from scores order by value desc
    ) 
 order by value desc limit 10

【问题讨论】:

    标签: mysql spring hibernate jpa jpql


    【解决方案1】:

    您的查询选择了分数并且只加入了一些用户,但并没有真正加入获取Score.usersuser.score 关联。如果你想同时加载 Score 和关联的用户,你需要这样:

    您可以使用自 MySQL 8 起受支持的Window Functions 并运行以下 SQL 查询:

    List<Score> scores = entityManager.createNativeQuery(
        "select u_s_r.* " +
        "from (   " +
        "    select *, dense_rank() OVER (ORDER BY value DESC) rank " +
        "    from (   " +
        "        select s.*, u.* " +
        "        from scores s  " +
        "        join users u s.user_id = u.id  " +
        "        order by u.id " +
        "    ) u_s " +
        ") u_s_r " +
        "where u_s_r.rank <= :rank", Score.class)
    .setParameter("rank", 10)
    .unwrap( NativeQuery.class )
    .addEntity( "s", Score.class )
    .addEntity( "u", User.class )
    .setResultTransformer( DistinctScoreResultTransformer.INSTANCE )
    .getResultList();
    

    DistinctScoreResultTransformer 可以如下所示:

    public class DistinctScoreResultTransformer 
            extends BasicTransformerAdapter {
     
        private static final DistinctScoreResultTransformer INSTANCE  = 
                new DistinctScoreResultTransformer();
     
        @Override
        public List transformList(List list) {
            Map<Serializable, Identifiable> identifiableMap = 
                    new LinkedHashMap<>( list.size() );
     
            for ( Object entityArray : list ) {
                if ( Object[].class.isAssignableFrom( 
                        entityArray.getClass() ) ) {
                    Score score = null;
                    User user = null;
     
                    Object[] tuples = (Object[]) entityArray;
     
                    for ( Object tuple : tuples ) {
                        if(tuple instanceof Score) {
                            score = (Score) tuple;
                        }
                        else if(tuple instanceof User) {
                            user = (User) tuple;
                        }
                        else {
                            throw new UnsupportedOperationException(
                                "Tuple " + tuple.getClass() + " is not supported!"
                            );
                        }
                    }
                    Objects.requireNonNull(score);
                    Objects.requireNonNull(user);
     
                    if ( !identifiableMap.containsKey( score.getId() ) ) {
                        identifiableMap.put( score.getId(), score );
                        score.setUsers( new ArrayList<>() );
                    }
                    score.addUser( user );
                }
            }
            return new ArrayList<>( identifiableMap.values() );
        }
    }
    

    如果您想知道这是否可以通过 JPQL 完成,那么您应该知道这是不可能的。但是,您不必通过 JPQL 运行每个查询。 SQL 随时为您提供帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-02
      • 2017-07-24
      相关资源
      最近更新 更多