Проблема экспорта большого количества данных из базы данных в .csv с помощью Java - PullRequest
2 голосов
/ 10 августа 2011

Я, спасибо за внимание.

Я хочу экспортировать много данных, действительно много данных (6 миллионов строк) в файл .csv, используя Java. Приложение представляет собой свинг-приложение с JPA, использующее toplink (ojdbc14).

Я пытался использовать:

BufferedWriter RandomAccessFile FileChannel

и т. Д., Но потребление памяти остается очень высоким, что приводит к исключению кучи Java из памяти, хотя я установил максимальный размер кучи в 800 м (-Xmx800m).

Моя последняя версия кода соуса:

...(more lines of code)

FileChannel channel = getRandomAccessFile(tempFile).getChannel();
Object[][] data = pag.getRawData(); //Database data in a multidimentional array

            for (int j = 0; j < data.length; j++) {
                write(data[j], channel); //write data[j] (an array) into the channel
                freeStringLine(data[j]); //data[j] is an array, this method sets all positions =null
                data[j] = null;//sets reference in null
            }

            channel.force(false); //force writing in file system (HD)
            channel.close(); //Close the channel
            pag = null; 

...(more lines of code)

 private void write(Object[] row, FileChannel channel) throws DatabaseException {
    if (byteBuff == null) {
        byteBuff = ByteBuffer.allocateDirect(1024 * 1024);
    }
    for (int j = 0; j < row.length; j++) {
        if (j < row.length - 1) {
            if (row[j] != null) {
                byteBuff.put(row[j].toString().getBytes());
            }
            byteBuff.put(SPLITER_BYTES);
        } else {
            if (row[j] != null) {
                byteBuff.put(row[j].toString().getBytes());
            }
        }
    }
    byteBuff.put("\n".toString().getBytes());        
    byteBuff.flip();
    try {
        channel.write(byteBuff);
    } catch (IOException ex) {
        throw new DatabaseException("Imposible escribir en archivo temporal de exportación : " + ex.getMessage(), ex.getCause());
    }
    byteBuff.clear();
}

Будучи 6 миллионами строк, я не хочу хранить эти данные в памяти во время создания файла. Я сделал много временных файлов (по 5000 строк в каждом), и в конце процесса добавляю все эти временные файлы в один, используя два FileChannel. Тем не менее, исключение из-за нехватки памяти запускается до присоединения.

У вас сейчас другая стратегия для экспорта большого количества данных?

Большое спасибо за любой ответ. Извините за мой английский, я улучшаю xD

1 Ответ

3 голосов
/ 10 августа 2011

Ответ заключается в использовании «потокового» подхода, то есть чтения одной строки, записи одной строки при прокрутке набора данных.Вам нужно получить результат запроса в виде курсора и выполнить итерацию по нему, а не получить весь набор результатов.

В JPA используйте код, подобный следующему:

ScrollableResults cursor = session.createQuery("from SomeEntity x").scroll();

while (cursor.next()) {
    writeToFile(cursor);
}

Это означает, чтоу вас есть только одна строка в памяти за раз, которая полностью масштабируется для любого количества строк и использует минимальное количество памяти (в любом случае, это быстрее).

Получение всех строк одновременно в наборе результатов - это удобный подход, которыйработает для небольшого набора результатов (что чаще всего), но, как обычно, удобство обходится дорого и не работает во всех ситуациях.

...