Skip to content

Hibernate enhancer NoSuchMethodError 수정#145

Merged
eastshine2741 merged 2 commits into
developfrom
fix/hibernate-enhancer-mapped-superclass-writer
Jul 22, 2026
Merged

Hibernate enhancer NoSuchMethodError 수정#145
eastshine2741 merged 2 commits into
developfrom
fix/hibernate-enhancer-mapped-superclass-writer

Conversation

@eastshine2741

@eastshine2741 eastshine2741 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

이해한 만큼만 주절주절썼는데 저도 잘 모르겠어서,, 궁금하신분은 이 description을 클로드에 넣고 이해하시는게 좋으실수도요..

문제

java.lang.NoSuchMethodError: com.wafflestudio.snuttev.core.common.model.BaseEntity.$$_hibernate_write_id(java.lang.Long)
    at com.wafflestudio.snuttev.core.domain.tag.model.TagGroup.$$_hibernate_read_id(TagGroup.kt)
    at com.wafflestudio.snuttev.core.common.model.BaseEntity.getId(BaseEntity.kt:16)

원인

요약: Hibernate 플러그인 버그임

  • native image 전환(OCI 마이그레이션 + Spring Boot 4.0 #134 ) 에서 추가된 org.hibernate.orm bytecode enhancement 플러그인(7.2.4.Final)에 버그가 있음
  • bytecode enhancement란?
    • = 엔티티의 바이트코드 변조.
    • hibernate는 lazy loading, dirty checking 등의 기능을 구현하기 위해 보통은 runtime에 proxy 클래스의 바이트코드를 만들어내서 사용함(이하 runtime proxy)
    • 그러나 OCI 마이그레이션 + Spring Boot 4.0 #134 에서 native image로 전환하다보니, 이 runtime proxy를 만드는 게 불가능했음
      • runtime에 인터프리터도 뭣도 없으므로, 만들어낸 바이트코드를 써먹을수가 없기 때문
    • 그래서 컴파일타임에 미리 bytecode를 조작해놔야 함. 그 방법을 bytecode enhancement라고 한다.
      • org.hibernate.orm 플러그인을 적용하고, enhancement {} 블록을 두는 식으로 적용할 수 있다. 우리도 그렇게 되어있다
  • 그러나 이 bytecode enhancement의 결과물에 모순이 있었음
    • bytecode enhancement는 엔티티를 찾아서 필드마다 $$hibernate_read{필드명}, $$hibernate_write{필드명} 이런 함수를 만듦.
      • 만일 해당 필드가 불변이라면(i.e. java 용어로는 final이라면, kotlin 용어로는 val이라면) $$_hibernate_write{필드명} 함수는 생성하지 않는다
    • 해당 필드에 접근하는 코드가 실행되면, 저 괴상한 함수들이 실행되는 것.
    • 예를들어 TagGroup.$$_hibernate_read_id 의 구현은 이렇게 생겼다고 한다(클로드피셜)
    // TagGroup에 생성된 코드 (enhancer가 만든 것)    
    public Long $$_hibernate_read_id() {              
        if ($$_hibernate_getInterceptor() != null) {  
       // Hibernate가 관리 중인 엔티티인가?           
            Long loaded = (Long)                      
  interceptor.readObject(this, "id",                  
  super.$$_hibernate_read_id());                      
            super.$$_hibernate_write_id(loaded);      
           // ← 문제의 호출                           
        }                                             
        return super.$$_hibernate_read_id();          
    }
  • 위 코드를 보면 interceptor라는 걸로 lazy loading이든 뭐든 한 후, 그 결과를 $$_hibernate_write_id()를 이용하여 객체의 해당 필드에 다시 담아둔다(dirty checking 때문인듯?)
  • 여기서 문제는: 저 super.$$_hibernate_write_id 가 존재하지 않는데도 불구하고 호출한다는 점.
  • TagGroup.id는 부모클래스인 BaseEntity에 val으로 정의되어있어서, $$_hibernate_write_id 없이 $$_hibernate_read_id 만 존재하기 때문
  • Hibernate의 버그로 보임 (https://hibernate.atlassian.net/browse/HHH-17418)
  • 해결은 안됐음

해결

  • 이 버그가 패치된 버전이 없어서, 그냥 쉽게 id를 var로 변경

Native image는 2월28일에 배포됐는데 왜 이제야 문제가됐는가?

  • 결론은 모른다는 것이며, 최소한 코드로 결정되는 버그는 아닌 것 같다. 빌드 환경 등 다른 영향이 있는 것 같다.
  • 클로드의 실험으로는 특정 파일시스템에서 버그가 재현되었음. bytecode enhancement 플러그인이 엔티티 파일을 순회하는 순서가 달라지면 재현된다...?고 하는데 더 파보지 않았습니다 버그같다면 버그같은듯... 클로드 말대로라면 github action runner 등 우리가 모르는 변경으로 인해 갑자기 발생한건가....
  • 이게 사실이라면 prod가 현재 정상인 건 운이 좋았던거고, 언제라도 이미지 다시 빌드하면 터질 수 있다는 뜻... 일단 dev부터 고쳐봅니다

dev 환경 전역에서 발생한 java.lang.NoSuchMethodError:
BaseEntity.$$_hibernate_write_id(java.lang.Long) 수정.

Hibernate bytecode enhancer(7.2.4)가 @MappedSuperclass의 Kotlin val(final
필드)에는 $$_hibernate_write_* 메서드를 생성하지 않으면서, 자식 엔티티의
enhanced 코드에는 해당 메서드 호출을 삽입해 런타임에 NoSuchMethodError가
발생한다 (HHH-17418 변종, 상속 필드 케이스). id/createdAt/updatedAt을
var로 바꿔 writer가 생성되도록 한다.

enableLazyInitialization = true 명시는 제거 (7.2.4 기본값이 true).
enhancement {} 블록 자체는 enhancement opt-in 트리거이므로 유지.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@eastshine2741
eastshine2741 requested review from a team and PFCJeong as code owners July 22, 2026 15:35
@eastshine2741 eastshine2741 changed the title Hibernate enhancer NoSuchMethodError 수정: BaseEntity 프로퍼티를 var로 변경 Hibernate enhancer NoSuchMethodError 수정 Jul 22, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread core/build.gradle.kts
apply(plugin = "org.hibernate.orm")
configure<org.hibernate.orm.tooling.gradle.HibernateOrmSpec> {
enhancement { enableLazyInitialization = true }
enhancement { }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게만 해놔도 enableLazyInitialization 기본값 true로 들어간대서 지웠습니다

그리고 찾아보니 enableDirtyTracking 이런것들도 별도로 있었음.. 걔네도 기본값으로 들어가고있었으니 달라지는건 없습니다 그냥 공유
https://docs.hibernate.org/orm/5.4/topical/html_single/bytecode/BytecodeEnhancement.html

@eastshine2741
eastshine2741 merged commit 430f6d4 into develop Jul 22, 2026
2 checks passed
@eastshine2741
eastshine2741 deleted the fix/hibernate-enhancer-mapped-superclass-writer branch July 22, 2026 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant