ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Querydsl cross join 개선 하기
    Spring Framework 2022. 6. 18. 19:48
    반응형

    1) 서론

    회사에서 Spring Data JPA는 서버 개발의 공통 기술 스택입니다. 그리고 당연하게 where 절을 많이 사용하는데요. 이때 만난 문제점을 개선하고 이를 공유하고자 합니다.

     

    Querydsl 뿐만 아니라, Spring Data JPA를 사용해보셨다면 이미 알고 계신 내용일 수도 있는데요. 그런 분들은 이번 글은 skip 하셔도 좋을 것 같습니다.


    2) Querydsl where절의 문제점

    ORM 프레임워크 상 혹은 SQL 쿼리를 직접 사용할 때 where절은 숨 쉬듯 자연스럽게 사용됩니다. 하지만 이러한 where절이 문제점을 일으킬 것이라고는 생각하지 못했었는데요.

     

    어떤 문제가 있을까요?

    @Table(name = "book", schema = "test")
    @Entity
    class Book: CommonTime() {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var idx: Long? = null
    
        @Column(name = "name", nullable = false, columnDefinition = "varchar(50)")
        var name: String? = null
    
        @Column(name = "price", nullable = false, columnDefinition = "decimal")
        var price: BigDecimal? = null
    
        @Cascade(CascadeType.PERSIST)
        @OneToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "author_idx", nullable = false, foreignKey = ForeignKey(name = "FK_book_author"))
        var author: Author? = null
    
        @Cascade(CascadeType.PERSIST)
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "publisher_idx", nullable = false, foreignKey = ForeignKey(name = "FK_book_publisher"))
        var publisher: Publisher? = null
    
    }
        override fun findByAuthor(authorName: String): Book {
            return jpaQueryFactory.selectFrom(book)
                // 책의 저자의 이름과 같은것을 조회
                .where(book.author.name.eq(authorName))
                .fetchFirst()
        }
    • Book 테이블을 조회합니다
    • authorName(저자 이름)과 같은 author(저자)의 book(책)을 찾습니다.
    • 조건 검색을 위해 where문을 사용합니다.

    • cross join 발생합니다.

     

    where 절을 사용했을 때 join절이 발생하는 이유는 명확합니다.

     

    Book 객체가 Author 객체를 참조하고 있습니다. 하지만 이는 단순히 객체의 연관 관계일 뿐입니다. Author 테이블의 실제 데이터는 SQL 쿼리를 발생시켜서 조회해야 합니다. 그리고 다른 테이블을 조회하는 방법은 join절을 사용해야 합니다.

     

    위와 같은 이유로 join문이 발생하는 것 자체는 매우 자연스럽습니다.

    2. 1) 꼭 cross join 이어야만 하는가?

    cross join은 inner join에 비해 당연히 성능상 불리할 수밖에 없는데요. 아래의 사진과 같이 조회 대상의 테이블의 모든 경우의 수대로 조회합니다. 

    https://www.educba.com/joins-in-mysql/

    그래서 특정 데이터만을 조회하려고 할 때는 XX join 절을 명시적으로 사용하는 것이 좋은데요.

     

    이 문제는 Querydsl뿐만 아니라, JPA 스펙에 의해 hibernate where절 자체에 대한 문제로 보입니다.

    https://github.com/querydsl/querydsl/issues/2182#issuecomment-707867112

     

    즉 JPA 스펙 상 where 절은 기본으로 cross join을 사용합니다.


    3) 그래서 어떻게 개선할 수 있을까요?

    사실 정답은 간단한데요. 명시적으로 inner join을 사용하면 됩니다.

    어찌 보면 명시적으로 자바 코드를 이용해서 쿼리를 사용할 수 있는 querydsl의 장점일 수도 있습니다.

        override fun findByAuthor(authorName: String): Book {
            return jpaQueryFactory.selectFrom(book)
                // 명시적으로 inner join 활용
                .innerJoin(author).on(author.eq(book.author))
                .where(author.name.eq(authorName))
                .fetchFirst()
        }
    • inner join을 명시적으로 사용해서 author 객체를 조회합니다.

    • cross join이 아닌, inner join이 발생합니다.

    4) 결론

    객체와 관계형 데이터베이스를 잘 맵핑시켜주는 ORM은 매우 편리합니다. SQL 쿼리를 코드를 이용해서 정적 타입으로 사용할 수 있기 때문입니다.

     

    하지만 편리함에 빠져 막연하게 사용하다 보면, 위와 같이 의도하지 않은 대로 쿼리가 발생할 수 있습니다. 이는 성능상의 문제점과 더불어 잘못된 조회 결과를 응답할 수 있습니다.

     

    이번 계기로 JPA를 사용했을 때 실제 의도한 대로 쿼리가 발생하는지, 적용 전 철저한 테스트 및 모니터링이 필요하다는 생각을 합니다.

    반응형

    댓글

Designed by Tistory.