.. _how_to_datastream: Перебор строк выборки в потоковом режиме (datastream) ======================================================= .. seealso:: :ref:`Работа с данными ` :java:type:`CoreDataStream` Для работы с большими списками введён режим потокового перебора строк выборки, который позволяет экономить память сервера. С его помощью можно перебирать строки табличного и древовидного представления, а также списка с группировкой. В данном режиме строки отдаются в том же порядке, что и при использовании :java:type:`CoreSelection#next()`, но, в определённый момент времени, пройденные строки выгружаются из памяти. Использование ---------------- Навигация ++++++++++ 1. Для входа в потоковый режим необходимо воспользоваться методом :java:type:`CoreSelection#openDataStream()`, который откроет датастрим и вернёт его экземпляр .. note:: Рекомендуется использовать интерфейс :java:type:`CoreDataStream` в блоках ``try-with-resources`` или ``Using``, потому что он расширяет ``java.lang.AutoCloseable`` 2. Для перебора строк используется метод :java:type:`CoreDataStream#nextToken()`, который вернёт один из следующих :java:type:`токенов `: - **BOF**, первый токен после открытия датастрима - **EOF**, токен окончания датастрима, идущий после полного перебора строк датастора - **ROW**, токен строки - **GROUP**, токен строки группировки - **RIGHT**, токен сдвига вправо, т.е. переход на один уровень вложенности ниже в иерархии дерева - **LEFT**, токен сдвига влево, т.е. переход на один уровень вложенности выше в иерархии дерева .. note:: Токены **LEFT** и **RIGHT** предназначены для возможности отражения структуры древовидного списка без запросов информации о предках/потомках узлов 3. Для проверки наличия следующего токена используется метод :java:type:`CoreDataStream#hasNextToken()` .. attention:: Если вы не использовали датастрим в рамках блоков ``try-with-resources`` или ``Using``, то необходимо закрывать его самостоятельно с помощью :java:type:`CoreDataStream#close()` **Пример 1.** Для простого списка из трёх строк вернётся следующая последовательность токенов: **BOF**, **ROW**, **ROW**, **ROW**, **EOF**. **Пример 2.** Для списка с группировкой со следующего изображения .. image:: group_grid_example.png вернётся последовательность токенов: **BOF**, **GROUP**, **RIGHT**, **ROW**, **ROW**, **LEFT**, **GROUP**, **RIGHT**, **ROW**, **EOF**. **Пример 3.** Для древовидного списка со следующего изображения .. image:: tree_grid_example.png вернётся последовательность токенов: **BOF**, **ROW**, **RIGHT**, **ROW**, **ROW**, **RIGHT**, **ROW**, **LEFT**, **LEFT**, **ROW**, **EOF**. Запрос данных ++++++++++++++++ 1. Если текущим токеном является **ROW**, то для получения значения из колонки для текущей строки необходимо использовать один из следующих методов: - :java:type:`CoreDataStream#getCellValue(String)`, который вернёт значение колонки с переданным именем .. note:: Результат вызова данного метода аналогичен результату вызова :java:type:`CoreSelectionShared#getVar(String)` - :java:type:`CoreDataStream#getCellValue(int)`, который вернёт значение колонки по переданному индексу. Индексы соответствуют порядку атрибутов, указанному в операции GetSqlText 2. Если текущим токеном является **GROUP**, то с помощью вызова: - :java:type:`CoreDataStream#getGroupFieldName` можно получить имя атрибута, по которому происходит группировка - :java:type:`CoreDataStream#getGroupCaption` можно получить значение, по которому сформирована группа .. _datastream_excel_export: Пример использования для формирования excel-документа -------------------------------------------------------- В следующем примере используется функционал Scala версии 2.13.10 и библиотеки `Apache POI `_ для создания и наполнения excel-документа .. code-block:: scala import org.apache.poi.ss.usermodel.HorizontalAlignment import org.apache.poi.xssf.streaming.{SXSSFSheet, SXSSFWorkbook} import ru.bitec.app.gtk.gl.selection.{Attrs, Selection} import ru.bitec.gtk.core.datastore.{CoreDataStream, CoreDataStreamTokenEnum} import java.nio.file.{Path, Paths} import java.io.FileOutputStream import scala.util.Using protected var workbook: SXSSFWorkbook protected var sheet: SXSSFSheet protected def streamExport(selection: Selection, fileName: String): Unit = { workbook = new SXSSFWorkbook() sheet = workbook.createSheet() Using (selection.openDataStream) { dataStream => fillHeaderRow(selection.attrs()) val tempExcelFilePath = Paths.get(System.getProperty("java.io.tmpdir"), fileName) fillWorkbook(dataStream, selection.attrs(), tempExcelFilePath) } } private def fillHeaderRow(attrs: Attrs): Unit = { val headerCellStyle = workbook.createCellStyle val headerFont = workbook.createFont headerFont.setBold(true) headerCellStyle.setFont(headerFont) headerCellStyle.setAlignment(HorizontalAlignment.CENTER) val headerRow = sheet.createRow(0) for ((attr, colIndex) <- attrs.toList.zipWithIndex) { val cell = headerRow.createCell(colIndex) cell.setCellStyle(headerCellStyle) cell.setCellValue(attr.caption) } } private def fillWorkbook(dataStream: CoreDataStream, attrs: Attrs, filePath: Path): Unit = { var rowNum = 1 var currentDepth = 0 while (dataStream.hasNextToken) { val token = dataStream.nextToken() token match { case CoreDataStreamTokenEnum.Right => currentDepth += 1 case CoreDataStreamTokenEnum.Left => currentDepth -= 1 case CoreDataStreamTokenEnum.Row => fillSheetRow(dataStream, attrs, rowNum, currentDepth) rowNum += 1 case CoreDataStreamTokenEnum.Eof => Using (new FileOutputStream(filePath.toString)) { out => workbook.write(out) } workbook.dispose() } } } private def fillSheetRow(dataStream: CoreDataStream, attrs: Attrs, rowNum: Int, rowDepth: Int): Unit = { val row = sheet.createRow(rowNum) for ((attr, colIndex) <- attrs.toList.zipWithIndex) { val cell = row.createCell(colIndex) cell.setCellValue(dataStream.getCellValue(attr.name).toString) } }