Выберите верхние N строк, первую максимальную строку из каждой группы, отличной на один столбец (Spring, Hibernate, JPQL) - PullRequest
0 голосов
/ 09 июня 2018

Объекты:

  • Оценка [id, user_id, значение]

  • Пользователь [id, имя]

Я хочу загрузить 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

1 Ответ

0 голосов
/ 09 июня 2018

Easy peasy!

Теперь ваш запрос выбирает результаты и присоединяется только к некоторым пользователям, но на самом деле не присоединяется к ассоциациям Score.users или user.score.Если вы хотите загрузить как Score, так и связанных пользователей, вам нужно что-то вроде этого:

Как я объяснил в этой статье , вы можете использовать для этого функции окна. MySQL 8 поддерживает функции Windows , поэтому просто выполните следующий запрос 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 - это волшебная палочка в вашем наборе инструментов уровня доступа к данным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...