인프런 커뮤니티 질문&답변

Truestar님의 프로필 이미지

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

회원 목록 조회

리포지토리 반환값을 List 에서 Stream 으로 바꿔 받는 과정에 문제가 생겨 남겨보아요

해결된 질문

20.09.22 06:43 작성

·

1.5K

13

안녕하세요 김영한강사님.
알찬 강의 열심히 응용중에 문제가 생겼습니다.

다음처럼 Repository  반환값을 List<엔티티> 에서
Stream<엔티티> 로 했을 경우
Controller 에서 엔티티를 받아오지 못하더라구요

 MemberRepository . findAll() ---> Stream<Member> 반환


em
.createQuery("select m from Member m", Member.class)
.getResultStream()


// MemberController 의 addAttr 직전 Stream을 List변환

memberService.findMembers().collect(Collectors.toList());

error1 : The object is already closed [90007-199]
error2 : could not advance using next()

직감으로는, 닫혔다는것이.. Stream 이라 불변객체여서 변경이 안된다는 이야기로 보이는데
List<Member> 로 싹 바꿔야 되나 고민하고 있습니다.

--------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------

LIst<엔티티> 로 바꾸니 문제없이 출력이 되었습니다.

원인이 createQeury().getResultStream() 이었는데
결국 Proxy 를 Stream 에 담은 상태로
불변성이 보장되어서
하이버네이트가 내부에 RealEntity 값을 심어야 되는데
이작업을 할 수 없으니 애러가 나는것이 맞는건가요?

맞다면, Stream은 어느시점에 사용하는것이 좋은가요..?

답변 6

17

김영한님의 프로필 이미지
김영한
지식공유자

2020. 09. 23. 22:38

안녕하세요. Truestar님^^

커서 내용은 아시겠지만 혹시 모르는 분들을 위해서 추가로 설명해둘께요.

List로 바로 받는 일반적인 쿼리의 경우 데이터베이스가 결과물을 애플리케이션에 한번에 모두 전달해줍니다. 그래서 데이터가 100만건이면 모든 데이터가 메모리에 올라오면서 OOM이 발생하겠지요. 그래서 우리가 페이징 등으로 조금씩 나누어서 조회합니다.

그런데 정말 100만건을 한번에 조회하고 싶으면 커서라는 기능을 사용하면 됩니다. 이 기능으로 조회하면 데이터베이스는 결과를 내부에 저장해두고 애플리케이션에서 달라고 할 때 마다 조금씩 전송해줍니다. 이렇게 되려면 중요한게 애플리케이션과 DB간에 커넥션이 계속 연결되어있어야 하고, 추가로 DB도 해당 데이터를 어딘가에 보관해두어야 합니다.

이렇게 하면 애플리케이션은 조금씩 데이터를 받기 때문에 OOM이 발생하지 않고, 사용한 데이터는 GC 처리하면 됩니다.

이 기능은 실시간 애플리케이션에서는 보통 사용하지 않고, 대량의 데이터를 한번에 처리할 때 사용합니다.

getResultStream()을 사용하면 바로 이 커서 기능이 동작합니다.

이 기능을 통해서 스트림을 받아서 스트림을 하나씩 호출하면 내부에서 Jdbc 드라이버의 next()를 호출해서 데이터를 가저옵니다. 바로 커서 기능이 동작하는 것이지요.

그런데 여기서! The object is already closed라는 뜻은 뭔가 이 커서 데이터가 끊어저 버렸다는 뜻입니다.

이 커서가 끊어지는 것은 데이터베이스마다 다르지만 일반적으로 커넥션이 종료되거나, 또는 트랜잭션이 커밋되어 버리는 경우입니다. 트랜잭션 커밋의 경우 데이터베이스 마다 다릅니다. 트랜잭션이 커밋되어도 커서를 유지하는 옵션을 제공하기도 합니다.

Truestar님의 경우는 바로 서비스 계층에서 트랜잭션이 커밋되어서 커서가 끊어저버려서 그렇습니다. 아마 서비스 계층에서 스프림을 돌리면 되고, 트랜잭션이 끝난 컨트롤러에서 스트림을 동작해보면 해당 오류가 발생할꺼에요^^

그런데 놀랍게도 ㅋㅋ H2 데이터베이스는 사실 커서를 지원하지 않습니다. 그냥 커서를 지원하는 것 처럼 보이도록 시뮬레이션 한다고 이해하시면 됩니다.

커서는 데이터베이스 리소스를 오랜시간 많이 잡아먹기 때문에 대량의 데이터를 효과적으로 처리해야 하는 경우에만 사용하는 것을 권장합니다.

도움이 되셨길 바래요^^

2

김영한님의 프로필 이미지
김영한
지식공유자

2020. 09. 23. 22:54

멋진 질문을 남겨주신 덕분에 빡센 답변을 드렸습니다. ㅋㅋㅋㅋㅋ 화이팅!

2

Truestar님의 프로필 이미지
Truestar
질문자

2020. 09. 23. 22:53

강의1편 정도를 만드셔도 될법한 양질의 답안을 주셨네요ㅠ 감동입니다.

많이배워갑니다! 감사해요

1

Truestar님의 프로필 이미지
Truestar
질문자

2020. 09. 23. 23:02

제가 실험정신이 강하다보니 그런 부담이 있었군요.. 제가 이것저것 해보는 응용맨이라 죄송합니다ㅎㅎㅎㅎ

강사님도 힘내셔요^^

늦은시간에 감사합니다^^

1

김영한님의 프로필 이미지
김영한
지식공유자

2020. 09. 22. 18:34

안녕하세요. Truestar님

해당 오류가 발생하는 버전으로 전체 프로젝트를 압축해서 올려주세요.

그리고 추가로 제가 답을 준비하는 동안 데이터베이스 커서와 페이징의 차이에 관해서 학습하고 계시면 좋겠습니다^^!

0

Truestar님의 프로필 이미지
Truestar
질문자

2020. 09. 23. 19:04

네이버에 ZIP 파일 전송이 막힌듯합니다.
클라우드 폴더 공유링크를 이메일로 보냈습니다.

next() 는 DB 튜플 리스트의 커서 이동 기능 이라고 알고있는데
원인이 도통 감이 오질 않아요..

Stream 과의 연계는 강의와 관련이 없는데도 친절하신 답변 너무나 감사드립니다.

Truestar님의 프로필 이미지

작성한 질문수

질문하기