mysql - 从每组中选择前 N 行,第一个最大行,相差一列(Spring、Hibernate、JPQL)

标签 mysql spring hibernate jpa jpql

实体:

  • 得分 [id, user_id, value]

  • 用户 [id, name]

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

native 查询有效,但 hibernate 无法将用户映射到分数(再次延迟加载),

我尝试编写 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

最佳答案

您的查询选择了 Scores 并且只加入了一些 Users 但并没有真正 JOIN FETCH Score.usersuser.score 关联。如果你想预先加载分数和关联的用户,你需要像这样:

您可以使用 Window Functions ,自 MySQL 8 起受支持并运行以下 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 可以帮助您。

关于mysql - 从每组中选择前 N 行,第一个最大行,相差一列(Spring、Hibernate、JPQL),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50773089/

相关文章:

java - 将Java连接到MySQL数据库

java - StackOverflow 错误设置多对多关系 Spring JPA

java - Hibernate 获取 CollectionOfElements Map 的值

php - Eloquent:根据表关系计算行数

mysql - 在查询的第一行得到错误的结果

MySql FLOAT 数据类型和超过 7 位数的问题

java - 从 Alfresco 集成测试上下文访问 Spring bean

java - Spring:将 XML 配置迁移到 util:map 的注释

java - 我可以订阅 Java 中的 SseEmitter

java - Hibernate:尝试列出表后表变空