2019. 5. 29. 16:47ㆍWeb/JPA
Spring boot + JPA 환경에서 개발중 멀티스레드 환경에서 JPA를 사용하면서 겪었던 문제이다. 우선 원인은 멀티 스레드 환경에서 트랜잭션의 공유가 안되는 문제였다.
[상황] CompletableFuture.runAsync()를 사용하여 Multi Thread 환경에서 JPA를 사용하는 상황이었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public Object controllerMethod(@PathVariable long id, Locale locale) {
CompletableFuture.runAsync(()->{
service.method(id);
},executor)
.exceptionally(e->{
log.debug("CompletableFuture.runAsync :::: exception - {}",e.getMessage());
return (Void)null;
});
return ...
}
|
cs |
service.method(id)을 호출하는 것은 서블릿의 메인 쓰레드와 별개로 Worker 쓰레드가 새로 생성되어 메소드를 수행한다. service.method(id)의 자세한 내용은 여기서 보여줄 수 없지만, @OneToMany 관계에 있는 Entity를 가져오는 도중에
LazyInitializationException 예외가 발생한 것이다. 왜냐하면 메인쓰레드와 새로 생성된 워커쓰레드는 서로 다른 쓰레드이기 때문에 트랜잭션을 공유하지 못하는 것이다. 즉, 트랜잭션이 끊기면서 Proxy에서 갖고 있던 @OneToMany 엔티티값을 잃어버렸기에 해당 객체를 호출하는 순간 예외가 발생한 것이다.(@OneToMany는 기본적으로 fetch=LAZY)
[LazyInitializationException 문제해결방법]
1) fetch=EAGER로 바꾼다. -> 필자는 이미 다른 필드에 fetch=EAGER를 사용해서
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 예외가 발생하였다. 물론 이것도 해결하는 방법이 있다.
1)-1 Caused by: org.hibernate.loader.MultipleBagFetchException 해결방법 ->
@OneToMany(fetch=FetchType.EAGER)
@LazyCollection(LazyCollectionOption.FALSE)
2)controllerMethod(메인스레드에서 수행하는 메소드)에는 별도로 JPA관련 오퍼레이션을 수행하는 부분이 존재하지 않음으로,
service.method(id)(워커스레드에서 수행되는 메소드)에 @Transanctional을 붙여준다.
'Web > JPA' 카테고리의 다른 글
JPA - @JoinColumn 정리 (0) | 2024.07.18 |
---|---|
Spring JPA - JPA cascade 란?! (0) | 2019.04.29 |
JPA - 영속성 컨텍스트와 JPQL (0) | 2019.02.12 |
JPA - JPAQuery와 Q클래스 (0) | 2019.02.11 |
JPA - NativeQuery ( SQL ) 네이티브 SQL 사용하기! (0) | 2019.02.11 |