작성
·
704
3
안녕하세요.
AbstractCursorItemReader#doOpen() -> JdbcCursorItemReader#openCursor()에서
......
this.rs = preparedStatement.executeQuery();
......
해당 시점에 쿼리가 실행이 되고, 이후 커넥션을 쭉 유지한채
AbstractCursorItemReader#doRead() -> JdbcCursorItemReader#readCursor(rs, currentRow)가 메소드가 호출될 때마다 DB에서 1row씩 데이터를 꺼내오는 방식으로 동작하는 것일까요?
openCursor에서 쿼리 실행 후 메모리에 쿼리 수행결과를 가지고 있다가 doRead() -> readCursor()를 수행하는건지 혼란이 와서 질문 드립니다.
JpaCursorItemReader의 경우는
JpaCursorItemReader#doOpen()에서 쿼리 실행 후 결과를 메모리에 가지고 있다가 이때는 DB 커넥션 유지하지 않고, doRead() 호출시 한로우씩 반환하는 식으로 동작하는게 맞나요?
답변 2
4
네
전자가 맞습니다.
openCursor 에서는 DB 와 커넥션을 맺은 상태에서 커서를 처음 위치에 놓게 되고
this.rs = preparedStatement.executeQuery();
readCursor 에서는 rs.next() 를 호출해서 실제 DB 에서 해당 커서의 레코드 즉 1개의 row 를 가지고 오게 됩니다.
protected T doRead() throws Exception {
if (rs == null) {
throw new ReaderNotOpenException("Reader must be open before it can be read.");
}
try {
if (!rs.next()) {
return null;
}
int currentRow = getCurrentItemCount();
T item = readCursor(rs, currentRow);
verifyCursorPosition(currentRow);
return item;
}
catch (SQLException se) {
throw getExceptionTranslator().translate("Attempt to process next row failed", getSql(), se);
}
}
위에서
re.next() 할 때 DB 호출이 이루어지고 그 결과를 담은 rs 를 readCursor(rs, currentRow) 에 담아 호출하게 되면
protected T readCursor(ResultSet rs, int currentRow) throws SQLException {
return rowMapper.mapRow(rs, currentRow);
}
위의 rowMapper 에 rs 에 담긴 데이터를 매핑하게 됩니다.
결론적으로 cursor 방식은 rs 를 호출할 때마다 DB 의 커서를 이동하면서 데이터를 전달하는 방식이라 할 수 있습니다.
다만 성능을 위해 cursor 방식으로 데이터를 불러올때 FetchSize 를 설정하게 되면 그 크기만큼 메모리로 가지고 온 다음 데이터를 읽을 수 있습니다.
적절한 FetchSize 를 설정하는 것이 좋습니다.
0
안녕하세요 선생님.
비슷한 고민을 하다가 이 질문을 찾아오게 되었는데요,
예를들어 1000 건의 데이터가 있고, 100건씩 데이터를 처리하려고 하더라도
실제로 SQL 자체는 단 한번 실행되고,
데이터베이스 서버에서 해당 resultSet 을 가지고있으면서
cursor 를 batch application 으로 반환하고,
cursor 를 통해서 필요시 DB server 에 실제 데이터를 요청한다고 이해했습니다. (그리고 받은걸 메모리에 올려서 작업)
그러면 결국 DB 서버에서는 그 많은 데이터를 전부 메모리에 올려놓고 batch 작업이 종료될 때까지 유지해야한다고 이해했는데, 혹시 제가 이해한게 맞나요?!
이해가 되었습니다 감사합니다