코딩하는 털보

21.11.25 TIL 본문

Diary/Today I Learned

21.11.25 TIL

이정인 2021. 11. 26. 02:14

오늘의 삽질

org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list

QueryDSL 쿼리 프로젝션을 사용해서 Entity가 아닌 특정 클래스에 SQL로 조회한 데이터를 담으면서 발생한 에러이다.

    @Override
    public List<RecommendationVo> searchByNumber(User login, int number) {
        return queryFactory
                .select(new QRecommendationVo(recommendation.type,
                        new QFollowVo(
                                user.username,
                                monsterDatabase.id,
                                monsterDatabase.imageUrl,
                                user.monsterCode,
                                new CaseBuilder()
                                        .when(follow.id.isNull())
                                        .then(Boolean.FALSE)
                                        .otherwise(Boolean.TRUE))
                        ))
                .from(recommendation).fetchJoin()
                .join(recommendation.user, user).fetchJoin()
                .join(user.monster, monster).fetchJoin()
                .join(monster.monsterDatabase, monsterDatabase)
                .leftJoin(follow)
                    .on(user.eq(follow.following)
                            .and(follow.follower.eq(login))
                    )
                .where(recommendation.number.eq(number))
                .fetch();
    }

RecommendationVo에 바로 담을 것이기 때문에 엔티티는 사용할 수 없다. 즉, 이 쿼리는 엔티티 그래프를 참조할 수 없는 쿼리이므로 fetch Join을 제거하고 순수한 join으로 변경하여 해결했다.

참고 : https://www.inflearn.com/questions/23847

쿼리 튜닝

추천 사용자 조회 쿼리 튜닝

HIBERNATE:

SELECT recommenda0_.id      AS id1_12_0_,
       user1_.id            AS id1_14_1_,
       recommenda0_.number  AS number2_12_0_,
       recommenda0_.type    AS type3_12_0_,
       recommenda0_.user_id AS user_id4_12_0_,
       user1_.created_at    AS created_2_14_1_,
       user1_.disabled      AS disabled3_14_1_,
       user1_.email         AS email4_14_1_,
       user1_.monster_id    AS monster_9_14_1_,
       user1_.monster_code  AS monster_5_14_1_,
       user1_.provider_type AS provider6_14_1_,
       user1_.social_id     AS social_i7_14_1_,
       user1_.username      AS username8_14_1_
FROM   recommendation recommenda0_
       INNER JOIN USER user1_
               ON recommenda0_.user_id = user1_.id
WHERE  recommenda0_.number = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ? 

조치사항

  • ASIS (N+1)
  • 인증 관련 컬럼 유니크 제약조건 (social_id)
  • QeuryDSL Join, projection
  • 인덱스 추가 Recommendation(number)
SELECT Cast(recommenda0_.type AS CHAR) AS col_0_0_,
       user1_.username                 AS col_1_0_,
       monsterdat3_.id                 AS col_2_0_,
       monsterdat3_.image_url          AS col_3_0_,
       user1_.monster_code             AS col_4_0_,
       CASE
         WHEN follow4_.id IS NULL THEN ?
         ELSE 1
       END                             AS col_5_0_
FROM   recommendation recommenda0_
       INNER JOIN USER user1_
               ON recommenda0_.user_id = user1_.id
       INNER JOIN monster monster2_
               ON user1_.monster_id = monster2_.id
       INNER JOIN monster_database monsterdat3_
               ON monster2_.monster_database_id = monsterdat3_.id
       LEFT OUTER JOIN follow follow4_
                    ON ( user1_.id = follow4_.following_id
                         AND follow4_.follower_id = ? )
WHERE  recommenda0_.number = ? 

팔로우 목록 가져오기

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.following_id = user1_.id
WHERE  user1_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_0_,
       user0_.created_at    AS created_2_14_0_,
       user0_.disabled      AS disabled3_14_0_,
       user0_.email         AS email4_14_0_,
       user0_.monster_id    AS monster_9_14_0_,
       user0_.monster_code  AS monster_5_14_0_,
       user0_.provider_type AS provider6_14_0_,
       user0_.social_id     AS social_i7_14_0_,
       user0_.username      AS username8_14_0_
FROM   USER user0_
WHERE  user0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_0_,
       user0_.created_at    AS created_2_14_0_,
       user0_.disabled      AS disabled3_14_0_,
       user0_.email         AS email4_14_0_,
       user0_.monster_id    AS monster_9_14_0_,
       user0_.monster_code  AS monster_5_14_0_,
       user0_.provider_type AS provider6_14_0_,
       user0_.social_id     AS social_i7_14_0_,
       user0_.username      AS username8_14_0_
FROM   USER user0_
WHERE  user0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_0_,
       user0_.created_at    AS created_2_14_0_,
       user0_.disabled      AS disabled3_14_0_,
       user0_.email         AS email4_14_0_,
       user0_.monster_id    AS monster_9_14_0_,
       user0_.monster_code  AS monster_5_14_0_,
       user0_.provider_type AS provider6_14_0_,
       user0_.social_id     AS social_i7_14_0_,
       user0_.username      AS username8_14_0_
FROM   USER user0_
WHERE  user0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ?

HIBERNATE:

SELECT user0_.id            AS id1_14_,
       user0_.created_at    AS created_2_14_,
       user0_.disabled      AS disabled3_14_,
       user0_.email         AS email4_14_,
       user0_.monster_id    AS monster_9_14_,
       user0_.monster_code  AS monster_5_14_,
       user0_.provider_type AS provider6_14_,
       user0_.social_id     AS social_i7_14_,
       user0_.username      AS username8_14_
FROM   USER user0_
WHERE  user0_.monster_code = ?

HIBERNATE:

SELECT follow0_.id           AS id1_1_,
       follow0_.follower_id  AS follower2_1_,
       follow0_.following_id AS followin3_1_
FROM   follow follow0_
       LEFT OUTER JOIN USER user1_
                    ON follow0_.follower_id = user1_.id
       LEFT OUTER JOIN USER user2_
                    ON follow0_.following_id = user2_.id
WHERE  user1_.id = ?
       AND user2_.id = ?

HIBERNATE:

SELECT monster0_.id                  AS id1_6_0_,
       monster0_.created_at          AS created_2_6_0_,
       monster0_.exp_point           AS exp_poin3_6_0_,
       monster0_.level               AS level4_6_0_,
       monster0_.monster_database_id AS monster_6_6_0_,
       monster0_.NAME                AS name5_6_0_
FROM   monster monster0_
WHERE  monster0_.id = ?

HIBERNATE:

SELECT monsterdat0_.id           AS id1_9_0_,
       monsterdat0_.image_url    AS image_ur2_9_0_,
       monsterdat0_.level        AS level3_9_0_,
       monsterdat0_.monster_type AS monster_4_9_0_
FROM   monster_database monsterdat0_
WHERE  monsterdat0_.id = ? 

조치사항

  • ASIS (N+1)
  • 인증 관련 컬럼 유니크 제약조건 (social_id)
  • QeuryDSL Join, projection
  • 인덱스 추가 Follow(follower, following)
SELECT user1_.username        AS col_0_0_,
       monsterdat3_.id        AS col_1_0_,
       monsterdat3_.image_url AS col_2_0_,
       user1_.monster_code    AS col_3_0_,
       CASE
         WHEN follow4_.id IS NULL THEN ?
         ELSE 1
       END                    AS col_4_0_
FROM   follow follow0_
       INNER JOIN USER user1_
               ON follow0_.follower_id = user1_.id
       INNER JOIN monster monster2_
               ON user1_.monster_id = monster2_.id
       INNER JOIN monster_database monsterdat3_
               ON monster2_.monster_database_id = monsterdat3_.id
       LEFT OUTER JOIN follow follow4_
                    ON ( follow4_.follower_id = ?
                         AND follow4_.following_id = user1_.id )
WHERE  follow0_.following_id = ? 
Comments