Web/JPA 2019. 5. 29. 16:47

 

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을 붙여준다.

posted by 여성게
: