JPA를 사용하려면 스프링 컨테이너가 제공하는 전략을 따라야 한다.
스프링 컨테이너는 JPA의 영속성 컨텍스트를 지원할 때 기본 전략으로 트랜잭션 범위의 영속성 컨텍스트
를 사용한다.
영속성 컨텍스트:
- 엔티티를 영구 저장하는 환경
- 어플리케이션과 데이터베이스 사이에서 객체를 보관하는 논리적인 개념
- EntityManager를 통해 영속성 컨텍스트에 접근한다.
트랜잭션의 범위와 영속성 컨텍스트의 생존 범위를 맞추는 전략으로, 트랜잭션이 시작하는 순간 영속성 컨텍스트도 생성되고, 끝나는 순간 영속성 컨텍스트가 종료된다.
비즈니스 로직을 시작하는 서비스 계층에 @Transactional 어노테이션을 선언해서 트랜잭션을 시작한다.
@Transactional
@Service
public class ArticleService {
public void getArticle(Long articleId) {
return articleRepository.findById(articleId);
}
}
트랜잭션의 메소드가 정상 종료되면 트랜잭션을 커밋하는데, JPA가 영속성 컨텍스트를 flush해서 변경 내용을 데이터베이스에 반영한 후에 데이터베이스 트랜잭션을 커밋한다.
트랜잭션이 같으면 같은 영속성 컨텍스트를 사용하고 다르면 다른 영속성 컨텍스트를 사용한다.
트랜잭션이 종료되면 영속성 컨텍스트가 끝나고 관리되던 엔티티가 준영속 상태
가 된다.
@Controller
public class ArticleController {
...
public String view(Long articleId) {
Article article = articleService.getArticle(articleId);
article.getContent(); //지연 로딩
}
}
getArticle 메소드를 호출해서 반환받은 article은 준영속 상태이기 때문에 지연 로딩을 하면 예외가 발생한다.
OSIV
를 사용해서 엔티티를 프레젠테이션 계층까지 영속 상태로 유지하는 방법영속성 컨텍스트를 뷰까지 열어둔다는 뜻
Hibernate에서 사용하는 용어.. JPA에서는 OEIV(Open EntityManager In View)라고 하지만 OSIV로 보통 부른다.
지연 로딩을 사용할 수 있다는 것이 큰 장점이다.
요청이 들어오자 마자 트랜잭션을 시작하고 요청이 끝날 때 종료하는 방법
코드량이 많이 증가하기 때문에 요청 당 트랜잭션 방식의 OSIV는 거의 사용하지 않는다.
비즈니스 계층에서 트랙잭션을 사용하는 OSIV - OSIV를 사용하지만 트랜잭션은 비즈니스 계층에서만 사용하는 방법
1. 클라이언트 요청이 들어오면 서블릿 필터나 스프링 인터셉터에서 영속성 컨텍스트를 생성하되 트랜잭션은 시작하지 않는다.
2. 서비스 계층에서 @Transactional로 트랜잭션을 시작할 때 영속성 컨텍스트를 찾아와서 트랜잭션을 시작한다.
3. 서비스 계층이 끝나면 트랜잭션을 커밋하고 영속성 컨텍스트를 flush한다. (트랜잭션은 종료되지만 영속성 컨텍스트는 유지)
4. 서블릿 필터나 스프링 인터셉터로 요청이 돌아오면 영속성 컨텍스트를 종료한다. flush를 호출하지 않고 바로 종료한다.
@Controller
class ArticleController {
...
public String view(Long articleId) {
Article article = articleService.getArticle(articleId);
article.setTitle("XXX");
articleService.logic_that_starts_transaction();
// 트랜잭션을 시작하는 로직
}
}
OSIV를 끄고 CQRS(Command Query Responsibility Segregation) 패턴을 사용하여 커맨트와 쿼리를 분리하는 것이 좋다.
스프링부트는 OSIV가 기본값으로 켜져있다.
jpa옵션에 open-in-view를 false로 설정해주면 된다.
참고: CQRS 패턴
자바 ORM 표준 JPA 프로그래밍
[JPA] OSIV란?
OSIV와 성능 최적화