티스토리 뷰

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
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함