인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

신동민님의 프로필 이미지

작성한 질문수

jooq로 auditing 구현하는방법

해결된 질문

작성

·

101

·

수정됨

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 되는데 작성자, 작성일이 안들어가고 있네요. 찍어놓은 프린트도 호출이 안되구요..혹시 왜그런지 알 수 있을까요..?

답변 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를 참조하는 것도 좋은 방법입니다.

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