소개
안녕하세요. 지식공유자 설동민입니다.
현재 카카오 백엔드 개발자로 근무하고 있으며, 복잡한 문제를 명쾌하게 풀어내는걸 좋아합니다.
경영학부 비전공자 출신으로, 다양한 OpenSource들에 대한 분석과 여러 기술적인 경험들을 통해
웹의 전반적인 기술을 학습하였습니다. OOP와 SQL, 이 어울리지 않는 두가지를 제일 좋아합니다.
Github: https://github.com/SightStudio
이력
2021. 12 ~ 현재 : 카카오 백엔드 개발
2020.12 ~ 2021.12: 전) 이스트소프트 백엔드 개발
강의
전체 1수강평
- 실전 jOOQ! Type Safe SQL with Java
- 실전 jOOQ! Type Safe SQL with Java
- 실전 jOOQ! Type Safe SQL with Java
- 실전 jOOQ! Type Safe SQL with Java
게시글
질문&답변
2024.10.25
FilmWithActor로 fetch 시 select와 생성자의 매개변수 순서가 다를 경우 map이 정상적으로 이루어 지지않는 케이스
하디님 안녕하세요 강의에서는 일부 row를 그룹핑해서 다른 객체로 만들 수 있다는걸 보여주기 위해서 위의 방식으로 소개했었는데요. 테이블 자체를 select하는 경우에는 Tables as SelectField 방식으로 처리하는것을 추천드립니다. 🙂 테이블 객체를 field로 사용하는 경우 아래와 같이 mapping 할 때 테이블 명으로 매핑할 클래스 필드명을 찾아서 매칭해주기 때문에 순서를 고려하지 않으셔도 됩니다. 예시 1) 위의 "Table as SelectField"를 사용한 경우 (사진) 예시 2) 강의의 DSL.row를 사용한 경우 (사진) 강의오픈 후에 이부분 설명이 아쉽다고 생각해서 강의노트에 추가했었는데요.이부분은 추후에 다른분들도 어떻게 잘 알 수 있을지 고민해보도록 하겠습니다. 테이블이 아닌경우 DSL.row()와 매퍼를 적절히 사용해주시면 됩니다.dslContext.select( DSL.row( FILM.FILM_ID, FILM.TITLE, FILM.DESCRIPTION ).mapping(SimpleFilmInfo::new) ) .from(FILM) .where(FILM.FILM_ID.eq(id)) .fetchOneInto(SimpleFilmInfoWrapper.class); // class SimpleFilmInfoWrapper { private SimpleFilmInfo simpleFilmInfo; }
- 1
- 1
- 53
질문&답변
2024.09.07
from절 subquery table filed nullable 처리
안녕하세요. 어떤상황인지 정확히 파악이 안되서 답변드리기가 어려운데요.관련된 예시 코드나 사진이 있을까요?
- 1
- 1
- 80
질문&답변
2024.09.03
kotlin mapping error
최종 정리본 안녕하세요. 이부분 조금 이해가 안가서 jOOQ 쪽에 이슈를 올렸었는데요.창시자 답변을 받아서 공유합니다. https://github.com/jOOQ/jOOQ/issues/17157 원인은 DefaultRecordMapper의 아래 두 옵션때문에 다르게 동작하는것으로 확인되었습니다.자바: isMapConstructorParameterNames (기본값: false)코틀린: isMapConstructorParameterNamesInKotlin (기본값: true) 해당 옵션은 생성자가 존재 할 경우 생성자의 변수명을 리플랙션으로 읽어서 sql의 alias와 매핑해주는 옵션입니다. 자바의 default 값이 false인 이유는 해당 옵션이 자바 8 이후 도입되어서 하위 의존성을 위해 false로 되어있기 때문에 그렇습니다. 다만 implicit path join 과 explicit path join은 이렇게되면 sql의 alias가 "alias_xxxx" 형식으로 나타나기 때문에 매핑이 안되고 에러가 발생하는 것이였습니다. (생성되는 SQL에서 확인 할 수 있습니다.) isMapConstructorParameterNamesInKotlin 옵션을 false로 처리하면 자바처럼 동작하겠지만,이 옵션을 켤지말지에 대한 판단이 필요합니다. (사실 코틀린이라면 이 옵션을 키는게 좋다고 생각합니다. 필드 선언부가 생성자와 같이 있기 때문에, 개인적인 의견입니다.) @Configuration class JooqConfig { @Bean fun jooqDefaultConfigurationCustomizer(): DefaultConfigurationCustomizer { return DefaultConfigurationCustomizer { c -> c.settings() // where 절 없이 delete, update 실행 금지 .withExecuteDeleteWithoutWhere(ExecuteWithoutWhere.THROW) .withExecuteUpdateWithoutWhere(ExecuteWithoutWhere.THROW) // -- 이부분 .withMapConstructorParameterNamesInKotlin(false) // implicit join 사용 금지 .withRenderImplicitJoinType(RenderImplicitJoinType.THROW) // schema 미포함 .withRenderSchema(false) } } } 만약 옵션을 키고 Explicit Path Join을 사용하고 싶다면 현재는 아래처럼 Path에 강제로 alias를 주시면 됩니다.fun findFilmWithActorsListLast(): List { val FILM_ACTOR = FILM.filmActor.`as`("filmActor") val ACTOR = FILM.filmActor.actor.`as`("actor") return dslContext .select( FILM, FILM_ACTOR, ACTOR, ).from(FILM) .join(FILM_ACTOR) .join(ACTOR) .fetchInto(FilmWithActor::class.java) } 창시자도 이부분이 애매한것같아 alias를 주지 않아도 되도록 개선해보겠다고 하니 지켜보면 될듯합니다. 감사합니다. (저도 이런 세팅이 존재하는지 몰랐네요 ㅎㅎ)https://github.com/jOOQ/jOOQ/issues/17170
- 0
- 4
- 289
질문&답변
2024.09.01
R2DBC 관련해서 질문 드립니다.
안녕하세요. 인프런에 jOOQ 강의를 출시한 강사입니다.다른분들 질문 답변드리다가 이 질문이 보여서 슬쩍... 의견드리고 갑니다. 본 강의 강사님의 생각이 다르실 수도 있지만. 제가 생각하기엔jOOQ 자체가 생각보다 잘 알려지지 않은 기술이기 때문에 강의의 내용과 동떨어 질 수 있음jOOQ는 다른 데이터베이스 접근 기술보다 초기 세팅비용이 큼 (마찬가지로 이때문에 강의의 내용과 멀어질 수 있음) jOOQ도 R2DBC 환경에서 기존 기능의 100%를 지원하지 못함 (이 질문에서도 확인 할 수 있습니다.)이런 이유 때문에 사용 안하셨을것이라고 조심스럽게 의견드려봅니다. 감사합니다 🙂
- 0
- 3
- 167
질문&답변
2024.08.11
kotlin mapping error
안녕하세요. 디버깅을 좀 해보니 이야기가 길어질것같아 답변먼저 작성하고 설명은 밑에 하도록 하겠습니다. 테이블의 모든 필드를 가져오려면 알려주신 방법처럼 테이블 인스턴스를 인자로 주는게 제일 나아보입니다. fun findFilmWithActorList(page: Long, size: Long): List { val FILM_ACTOR = JFilmActor.FILM_ACTOR val ACTOR = JActor.ACTOR return dslContext .select( FILM, FILM_ACTOR, ACTOR ) .from(FILM) .join(FILM_ACTOR).on(FILM.FILM_ID.eq(FILM_ACTOR.FILM_ID)) .join(ACTOR).on(ACTOR.ACTOR_ID.eq(FILM_ACTOR.ACTOR_ID)) .offset((page - 1) * size) .limit(size) .fetchInto(FilmWithActor::class.java) } 여기서 만약, SELECT한 필드중 일부를 객체로 매핑하려면 아래처럼 하시면 됩니다. data class FilmWithActorName(val film: Film, val filmActor: FilmActor, val actorName: ActorName) data class ActorName(val firstName: String?, val lastName: String?) fun findFilmWithActorsListLast( page: Long, pageSize: Long ): List { val FILM_ACTOR = JFilmActor.FILM_ACTOR val ACTOR = JActor.ACTOR return dslContext .select( FILM, FILM_ACTOR, row( ACTOR.FIRST_NAME, ACTOR.LAST_NAME ).mapping(::ActorName).`as`("actor_name") // 문자열이 부담된다면 .`as`(ActorName::class.simpleName) 로도 가능 ).from(FILM) .join(FILM_ACTOR).on(FILM.FILM_ID.eq(FILM_ACTOR.FILM_ID)) .join(ACTOR).on(FILM_ACTOR.ACTOR_ID.eq(ACTOR.ACTOR_ID)) .limit(pageSize) .offset((page - 1) * pageSize) .fetchInto(FilmWithActorName::class.java) } 해설 테이블 인스턴스를 인자로 넘기는 것과 DSL.row(*TABLE.fields()) 를 인자로 넘기는 것의 차이는 테이블로부터 생성된 레코드인지 아니면 커스텀하게 생성된 레코드 인지 여부입니다. 테이블 객체를 넘기면 clllickme 님이 올려주신 fetch 후 map 하는 작업을 내부적으로 진행합니다. 다만, 코틀린에서는 커스텀하게 생성된 레코드들에 대해서 매칭을 하지 못해 내부적으로 null을 할당하다 에러가 발생한 것으로 보입니다.(자세한 동작을 보고싶다면. jOOQ 내부의 DefaultRecordMapper의 ImmutablePOJOMapper를 보시면 됩니다.) 그래도 이부분은 크게 문제 없어보입니다. 테이블 컬럼 전체를 select 하는게 아니면 보통 제가 올린 답변의 예제처럼 .mapping으로 매핑 할 객체를 명시하기 때문에 답변대로 진행하면 큰 문제 없어보입니다. (사진)(사진)
- 0
- 4
- 289