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 |