Backend/kotlin

kotlin jdsl에서 ElementCollection 컬럼 조건 추가하기

keepbang 2024. 12. 20. 13:02

코프링으로 개발중에  ElementCollection으로 된 컬럼을 조건으로 조회하는 기능을 개발하던중 여러 여러가지 안되는 상황이 있어서 기록하고자 글을 남긴다...

 

해결방법은 kotlin-jdsl github 이슈에서 찾았다!!

 

exists subquery 문의 · Issue #774 · line/kotlin-jdsl

안녕하세요. exists subquery에 대해 문의드립니다. Entity 구조가 아래와 같을 때, @Entity class Notification( @Id val id: Long, @Enumerated(EnumType.STRING) @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name ...

github.com

 

 

엔티티 구조

일단 엔티티 내부에 enum list가 들어가야하는 상황이 있어서 어떻게 넣을까 생각하다가 ElementCollection을 사용하기로했다.

class Entity(
	enumTypes: Set<EnumType>,
    // 이하 생략
) {

....
 
    @ElementCollection(targetClass = EnumType::class)
    @CollectionTable(name = "enum_types", joinColumns = [JoinColumn(name = "entity_id")])
    @Column(name = "enum_type", nullable = false)
    @Enumerated(EnumType.STRING)
    var _EnumTypes : MutableSet<EnumType> = enumTypes.toMutableSet()
        protected set
....

}

 

이런식으로 enum 목록이 담긴 컬럼을 만들었다. 이렇게하면 테이블이 2개가 생기게 된다.

 

repository의 jdsl쿼리는 아래처럼 작성했다.

 

override fun findByEntity(query: FindQuery): Page<Entity> {
        return queryFactory.pageQuery(query.getPageable()) {
            selectFrom(Entity::class)
            where(
                and(
                inFilter(query.enumType, Entity::enumTypes)
                )
            )
        }
    }
    
 inline fun <reified T, R> SpringDataPageableQueryDsl<T>.inFilter(
    value: R?,
    property: KProperty1<T, R?>,
): PredicateSpec? {
    return value?.let { col(property).`in`(it) }
}

 

inFilter라는 확장함수를 작성해서 만들어봤다.

발생한 에러

org.springframework.dao.InvalidDataAccessResourceUsageException: Multi valued paths are only allowed for the member of operator

 

 

대충 접근할수없는 경로라서 에러가 발생하게 된다.

 

분명이거는 다른사람도 발생했을것이라 생각하고 kotlin-jdsl의 이슈를 찾아보니 역시나 있었다!

 

변경된 코드

return queryFactory.pageQuery(query.getPageable()) {
    select(entity(Entity::class))
    from(entity(Entity::class))
    join(Entity::enumTypes)
    where(
        and(
            query.enumType?.let { 
                entity(EnumType::class).`in`(it)
            }
        )
    )
}

 

join을 사용해서 해당 필드에 조인이 걸리게 작성하고 조건절에 Enum class를 넣어서 단수로 조건절을 적용했다.

 

저부분을 확장함수로 변경해서 사용하기 편하게 만드는건 나중에 따로 해봐야할것같다...