항상 스프링을 실행시키면 다음과 같은 로그를 확인할 수 있다.
WARN 13164 --- [book] [ restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
spring.jpa.open-in-view=true #기본값
true로 명시 해 적어주지 않아도 기본 설정은 true이며 영속성 컨텍스트가 컨트롤러와 뷰 계층에서도 유효하다.
spring.jpa.open-in-view=false
프로퍼티에서 다음과 같이 설정한다면 영속성 컨텍스트는 트랜잭션 범위 내에서만 유효하다.
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private String publisher;
private LocalDate published_date;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id")
@JsonBackReference
private Store store;
}
public class Store {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
책과 상점의 엔티티를 만들고, 책에서 상점으로의 단방향 매핑을 맺는다.
책과 연관된 상점을 가져올 땐 지연로딩을 사용한다.
public Book insertBook(BookDto bookDto){
Store store = storeRepository.save(Store.builder().name(""+Math.random()).build());
Book book = Book.builder().title(bookDto.getTitle()).author(bookDto.getAuthor()).publisher(bookDto.getPublisher()).published_date(bookDto.getPublished_date())
.store(store).build();
return bookRepository.save(book);
}
테스트를 위해 책을 등록 시 랜덤 숫자를 이름으로 가지는 상점의 엔티티를 생성해 만들고 책과 연관관계를 맺는다.
@Transactional(readOnly = true)
public Book getBookById(Long id){
Book book = bookRepository.findById(id).orElse(null);
log.info("이 책이 속한 상점 : {}",book.getStore().getName());
return book;
}
서비스에서 책의 상점의 이름을 호출 해 로그로 확인한다.


트랜잭션 범위 내에선 지연로딩이 잘 이루어지는 것을 확인할 수 있다.
@GetMapping("/{id}")
public ResponseEntity<Book> searchBook(@PathVariable Long id){
Book book = bookService.getBookById(id);
log.info("서점의 번호 : " + book.getStore().getName());
return ResponseEntity.ok(book);
}
컨트롤러에서 책의 상점의 이름을 호출 해 로그로 확인한다.


osiv를 끄면 컨트롤러에서 영속성 컨텍스트가 끊겨 연관 객체를 불러올 수 없는 것을 확인할 수 있다.
@Controller
@RequiredArgsConstructor
public class BookWebController {
private final BookService bookService;
@RequestMapping("/test")
public String test(Model model){
Book book = bookService.getBookById(1L);
model.addAttribute("book",book);
return "test";
}
}
미리 책을 생성하여 책을 찾아 뷰로 반환한다
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<h1 th:text="${book.getStore().getName()}"></h1>
</body>
</html>
타임리프 사용해 책 객체에 있는 상점의 이름을 가져온다.


osiv를 끌 시 뷰에서도 연관객체를 가져올 수 없다.
osiv를 킨다면 뷰까지 영속성 컨텍스트가 유지된다.
어디서든지 원하는 연관된 객체를 불러올 수 있다는 편리한 점이 있지만,
그만큼 데이터베이스 커넥션을 잡아먹고 있다는 뜻이다.
1. 즉시로딩을 사용한다면 기존 객체를 불러오는 시점에서 연관 객체를 불러오기 때문에 뷰나 컨트롤러 계층에서 연관 객체를 호출할 수 있다.
2. 트랜잭션 범위에서 연관 객체의 호출하고 싶은 속성을 호출한다. 한 번 호출하면 그 정보는 메모리에 저장되기 때문에 컨트롤러나 뷰 계층에서도 그 데이터를 사용 가능하다.
하지만 타임리프를 통해 뷰를 제공하는 것이 아닌,
RESTAPI를 개발에서는 컨트롤러에서 서비스에서 반환된 dto를 바로 응답하기에 컨트롤러까지 영속성 컨텍스트가 유지되어 db 커넥션을 먹어도 찰나의 순간 차이이기에osiv가 on, off 든 차이가 미세하거나 거의 없다는 생각이 들었다.
RESTAPI에서 oisv의 on,off 상태가 성능의 미세한 차이를 가진다 해도, 혹시 모를 실수로 인한 컨트롤러에서의 엔티티 접근 방지와 조금의 성능 개선을 위해 끄는 것이 좋다고 생각한다.