JPA : 동시성 문제 + OSIV

    이 게시글은 자바 ORM 표준 JPA 프로그래밍을 보고 필요한 부분을 정리한 글입니다.

     


    스프링의 영속성 컨텍스트 기본전략

    트랜잭션 시작 시 영속성 컨텍스트 생성

    스프링의 영속성 컨텍스트 기본 전략은 트랜잭션을 시작할 때, 영속성 컨텍스트를 만든다. 그리고 트랜잭션이 끝날 때 영속성 컨텍스트를 종료하는 것이다. 따라서 트랜잭션과 영속성 컨텍스트의 생명주기는 동일하다. 

    스프링에서는 같은 트랜잭션 내에서는 어떤 위치에서 엔티티 매니저를 주입 받더라도 같은 영속성 컨텍스트를 관리하는 것이 보장된다. 항상 같은 엔티티 매니저가 주입되지 않을 수 있다. 그렇지만 서로 다른 엔티티 매니저가 들어오더라도 동일 트랜잭션에서는 동일 영속성 컨텍스트만을 관리하게 된다. 

    쓰레드마다 다른 영속성 컨텍스트 배정

    스프링은 쓰레드마다 서로 다른 영속성 컨텍스트를 배정한다. 따라서 멀티 쓰레드 환경에서 서로 다른 영속성 컨텍스트를 사용하는 것이 보장된다. 따라서 영속성 컨텍스트는 멀티 쓰레드의 동시성 문제에서 자유롭다. 또한 서로 다른 쓰레드마다 동일한 엔티티 매니절르 주입받을 수 있다. 그렇지만 쓰레드마다 다른 영속성 컨텍스트를 관리하기 때문에 동시성 문제에서 자유롭다. 

    트랜잭션마다 영속성 컨텍스트 생성 전략은 트랜잭션이 끝나면 영속성 컨텍스트가 없어진다. 이 말은 트랜잭션 내에서 불러온 엔티티는 트랜잭션이 끝이 나면 영속성 컨텍스트가 사라지게 되면서 '준영속' 상태가 된다는 것이다. 영속성 컨텍스트가 없기 때문에 이 엔티티였던 객체는 '지연 로딩'이 불가능하다. 

    또한, 준영속 상태가 된 엔티티는 DB에서 동일한 PK로 조회를 했다고 하더라도, 다른 객체가 된다. 이 부분은 엔티티의 동일성 부분에서 다시 공부할 예정이다.

     


    스프링의 영속성 컨텍스트 OSIV(Open Session In View)

    OSIV는 스프링의 기본 전략에서 트랜잭션이 끝난 View 단에서 필요한 값이 '지연로딩'이 되지 않는다는 문제점을 커버하고자 나온 동작방식이다. OSIV는 스프링의 application.properties에서 다음 코드로 동작 방식을 설정할 수 있다. 기본적으로 스프링부트는 OSIV를 사용한다.

    spring.jpa.open-in-view=true

     

    OSIV는 쉽게 말해 영속성 컨텍스트가 View단까지 살아있도록 하는 것이다. 이렇게 하면 ThymeLeaf와 같은 View에서 엔티티에서 필요한 값을 지연로딩으로 손쉽게 불러올 수 있다는 장점이 있다. OSIV에서 영속성 컨텍스트의 동작방식은 다음과 같다.

    1. HTTP 요청이 들어오면 필터/인터셉터에서 영속성 컨텍스트를 만든다.
    2. 영속성 컨텍스트는 Dispatcher Servlet을 타고 들어가서 필요한 일을 하고 응답한다.
    3. 응답이 필터/인터셉터로 들어오게 되면 이 때 영속성 컨텍스트를 종료한다. 

    OSIV를 설정하면 영속성 컨텍스트는 필터에서 생성되서 필터에서 파괴된다. 따라서 View 단에서 영속성 컨텍스트를 사용할 수 있는 것이 보장된다. 

    OSIV는 멀티 쓰레드 관점에서 자유롭다. 앞서 스프링은 쓰레드마다 다른 영속성 컨텍스트를 배정한다고 했다. 따라서 서로 다른 요청이 오면, 서로 다른 쓰레드가 배정된다. 서로 다른 쓰레드는 서로 다른 영속성 컨텍스트를 배정 받기 때문에 영속성 컨텍스트는 동시성 문제에서 자유롭다. 

    한 가지 주의해야 할 점은 트랜잭션이 끝나도 영속성 컨텍스트가 남아있다는 점이다. 트랜잭션이 끝나면 em.flush()를 한다. 즉, 트랜잭션에서 조회한 엔티티들은 영속화 되어있다. 이 때 다시 한번 트랜잭션을 타게 되면, 영속화 된 엔티티들이 다시 한번 영향을 받을 수 있기 때문에 의도치 않은 동작이 발생할 수 있다. 

     


    트랜잭션 없이 읽기

    JPA는 트랜잭션이 없어도 DB에서 데이터를 조회할 수 있다. 왜냐하면 트랜잭션 내에서 데이터를 수정할 수 있기 때문에 조회는 상관이 없다. 그렇지만 JPA는 영속화를 해야하기 때문에 영속성 컨텍스트는 반드시 존재해야한다. 

    OSIV는 트랜잭션과 상관없이 필터/인터셉터 단에서 영속성 컨텍스트가 만들어진다. 따라서 OSIV는 트랜잭션 여부와 상관없이 어느 곳에서도 DB의 값을 조회할 수 있다. View 단에는 트랜잭션이 없다. 그렇지만 영속성 컨텍스트가 남아있다. 따라서 OSIV는 영속성 컨텍스트가 남아있기 때문에 View 단에서 지연 로딩이 가능해진다. 

     


    정리

    • 스프링은 쓰레드마다 다른 영속성 컨텍스트를 배정한다. 따라서 영속성 컨텍스트는 멀티 쓰레드 환경에서 자유롭다.
    • 스프링은 서로 다른 엔티티 매니저가 주입되어도 영속성 컨텍스트만 같으면 아무 문제가 없다. 
    • 스프링의 기본 트랜잭션 전략은 트랜잭션이 생성될 때 영속성 컨텍스트를 만드는 전략이다. 트랜잭션이 끝나면 영속성 컨텍스트가 사라지고, 객체는 준영속화된다. 따라서 지연 로딩이 불가능하다.
    • OSIV는 요청이 필터로 왔을 때 영속성 컨텍스트를 만들고, 필터로 나갈 때 영속성 컨텍스트가 파괴된다. 영속성 컨텍스트가 남아있기 때문에 트랜잭션 읽기를 통해 View 단에서도 지연 로딩을 지원한다.

    댓글

    Designed by JB FACTORY