어제 작성한 글에서 이어집니다. 그 해결책 세개 중에서 그나마 쓸만한 해결책은 이것이고, 그 효과도 똑같고, 그 단점도 똑같은 방법입니다.

@OneToOne 맵핑에 @LazyToOne(LazyToOneOption.NO_PROXY)를 추가합니다.

[java]
@Entity
public class Product {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private BigDecimal price;

private String name;

@OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "product")
@LazyToOne(LazyToOneOption.NO_PROXY)
private ProductDetails productDetails;

@OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "product")
@LazyToOne(LazyToOneOption.NO_PROXY)
private ProductInfo productInfo;

}
[/java]

이렇게요. 그리고 빌드 타임에 코드를 조작하도록 메이븐 플러그인을 설정합니다.
[xml]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<tasks>
<taskdef name="instrument"
classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
<classpath>
<path refid="maven.runtime.classpath" />
<path refid="maven.plugin.classpath" />
</classpath>
</taskdef>
<instrument verbose="false">
<fileset dir="${project.build.outputDirectory}">
<include name="/usecase/snapshot/domain/*.class" />
</fileset>
</instrument>
</tasks>
</configuration>
</plugin>
[/xml]

여기서 include에 사용한 경로는 자신의 프로젝트에 맞게 잘 고쳐야겠죠.

그리고 빌드를 합니다. antrun의 run 골을 실행하는게 주요 목적이겠죠.

[java]
//Test
Product product = productRepository.findOne(1l);
assertThat(product, is(notNullValue()));
assertThat(product.getName(), is("keesun"));

System.out.println("========LAZY LOADING…=========");
product.getProductDetails().getDetails();
[/java]

이제 다시 테스트를 실행하면 다음과 같은 로그를 볼 수 있습니다.

18:51:54.224 [main] DEBUG org.hibernate.loader.Loader – loading entity: [usecase.snapshot.domain.Product#1]
18:51:54.226 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
18:51:54.227 [main] DEBUG org.hibernate.SQL –
select
product0_.id as id1_0_,
product0_.name as name1_0_,
product0_.price as price1_0_
from
Product product0_
where
product0_.id=?
Hibernate:
select
product0_.id as id1_0_,
product0_.name as name1_0_,
product0_.price as price1_0_
from
Product product0_
where
product0_.id=?
18:51:54.231 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to open ResultSet (open ResultSets: 0, globally: 0)
18:51:54.231 [main] DEBUG org.hibernate.loader.Loader – result row: EntityKey[usecase.snapshot.domain.Product#1]
18:51:54.238 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to close ResultSet (open ResultSets: 1, globally: 1)
18:51:54.238 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
18:51:54.239 [main] DEBUG org.hibernate.engine.TwoPhaseLoad – resolving associations for [usecase.snapshot.domain.Product#1]
18:51:54.244 [main] DEBUG org.hibernate.engine.TwoPhaseLoad – done materializing entity [usecase.snapshot.domain.Product#1]
18:51:54.245 [main] DEBUG o.h.e.StatefulPersistenceContext – initializing non-lazy collections
18:51:54.245 [main] DEBUG org.hibernate.loader.Loader – done entity load
========LAZY LOADING…=========
18:51:54.246 [main] DEBUG org.hibernate.loader.Loader – loading entity: [usecase.snapshot.domain.ProductDetails#1]
18:51:54.247 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
18:51:54.247 [main] DEBUG org.hibernate.SQL –
    select
productdet0_.id as id2_0_,
productdet0_.details as details2_0_,
productdet0_.product_id as product3_2_0_
from
ProductDetails productdet0_
where
productdet0_.product_id=?

Hibernate:
select
productdet0_.id as id2_0_,
productdet0_.details as details2_0_,
productdet0_.product_id as product3_2_0_
from
ProductDetails productdet0_
where
productdet0_.product_id=?
18:51:54.249 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to open ResultSet (open ResultSets: 0, globally: 0)
18:51:54.250 [main] DEBUG org.hibernate.loader.Loader – result row: EntityKey[usecase.snapshot.domain.ProductDetails#1]
18:51:54.251 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to close ResultSet (open ResultSets: 1, globally: 1)
18:51:54.251 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
18:51:54.251 [main] DEBUG org.hibernate.engine.TwoPhaseLoad – resolving associations for [usecase.snapshot.domain.ProductDetails#1]
18:51:54.252 [main] DEBUG org.hibernate.engine.TwoPhaseLoad – done materializing entity [usecase.snapshot.domain.ProductDetails#1]
18:51:54.253 [main] DEBUG o.h.e.StatefulPersistenceContext – initializing non-lazy collections
18:51:54.253 [main] DEBUG org.hibernate.loader.Loader – done entity load
18:51:54.253 [main] DEBUG org.hibernate.loader.Loader – loading entity: [usecase.snapshot.domain.ProductInfo#1]
18:51:54.254 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
18:51:54.254 [main] DEBUG org.hibernate.SQL –
    select
productinf0_.id as id0_0_,
productinf0_.info as info0_0_,
productinf0_.product_id as product3_0_0_
from
ProductInfo productinf0_
where
productinf0_.product_id=?

Hibernate:
select
productinf0_.id as id0_0_,
productinf0_.info as info0_0_,
productinf0_.product_id as product3_0_0_
from
ProductInfo productinf0_
where
productinf0_.product_id=?
18:51:54.256 [main] DEBUG org.hibernate.jdbc.AbstractBatcher – about to open ResultSet (open ResultSets: 0, globally: 0)
18:51:54.256 [main] DEBUG org.hibernate.loader.Loader – result row: EntityKey[usecase.snapshot.domain.ProductInfo#1]

ㅎㅎ충격적이지 않나요? 일단 Lazy Fetching이 되긴 했습니다. 그런데 문제는 OneToOne 관계이면서, 아직 로딩하지 않았던 다른 객체도 같이 가져왔네요? 이게 바로 제가 OneToOne Lazy Fetching을 파보게 된 계기입니다. ㅎㅎ;;

이것 때문에 동료분과 하이버네이트와 javassist 소스 코드를 까보았는데… 아주… 그냥.. 죽여주네요. OP코드도 필드 추가하고, 세터 게터 메서드 추가하고, 하이버 Session 가지고 initialize도 시키고.. @_@;;; 아오.. 이 방법 밖에 없는것인가? 필드를 개별적으로 Lazy Fetching 할 순 없는 것인가?….