inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

jooq로 auditing 구현하는방법

해결된 질문

206

신동민

작성한 질문수 1

1

jpa, querydsl 을 완전 걷어내고 jooq 만 사용해보려 하는중인데, 아무래도 모든테이블에 "작성일, 작성자, 수정일, 수정자" 정보가 들어갈거 같아 auditing 기능을 구현 하려 하는중입니다.

프로페셔널 상품을 사용하기는 어려운 상황이고, RecordListener 를 이용하여 구현해보려 하는데 리스너가 제대로 등록이 되질 않고 있네요 ㅠㅠ

@Repository
class UserRepository(
    private var dslContext: DSLContext,
) {
    init {
        val config = dslContext.configuration().derive()
        config.set(userRecordAuditListener())
        dslContext = DSL.using(config)
    }

    private fun userRecordAuditListener(): RecordListener {
        val createAudit: (UsersRecord) -> Unit = {
            it.createdAt = LocalDateTime.now()
            it.createdBy = 1
        }
        val updateAudit: (UsersRecord) -> Unit = {
            it.updatedAt = LocalDateTime.now()
            it.updatedBy = 2
        }
        return RecordAuditListenerGenerator<UsersRecord>().generate(
            UsersRecord::class.java,
            createAudit,
            updateAudit,
        )
    }

    fun insertUser(user: UsersRecord): Long? {
        return dslContext
            .insertInto(
                USERS,
                USERS.NAME,
                USERS.AGE,
            ).values(
                user.name,
                user.age
            ).returningResult(USERS.ID)
            .fetchOneInto(Long::class.java)
    }
}

class RecordAuditListenerGenerator<R : UpdatableRecordImpl<R>>(
) {

    fun generate(
        recordClass: Class<R>,
        insertStart: (R) -> Unit,
        updateStart: (R) -> Unit,
    ): RecordListener {
        return object : RecordListener {
            override fun insertStart(ctx: RecordContext) {
                println("============== 1 ==============")
                insertStart(recordClass.cast(ctx.record()) as R)
            }

            override fun updateStart(ctx: RecordContext) {
                println("============= 2 ===============")
                updateStart(recordClass.cast(ctx.record()) as R)
            }
        }
    }
}

 

데이터는 insert 되는데 작성자, 작성일이 안들어가고 있네요. 찍어놓은 프린트도 호출이 안되구요..혹시 왜그런지 알 수 있을까요..?

kotlin springboot jooq

답변 2

2

설동민

안녕하세요. 질문이 올라왔는지 몰랐네요 ㅎㅎ

제 메일로도 받았던 질문인데 기록차 인프런에도 올려달라고 요청드렸던 글입니다.

 

jOOQ RecordListener는 ActiveRecord 기반으로 동작합니다.

아래의 코드는 ActiveRecord 기반으로 동작하지 않기때문에 이벤트 리스너가 트리거되지 않습니다.

 

    // RecordListener가 트리거되지 않음
    fun insertUser(user: UsersRecord): Long? {
        return dslContext
            .insertInto(
                USERS,
                USERS.NAME,
                USERS.AGE,
            ).values(
                user.name,
                user.age
            ).returningResult(USERS.ID)
            .fetchOneInto(Long::class.java)
    }
    
    // RecordListener가 트리거됨 
    fun insertUser_record(user: UsersRecord): Long? {
       user.insert();
       return user.getId();
    }

 

JPA처럼 영속성 컨텍스트 내에서 insert or update가 되는 형태를 원하신다면 
ActiveRecord 내부에 DB 연동 상태가 있는데요. 이를 통해 처리 할 수 있습니다.

 

JPA의 경우 엔티티 매니저가 영속성 컨텍스트 내에서 이 insert or update의 라이프사이클을 관리하지만

jOOQ는 영속성 컨텍스트 개념이 없기 때문에 ActiveRecord 객체 단위로 라이프사이클을 관리합니다.

 

이부분을 유의해서 적용 부탁드립니다.

이 부분 관련 질문이 몇번 들어와서 강의에 추가해놔야겠네요. 감사합니다 ~

0

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴입니다.
신동민님, jOOQ와 관련한 문제를 해결하는 데 도움을 드리겠습니다.

제공해주신 코드를 보면, RecordListener를 통한 auditing 기능을 구현하려고 하시는 것 같습니다. 문제의 원인은 RecordListenerConfiguration에 제대로 등록되지 않아서 발생하는 것으로 보입니다.

  1. RecordListener 등록 위치 검토:
    Configuration 객체에 RecordListener를 설정하는 부분이 있습니다. 이를 init 블록에서 설정하고 있지만, 해당 dslContext가 이후에 다른 곳에서 새로 생성하거나 덮어쓰지 않았는지 확인해 보세요.

  2. DSLContext 생성 후 바로 사용 검사:
    만약 DSLContext가 다른 곳에서 다시 생성된다면 초기 설정이 유실될 수 있습니다. DSLContext가 언제 어디서 생성되는지를 명확히 하고, 리스너가 항상 포함되도록 처리해야 합니다.

  3. 프린트 호출 검사:
    프린트가 호출되지 않는다면 리스너가 작동하지 않는 것입니다. 이는 위의 문제와 연관이 있을 수 있으니, 리스너의 등록 과정을 다시 한번 확인하시고, 모든 설정 단계가 예상대로 진행되는지를 디버깅해 보세요.

추가적인 확인 사항이 있거나, 비슷한 경우를 확인하고 싶으시면 공식 jOOQ documentation를 참조하는 것도 좋은 방법입니다.

도움이 되었기를 바랍니다. 다른 질문이 있으면 언제든지 문의해 주세요.

다양한 관점의 코드 경험을 위해 개선하지 않은 코드

1

47

1

강의가 검은 화면으로 나옵니다.

0

48

1

histories() 응답에 PointHistory.id를 포함한 이유가 궁금합니다/

1

43

2

DI시 eager과 lazy

0

36

2

타입스크립트

0

46

1

actuator에서 health 외의 다른 URL은 404 Notfound 에러가 발생합니다.

0

46

2

SettlementTargetRepository Jquery 질문

1

46

2

부가 기능을 이벤트 핸들러로 분리하는 기준이 있을까요?

1

59

2

주사위앱 소개 및 레이아웃 설정 문제

0

59

2

엔티티의 pk 를 0으로 초기화하시는 이유가 있을까요??

1

65

2

제미니님 안녕하세요!

1

72

2

MCP 연동 인텔리제이 2.6.1

0

116

2

개념 간 격벽 분리와 목록 조회 시 발생하는 참조 구조

1

79

2

무중단 배포 / 컨테이너 이미지 관리에 주로 어떤 인프라를 활용하시나요?

0

66

1

수업노트

0

53

1

프로덕트와 프로덕트카테고리 사이의 삭제 정책

1

74

2

MCP 연동관련

0

130

4

클로드 개발자 구성편집 설정시 문제점

0

95

2

2025.3.3 인텔리제이 버전에서 MCP설정하는 방법

0

333

2

service 를 interface 로 두는 이유

0

71

1

강의 오류

0

52

2

KType 관련 Kotlin 2.3 변경점

1

61

2

Kotlin 2.0(K2 컴파일러)에서 달라진 Java SAM 변환 동작

2

98

3

이벤트 발행이 불필요한 것은 어떻게 구분하나요?

0

55

2