티스토리 뷰
0. bytecode instrumentation
jpa 의 프록시는 bytecode 조작으로 만들어집니다. 포스트의 최종목적은 OneToOne 의 양방향관계에서 프록시를 만들기 위해 발생하는 N+1 문제를 해결하는 것 입니다. 따라서 bytecode 을 조작하여 프록시 객체가 생성되지 않아도 되도록 해야하며, 또한 프록시가 초기화 되지 않더라도 나중에 필요한 순간에 객체를 가져올 수 있도록 해야합니다. 이를 위해 bytecode instrumentation 이 사용됩니다.
bytecode instrumentation 은 bytecode 에 직접 수정을 가해서 소스파일의 변경없이 원하는 기능을 부여하는 기술입니다.
gradle / maven 과 같은 빌드 도구의 설정파일에서 수정할 내용을 작성할 수 있습니다.
1. Maven 프로젝트
메이븐의 경운 제가 사용하는 빌드도구가 아니기 때문에 정확하게 동작하는지까지는 확인해보지 못했습니다.
추가적인 정보는 출처의 hibernate 깃을 참고해주세요.
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
<enableAssociationManagement>true</enableAssociationManagement>
<enableExtendedEnhancement>false</enableExtendedEnhancement>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
2. Gradle 프로젝트
저는 프로젝트 빌드도구로 gradle groovy dsl 을 이용합니다. 2022-02-25 기준으로 버전별 설정방법을 포스팅했습니다.
다른 많은 블로그와 포스트를 참고했는데 날자별로 차이가 있는것을 보아 지금 제 포스트도 미래에는 제대로 작동하지 않을 수 도 있습니다.
우선 spring boot 프로젝트를 처음 시작하는 분, 혹은 가장 최신 버전의 hibernate 를 사용하고 계신 분들은 6.0.0.Alpha9 버전을 사용할 수 있으며 다음과 같이 세팅할 수 있습니다. 이전 버전의 경우 enhance 로 설정했다면 6 버전 이후는 enhancement 를 이용합니다. 또한 변수명도 이전버전과는 약간 상이합니다.
gradle 프로젝트 (version 6.0.0.Alpha9 사용하는 경우)
// plugins 에 추가
plugins {
id "org.hibernate.orm" version '6.0.0.Alpha9'
}
// configuration 에 추가
hibernate {
enhancement {
lazyInitialization = true
dirtyTracking = true
associationManagement = true
extendedEnhancement = false
}
}
저와 같이 hibernate 5 버전을 이용하시는 분들은 다음과 같이 설정할 수 있습니다. 약간 legacy 한 방법이지만 buildscript 에 plugin dependency 경로를 직접 선언하고 apply plugin 을 이용해 플러그인을 사용할 수 있습니다. 위와 동일하게 코드를 작성할 수 없는 이유가 gradle plugin repository 에는 최신버전인 6.0.0.Alpha9 버전만 올라와 있기 때문입니다.
그럼 hibernate 를 버전업해서 사용하면 되는게 아니냐 생각도 듭니다. 저같은 경우는 postgresql 의 json type 컬럼을 사용하기 위해
아래 라이브러리를 추가로 사용하고 있는데, 이 라이브러리는 hibernate 5버전까지 커버하기 때문에 6버전을 사용할 수 없었습니다.
implementation 'com.vladmihalcea:hibernate-types-52:2.10.0'
gradle 프로젝트 (6.0.0.Alpha9 외 다른 버전을 사용하는 경우)
ext {
hibernateVersion = '5.6.5.Final'
}
buildscript {
dependencies {
classpath "org.hibernate:hibernate-gradle-plugin:$hibernateVersion"
}
}
apply plugin: 'org.hibernate.orm'
hibernate {
enhance {
// available options - all default to false
enableLazyInitialization = true
enableDirtyTracking = true
enableAssociationManagement = true
enableExtendedEnhancement = false
}
}
dependencies {
implementation "org.hibernate:hibernate-core:$hibernateVersion"
}
3. bytecode instrumentation
gradle 혹은 maven 프로젝트에 설정을 마무리 하고 다시 빌드해보면 엔티티의 바이트 코드에 $$_hibernate_ 로 시작하는 메서드가 잔뜩 생긴걸 확인할 수 있습니다. (intellij 를 사용하시는 분들이 많은데 out directory 가 아닌 build/class 를 확인해야 합니다)
@Entity
@Table(
name = "post"
)
public class Post implements ManagedEntity, PersistentAttributeInterceptable, ExtendedSelfDirtinessTracker {
@Transient
private transient PersistentAttributeInterceptor $$_hibernate_attributeInterceptor;
public Object $$_hibernate_getEntityInstance() {
return this;
}
public EntityEntry $$_hibernate_getEntityEntry() {
return this.$$_hibernate_entityEntryHolder;
}
public void $$_hibernate_setEntityEntry(EntityEntry var1) {
this.$$_hibernate_entityEntryHolder = var1;
}
public ManagedEntity $$_hibernate_getPreviousManagedEntity() {
return this.$$_hibernate_previousManagedEntity;
}
public void $$_hibernate_setPreviousManagedEntity(ManagedEntity var1) {
this.$$_hibernate_previousManagedEntity = var1;
}
...
}
이때 만들어지는 AttributeIntercepter 가 getter 메서드를 인터셉트하여 프록시 생성을 관리하게 됩니다.
바이트 코드에 $$_hibernate_ 형식의 코드가 생성된것을 확인하고 N + 1 문제가 발생하던 쿼리를 다시 실행시키면 정상적으로 한번만 쿼리가 실행되는것을 확인할 수 있습니다.
참고
hibernate doc : https://hibernate.org/orm/documentation/getting-started/#system-requirements
hibernate git : https://github.com/hibernate/hibernate-orm/tree/main/tooling/hibernate-gradle-plugin
jboss doc: https://docs.jboss.org/hibernate/orm/5.2/topical/html_single/bytecode/BytecodeEnhancement.html
gradle plugin: https://plugins.gradle.org/plugin/org.hibernate.orm
'jpa' 카테고리의 다른 글
[JPA] bytecode instrumentation 을 이용한 lazy loading 활성화-1 (1) | 2022.02.24 |
---|