Composite
Posted on March 13, 2020
만약 Mybatis
와 Cursor
를 한글로 조회하면 100이면 100 "오라클 커서"가 조회될 것이다.
압도적으로 이런 용도로 많이 쓰기 때문에 아마 대부분 Mybatis 에 Cursor
인터페이스가 있다는 것 조차 모르는 사람들 많을 것이다.
그래서 간단하게 쓴다.
물론 어자피 공식 설명서 봐도 되긴 하는데... 아무도 Cursor 사용법의 짤막한 한줄조차 번역 안되어 있더라...
대단하다...
A
Cursor
offers the same results as aList
, except it fetches data lazily using an Iterator.
뭐 굳이 해석하자면...
Cursor
는List
와 같은 결과물을 제출하지만, 데이터를 Iterator 를 사용해서 열거할 때만 가져온다.
즉, Lazy의 뜻이 아는 개발자라면, 특히 대용량 로우를 불러와야 하는 개발자라면 한줄기 빛이나 다름없다.
물론 기존처럼 ResultHandler
인터페이스를 구현해서 대용량 로우 불러오기를 꾀할 수 있는데, Cursor
는 List
대신에 넣으면 된다는 간편한 차이점이 있다는 거.
@Repository
public interface PersonMapper {
// 그냥 리스트 불러올 때
List<Person> selectPersonList(Map<String, Object> params);
// Mybatis 3.2.4 이전까지 대용량 리스트 불러올 때
void selectPersonListHuge(Map<String, Object> params, ResultHandler handler);
// Mybatis 3.2.4 이후 대용량 리스트 불러올 때
Cursor<Person> selectPersonListLazy(Map<String, Object> params);
}
그렇다면 사용법은 어떠한가? Cursor<T>
인터페이스는 Iterable<T>
를 확장하기 때문에, for
문에서 foreach
처럼 작성할 수 있다. 또한, 잊지 말아야 할 건, Closable
인터페이스도 확장하기 때문에, try
문에 선언하거나, 쓰기 싫으면 close()
메소드로 닫아야 한다. 공식 설명서에서 소개한 사용법은 아래와 같다.
try (Cursor<Person> lazyList = mapper.selectPersonListLazy(params)) {
for (Person person : lazyList) {
// 여기에 Person 가지고 뭐 할겨?
}
}
Javadoc 을 참조하면 사실 별 거 없다.
그나마 유용한 메소드가 getCurrentIndex()
메소드인데, 현재 가져온 행 번호를 가져온다. 물론 자바답게 0부터 시작한다.
자, 소개는 여기까지.
아, 이건 내 경험인데, 내잘못인지 버그인지 모르겠지만, jdbc 로깅한답시고 추가한 log4jdbc 하고 연계했더니 Cursor<T>
리턴은 되는데 항상 데이터 반환이 없다. 리스트가 없다고.
어디쪽 문제인지 모르겠지만, log4jdbc
는 더 이상 개발이 없고, mybatis
로깅도 나쁘진 않기 때문에, 신규 프로젝트에 Mybatis 추가할 경우 log4jdbc
연계는 지양하는 게 좋을 것 같다.
혹시 이거 해결한 분 있으면 제보해 주시면 감수광.
2020-03-23 업데이트
Cursor<T>
의 올바른 사용법
위에 log4jdbc
와 충돌한 줄 알았던 내 자신을 반성하는 의미이고, MyBatis 공식 문서에 잘 나와 있지도 않아 해맸는데, 알고보니 트랜잭션 내에 있어야 한다는 것.
즉, Cursor<T>
사용 시, 트랜잭션 내에 있어야 한다는 것이다. Spring 을 예를 들면,
@Transactional // <- 이거 이거 이거 이거 이거 이거 이거 이거 이거 이거
public List<Person> getList() {
try (Cursor<Person> cursor = mapper.getPersonListByCursor()) {
for (Person person : cursor) {
// ...
}
}
return ...
}
이렇게 @Trasactional
어노테이션이라던가, TransactionTemplate
클래스 또는 PlatformTransactionManager.getTransaction(...)
메소드를 통해 스레드 내 트랜잭션을 활성화해야 작동한다는 것이었다...
내가 새로 Spring Webflux 기반의 프로젝트 구성하면서 알게 되었다... 하아...
게다가, 트랜잭션 내에 있어야 작동하는 특성 상, 트랜잭션 밖에 있거나, 트랜잭션 스레드 밖에서 for
문 등으로 열거하는 행위는 안 될 것이다. 예를 들어 Stream
으로 변환해서 쓴다던지... 이런 게 트랜잭션 내에서만 되고 그 밖에서는 안된다는 것이다.
밖으로 빼내려면 List
로 돌리거나, 아예 List
로 반환해야 하는 상황이다.
그래서 reactor
와 같이 이용할 때는 이걸 blocking
으로 대응해야 한다는 아쉬움이 남는다.
여담으로, Mybatis 개발자가 R2DBC 대응을 한다고는 했는데... 언제가 될 지는 모른다.
만약 Webflux를 쓴다면 DB 연동의 해답은 아직 없다는 점이 아쉬운 점 되겠다. R2DBC도 아직 베타 단계고...
Posted on March 13, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.