이거 때매 거의 이틀을 삽질했다.... 이해가 안되어서....
OSIV=false + getReferenceId - @Transactional = LazyInitializationExceptions
문제의 코드
@Override
public Product selectProduct(Long number) {
Product selectedProduct = productRepository.getReferenceById(number);
return selectedProduct;
}
다음과 같이 getReferenceById()
를 사용한 객체를 리턴하고,
@Override
public ProductResponseDto getProduct(Long number) {
Product product = productDAO.selectProduct(number);
ProductResponseDto productResponseDto = ProductResponseDto.builder()
.number(product.getNumber())
.name(product.getName())
.price(product.getPrice())
.stock(product.getStock())
.build();
return productResponseDto;
}
그 객체를 외부 메서드에서 사용한다면, 이런 에러가 발생한다.
org.hibernate.LazyInitializationException: could not initialize proxy [org.spring.study.data.entity.Product#1] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:314) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:44) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:102) ~[hibernate-core-6.4.1.Final.jar:6.4.1.Final]
at org.spring.study.data.entity.Product$HibernateProxy$0VRfTOUF.getName(Unknown Source) ~[main/:na]
at org.spring.study.data.service.impl.ProductServiceImpl.getProduct(ProductServiceImpl.java:28) ~[main/:na]
at org.spring.study.data.controller.ProductController.getProduct(ProductController.java:23) ~[main/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~
...
메서드에 @Transactional를 적용하면 잘 동작한다.
@Override
@Transactional
public ProductResponseDto getProduct(Long number) {
Product product = productDAO.selectProduct(number);
ProductResponseDto productResponseDto = ProductResponseDto.builder()
.number(product.getNumber())
.name(product.getName())
.price(product.getPrice())
.stock(product.getStock())
.build();
return productResponseDto;
}
발생 원인?
이게 뭐가 문제냐면..
- getReferenceId는 지연 로딩(Lazy Loading)을 사용하므로 빈 프록시 객체를 리턴한다.
- OSIV가 활성화 되어있다면 외부 메서드에서 프록시 객체를 사용해도 문제가 되지 않는다. 왜냐면 Session과 Transaction이 쭉 유지되어 있기 때문에
- 그런데 OSIV가 비활성화 되어 있을 경우, getReferenceId 메서드가 끝나면 자동으로 Session과 Transaction이 닫힌다. 따라서 getReferenceId 메서드 외부에서 프록시 객체를 사용하면 LazyInitialzationException이 발생한다.
근데 이상하게 프록시 객체의 @Id를 접근하려 하면 또 쿼리가 필요없이 값을 얻어오더라..