[에러노트] Cause: java.sql.SQLSyntaxErrorException: ORA-01795: 목록에 지정 가능한 식의 최대수는 1000 입니다
현상
데이터가 많은 리스트를 엑셀 다운로드할 때 해당 에러가 발생했다.
원인
해당 에러가 발생한 쿼리를 보니 IN 절에 userOid를 데이터 리스트 갯수만큼 넣고 있었다.
화면에서 검색 조건없이 그대로 엑셀 다운로드를 누르면 총 유저 리스트인 13만건 이상 userOid가 IN절에 ? 로 들어가는 것이다.
즉, 마이바티스에 ?로 들어가는 부분이 1000개를 넘으면 해당 에러가 발생된다.
해결 방법
리마커블 base util 패키지의 ListUtils.*partitionOnSize
()를 사용한다.*
public abstract class ListUtils {
public ListUtils() {
}
public static <T> Collection<List<T>> **partitionOnSize**(List<T> inputList, int size) {
AtomicInteger counter = new AtomicInteger(0);
return ((Map)inputList.stream().collect(Collectors.groupingBy((input) -> {
return counter.getAndIncrement() / size;
}))).values();
}
}
사용예시
List를 size만큼 분리할 경우 사용
List<SyncUserInfo> userList = syncUserTargetDAO.listAll();
Collection<List<SyncUserInfo>> list = **partitionBasedOnSize**(userList, 1000);
적용
해당 쿼리를 호출하는 commonList
protected List<UserInfo> commonList( List<UserInfo> userList, UserCnd cnd ) {
if ( CollectionUtils.isEmpty( userList ) ) {
return CollectionUtils.emptyList();
}
List<String> userOids = getUserOidList( userList );
if ( CollectionUtils.isEmpty( userOids ) ) {
return CollectionUtils.emptyList();
}
... (중략)
/** 사용자 상태값 채울지 여부 */
if ( cnd.isFillUserStatus() ) {
**// 100개 이상인 경우 나눠서 처리
if ( userOids.size() > SystemConstants.LIST_PARTITION_LIMIT ) {
Collection<List<String>> partitionOidList = ListUtils.partitionOnSize( userOids, SystemConstants.LIST_PARTITION_LIMIT );
partitionOidList.parallelStream().forEach( partition -> userStatusBLO.fillUserStatus ( userList, partition ) );
}**
else {
userStatusBLO.fillUserStatus ( userList, userOids );
}
}
return userList;
}
- ?표로 들어갈 userOids가 100개 이상인지 (SystemConstants.LIST_PARTITION_LIMIT) 체크한다. 1000개가 아닌 100개인 이유는 성능 테스트 결과 100개로 잘라서 반복했을때 가장 성능이 좋았기 때문이다.
- 100개 이상인 경우 ListUtils.partitionOnSize()로 리스트를 분배한다.
- 분배된 리스트(partitionOidList)를 돌면서 fillUserStatus()를 호출한다
- 분배된 리스트에는 최대 100개의 userOid가 들어있기 때문에 에러가 나지 않는다.
-
테스트코드
@Test public void 벌크_셀렉트_for_엑셀_다운로드_테스트 (){ ArrayList<String> userTypeList = new ArrayList<>(); userTypeList.add( "USSFGG04" ); ArrayList<String> statusTypeList = new ArrayList<>(); statusTypeList.add( "U" ); statusTypeList.add( "W" ); UserCnd cnd = new UserCnd(); cnd.setFillArea( true ); cnd.addOrderByList( "U.JOIN_DATE DESC NULLS LAST" ); cnd.setUserTypeList( userTypeList ); cnd.setStatusTypeList( statusTypeList ); List<UserInfo> userList = userBLO.listAll( cnd ); List<String> userOids = userBLO.getUserOidList( userList ); System.out.println( "userOids size!! -> " + userOids.size() ); // userOids가 1000개 이상이면 에러 발생 // ListUtils.partitionOnSize() if ( userOids.size() > SystemConstants.LIST_PARTITION_LIMIT ) { Collection<List<String>> partitionOidList = ListUtils.partitionOnSize( userOids, SystemConstants.LIST_PARTITION_LIMIT ); System.out.println( "======================= partitionList!!!!!!!!!!!!!!!!!!!!!" ); partitionOidList.parallelStream().forEach( data -> System.out.println( data ) ); System.out.println( "======================= partitionList!!!!!!!!!!!!!!!!!!!!!" ); partitionOidList.parallelStream().forEach( partition -> userStatusBLO.fillUserStatus ( userList, partition ) ); } }
관련 용어 및 기능
참고
이게 리마커블 버전인듯?
질문:
Is there a common Java utility to break a list into batches?
댓글남기기