[JPA]open-in-view 데이터베이스 커넥션 관리
open-in-view 설정은 JPA에서 영속성 컨텍스트와 관련된 설정입니다. 이는 엔티티매니저(Entity Manager) 가 데이터베이스 커넥션을 언제 반납할지를 결정하는 역할을 합니다.
Aug 22, 2024
Contents
open-in-view 란?엔티티를 설정할 때 외래키를
(fetch = FetchType.LAZY)
로 작성하는 경우가 있습니다.이때 활용할 수 있는 것이 open-in-view 인데 이것에 대해 알아보겠습니다.
open-in-view 란?
open-in-view
설정은 JPA에서 영속성 컨텍스트와 관련된 설정입니다. 이는 엔티티매니저(Entity Manager) 가 데이터베이스 커넥션을 언제 반납할지를 결정하는 역할을 합니다.- 데이터베이스 커넥션 (Database Connection) request 가 왔을 때 할당됩니다. em 과 db 사이 연결 선이 생겼다고 상상해 봅시다. 쿼리를 실행하고 결과를 반환하는 데 사용됩니다.
- 영속성 컨덱스트(Persistence Context)
엔티티 매니저가 관리하는 일종의 “1차 캐시” 로 엔티티 객체들을 관리합니다.
엔티티 매니저가 이 컨텍스트를 통해 엔티티 객체들을 조회하거나 저장하며, 영속성 컨텍스트에 있는 엔티티들은 "영속 상태"에 있게 됩니다. 즉, 이 컨텍스트는 엔티티 매니저에 의해 관리됩니다.
- 엔티티 매니저 (Entity Manager)
영속성 컨텍스트를 관리하는 주체입니다.
엔티티 매니저는 영속성 컨텍스트를 생성, 유지, 종료하며, 필요한 경우 데이터베이스와 상호작용할 때 데이터베이스 커넥션을 획득하고 작업이 끝나면 반납합니다.
이해한 바를 그림으로 그려보면 다음과 같습니다.

파란 박스가
spring.jpa.open-in-view=
true
일때핑크 박스가
spring.jpa.open-in-view=
false
입니다. false 는 service 가 끝나면 종료 되는 것을 알 수 있습니다.spring.jpa.open-in-view=
true
request 시 할당 받았던 데이터베이스 커넥션이 응답이 종료될 때 까지 남아있기 때문에 controller 에서도 DB에 추가 요청을 보낼 수 있습니다.
Lazy Loading도 컨트롤러에서 동작할 수 있습니다.spring.jpa.open-in-view=
false
데이터베이스 커넥션이 서비스 계층에서 닫히기 때문에, 이미 조회된 데이터만 사용할 수 있고 추가적인 데이터 로딩은 불가능 합니다.
추가 데이터 로딩이 필요한 경우 service 에서 가능합니다.코드 예제
다음과 같이
Board
엔티티가 있습니다.
Board 엔티티내부에는 User
테이블이 외래키로 설정되어 있으며 fetch LAZY
전략을 사용하고 있습니다. @GetMapping("/test/board/1")
public void testBoard() {
List<Board> boardList = boardRepository.findAll();
System.out.println("---------------------------------------------");
System.out.println(boardList.get(2).getUser().getPassword());
System.out.println("---------------------------------------------");
}

spring.jpa.open-in-view
=
true
컨트롤러 - 서비스 - 리포지토리 일 때 이렇게 엔티티 매니저가 남아있게 됩니다.
(0) (0) (0)
(0) (0) (0)
따라서 컨트롤러의 마지막 출력까지 나오며 쿼리문이 2번 발생합니다.
컨트롤러에서도 데이터베이스 커넥션이 열려있기 때문입니다!
실행된 콘솔 로그
Hibernate:
select
*
from
board_tb
order by
id desc
---------------------------------------------
Hibernate:
select
u1_0.id,
u1_0.created_at,
u1_0.email,
u1_0.password,
u1_0.username
from
user_tb u1_0
where
u1_0.id=?
1234
---------------------------------------------
spring.jpa.open-in-view
=false
컨트롤러 - 서비스 - 리포지토리 일 때 service 응답이 종료될 때
(0) (0) (0)
(X) (0) (0)
커넥션이 컨트롤러에선 닫혀있기 때문에
레이지 로딩을 컨트롤러 에서 접근하고자 하면
LazyInitializationException
가 발생합니다.
이를 해결하기 위해선 레이지 로딩 접근을 서비스 계층에서 마무리 해야합니다.실행된 콘솔 로그
Hibernate:
select
*
from
board_tb
order by
id desc
---------------------------------------------
2024-08-22T11:42:07.751+09:00 ERROR 1632 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.hibernate.LazyInitializationException: could not initialize proxy [shop.mtcoding.blog.user.User#2] - no Session] with root cause
org.hibernate.LazyInitializationException: could not initialize proxy [shop.mtcoding.blog.user.User#2] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:314) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:44) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:102) ~[hibernate-core-6.5.2.Final.jar:6.5.2.Final]
at shop.mtcoding.blog.user.User$HibernateProxy$h72U8kmI.getPassword(Unknown Source) ~[classes/:na]
at shop.mtcoding.blog.board.BoardController.testBoard(BoardController.java:88) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) ~[spring-web-6.1.11.jar:6.1.11]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) ~[spring-web-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.11.jar:6.1.11]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.11.jar:6.1.11]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.11.jar:6.1.11]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.11.jar:6.1.11]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.11.jar:6.1.11]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.11.jar:6.1.11]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.11.jar:6.1.11]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.11.jar:6.1.11]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.11.jar:6.1.11]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:904) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.26.jar:10.1.26]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Share article