Search

47. 스프링부트 포토그램 프로필 페이지 open in view 개념잡기

프로필 페이지

사진설명에 하이루라고 적고 업로드하면은
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.cos.photogramstart.domain.user.User.images, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final] at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final] at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final] at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final] at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:621) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final] at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na] at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na] at com.cos.photogramstart.domain.user.User.toString(User.java:27) ~[classes/:na] at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na] at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na] at com.cos.photogramstart.domain.image.Image.toString(Image.java:23) ~[classes/:na] at java.base/java.lang.String.valueOf(String.java:2951) ~[na:na] at java.base/java.lang.StringBuilder.append(StringBuilder.java:168) ~[na:na] at com.cos.photogramstart.service.ImageService.사진업로드(ImageService.java:47) ~[classes/:na] at com.cos.photogramstart.web.ImageController.imageUpload(ImageController.java:44) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] . . .
Java
복사
오류가 뜬다
오류가 난 이유는 ImageService.java에서
System.out.println("imageEntity :: " +imageEntity);
Java
복사
를 주석처리하면 오류가 나지 않는다
Image imageEntity
Java
복사
Image 가면 이미지관련 컬럼과 User도 있다
또 User에 가면
또 Image가 있고 이게 반복되면서
System.out.println("imageEntity :: " +imageEntity);
Java
복사
imageEntity인 오브젝트를 실행하면
System.out.println("imageEntity :: " +imageEntity.toString());
Java
복사
랑 같은거다.
Image.java가서 우클릭 하고
source > Generate toString()누르면
이미 되어있는데 왜 또 하냐고 물어본다.
언제 우리가 처리해줬지 싶냐면 @Data를 넣어주면서 내부적으로 처리가 됐었다.
우선 Yes 눌러주고 보면은
@Override public String toString() { return "Image [id=" + id + ", caption=" + caption + ", postImageUrl=" + postImageUrl + ", user=" + user + ", createDate=" + createDate + "]"; }
Java
복사
user에서 오류가 나고 있는거다.
여기서 user를 제외시켜준다.
@Override public String toString() { return "Image [id=" + id + ", caption=" + caption + ", postImageUrl=" + postImageUrl + ", createDate=" + createDate + "]"; }
Java
복사
이렇게 처리하면 오브젝트를 부를때 user를 부르지않을거다.
이제 오류없이 잘 들어갔다.
해당부분은 이제 필요가 없으니 지워준다.
어차피 함수 자체가 void라 return 해줄 데이터도 없다.
클라이언트가 요청하면은 톰켓안에 내부에 스프링 컨테이너가 있다.
디스패처를 통해 컨트롤러 → 서비스 → 레파지토리 → 영속성 컨테스트 → DB
클라이언트가 요청한 종류에 따라 디스패처가 그에 맞는 컨트롤러로 보내준다.
(ex) user에 대한 요청이면 UserController 이런식으로)
이때 데이터베이스에 접근할 수 있는 세션이 생긴다 (이미지에 2번)
그렇게 레파지토리까지 가고 영속성 컨테스트에 데이터가 있으면 바로 응답
없다면 DB에 응답받아서 알려준다.
응답하면 서비스가 컨트롤러한테 돌려주는데 이 시점에서 세션이 종료된다
그래서 컨트롤러에서 Lazy 로딩 불가능하다. 왜냐면 세션이 종료가 되서 지원로딩을 못한다.
User라는 클래스가 있는데
id
username
images
를 가지고 있는데
images는 다른페이지 꺼니까 FetchType이 만약 Lazy이면은
초반에 User를 셀렉트할때 이미
영속성 컨텍스트에서 user만 들고온다
하지만 EAGER 경우에는 조인해서 바로 들고오니까
이렇게 같이 들어온다.
컨트롤러에서 Lazy 로딩이 불가능하니까 다시 재요청을 하는데
이미 DB에서는 세션이 종료됐으니까 못들어온다고 막아버린다.
application.yml에서
잠깐 false로 바꿔서 확인해보자
user/1 페이지 들어가는거 조차 안된다.
User 클래스안에 컬럼들을 다 갖고와서 영속성 컨테이너에 넣어두고
profile.jsp에서 user만 들고온 상태에서
이게 동작이 안되서 오류가 난다.
open-in-view: false
Java
복사
로 잡혀 있으면 세션이
보라색 구역부분에서 종료가 된다
User에서 만약 EAGER
이미 조인해서 다 들고오는 상태니까
세션이 종류가 되도 오류가 안난다.
다시 open-in-view: true로 돌리고 User도 LAZY로 복구시켜준다
open-in-view: true 를 한다는건 view 단 까지 세션으로 open한다는 뜻이다
세션종료가 이제 컨트롤러 지나고 일어난다. 그러고 Lazy 로딩도 가능하게 되면서 image도 DB에서 줄수 있게 된다
ImageService.java에서 사진업로드 함수에 @Transactional 안줘서 추가해준다
@Transactional을 주는 이유는
우리가 송금서비스를 만들고 그안에 송금이라는 함수도 만들어줬다 치면
2번유저의 머니를 송금업데이트가 성공은 했는데
1번유저의 머니 업데이트에서 실패하게 됐다.
두가지 로직이 하나의 송금서비스가 되기때문에 하나의 트랙잰션(일의 최소단위)라고 한다
@Transactional 붙이면 전원성공이 실패하면 롤백시켜준다. 원상복귀
그래서 데이터베이스의 변형이 일어나는일(Delete, Update)이 있을때는 @Transactional을 붙여주면 좋다
UserService.java에도
@Transactional(readOnly = true)을 붙여주는데
select에도 붙여주면 좋다
레파지토리가 요청했을때 영속성 컨텍스트에 데이터가 없으면 DB에서 응답해서 주고 레파지토리에도 던져질거다.
이 상태에서 레파지토리의 user정보/영속성컨텍스트의 user정보/DB의 user정보 는 지금 동기화가 되어있는 상태다
그때 service에서 username이 ssar에서 cos로 변경하면
한바퀴 돌고 서비스가 끝나는 시점에서 영속성 컨텍스트는 변경된 데이터를 DB에 자동으로 업데이트 쳐준다
그래서 서비스 끝난 시점에서 영속성 컨텍스트는 변경된 오브젝트를 계속 감지한다 → 연산
근데 readOnly = true 로 주면 읽기전용이라 감지(연산)하지 않는다.
그래서 영속성 jpa가 일을 조금 더 적게 할 수 있는 방법이다.

*참고