OSIV란
OSIV
OSIV(open session in View)는 영속성 컨텍스트를 뷰까지 열어준다는 의미이다.
OSIV는 하이버네이트에서 사용하는 용어이고 JPA에서는 OEIV라고 하지만, 관례상 OSIV라고 부른다.
OSIV 사용이유
OSIV(Open Session In View)를 사용하면 트랜잭션의 범위가 뷰까지 확장되어 영속 상태가 유지됩니다. 기본적으로 JPA에서는 트랜잭션 내에서 엔티티를 관리하고, 트랜잭션이 종료되면 영속성 컨텍스트가 닫혀 엔티티는 준영속 상태가 됩니다.
그러나 OSIV를 사용하면 트랜잭션을 뷰까지 확장하여 영속성 컨텍스트를 닫지 않고 유지합니다. 이는 컨트롤러나 뷰에서도 영속 상태의 엔티티를 유지하고 조회, 수정할 수 있게 됩니다. 따라서 뷰나 컨트롤러에서도 지연 로딩을 통해 필요한 데이터를 조회하거나 변경할 수 있게 됩니다.
요청 당 트랜잭션 방식의 OSIV 문제점
그림 13.6 처럼 과거의 요청당 트랜잭션 방식이있다. 하지만 이러한방식은 문제점있다. 만약에 컨트롤로에서 엔티티의 객체를 변경한다고 해보자 서비스단에서 변경하는것은 당연하다고 생각하지만. 컨트롤로에서 변경을 한다면 스프링이 추구하는 MVC패턴 방법을 위배하며, 실제 데이터 베이스까지 변경 내용이 반영되어서 애플리케이션을 유지보수하기 힘들어지는 문제점이 발생한다. 이런 문제점을 해결하기 위해서 다음과 같은 방법을 사용하면된다.
- 엔티티 읽기 전용 인터페이스로 제공 : 읽기 전용메소드만 있는 인터페이스를 만들어 프렌젠테이션 계층은 읽기 전용메소드만 있는 인터페이스를 사용한다.
- 엔티티 래핑 : 엔티티의 읽기 전용 메소드만 가지고 있는 엔티티를 감싼 객체를 만들고 프리젠테이션 계층에 반한하는 방법이다.
- DTO만 반환 : 데이터만 전달하난 객체인 DTO를 생성해서 반환하는 방법
하지만 이러한 문제점은 코드량이 상당히 증가한다는 단점이 있다. 이러한 문제점들 때문에 최근에는 요청당 트랜잭션 방식의 OSIV 방식을 사용하지 않고있다.
스프링 OSIV : 비지니스 계층 트랜잭션
스프링이 제공하는 OSIV는 위에서 설명했던 문제들을 어느정도 해결했다. 과거의 OSIV는 트랜잭션 범위가 프리젠테이션 범위까지 존재했지만 스프링이 제공하는 OSIV방식은 비지니스 계층에서만 트랜잭션이 동작한다
트랜잭션 없이 읽기
JPA 에서 영속성 컨텍스트의 변경은 트랜잭션 안에서 수행되어야한다. 만약 트랜잭션이 아닌 곳에서 flush() 를하면 TransactionRequiredException 예외가 발생한다. 즉, 엔티티를 변경하지 않고 조회만 할때는 트랜잭션이 없어도 된다. 이것을트랜잭션 없이 읽기(Nontransactional reads)라고 부른다.
OSIV 활성 방법
- build.gradle :
- implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 의존성 추가(없을시)
- application.properties :
- spring.jpa.open-in-view=true OSIV활성화 적지않으면 OSIV 비활성화
예제 코드
Repository
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {}
스프링 데이터 JPA 사용
Service
@Transactional
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberJpaRepository memberJpaRepository;
public Member getMember(Long memberId){
Member member = memberJpaRepository.findById(memberId).get();
return member;
}
}
- getMember(Long memberId) : 멤버의 Id값으로 사용자의 정보를 조회하는 메소드
Controller
@Controller
@RequiredArgsConstructor
public class OSIVController {
private final MemberService memberService;
@PostMapping("/OSIV")
public ResponseEntity<?> osi(@RequestParam Long id){
try{
Member member = memberService.getMember(id);
member.setUsername("수정");
return ResponseEntity.ok("사용자 이름 :"+member.getUsername());
}catch (Exception e){
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("오류발생");
}
}
}
- 먼저 조회를 통해 멤버 엔티티를 가져오고 그후에 사용자 이름을 수정하는 코드를 작성.
실행하기전 DB 테이블
실행(postman 사용)
실행후 DB 테이블
위에 예제처럼 실행하고 나면 MEMBER의 username이 수정으로 변경되었으나. 실제 DB에는 변경되지 않은 모습을 볼수있다. 여기서 강제로 강제로 플러쉬를 한다해서 db에 저장이 되지않고 트랜잭셕범위 밖에있다고 오류가 예외가 발생한다.
'JPA' 카테고리의 다른 글
[JPA] QueryDSL 에서 FROM절에 SubQuery 및 ROW_NUMBER() 사용 방법 (1) | 2024.09.18 |
---|---|
[JPA] QueryDsl 를 이용한 무한 페이징(Infinite scrolling) - 성능 개선 (0) | 2024.05.02 |
[JPA] 값 타입 (1) | 2024.01.05 |
[JPA] 프록시와 연관관계 관리 (0) | 2024.01.05 |