Перейти к основному содержанию
`Ctrl`+`K`

# Принципы работы commit и flush

## Применение Session.commit, Session.commitWork, flush и flush(true)

`session.commit` - применяет изменения сессии к базе данных с успешным завершением транзакции, очищает транзакционный кэш после операции, обнуляет счетчик загруженных ячеек.

`session.commitWork` - применяет изменения сессии к базе данных без завершения транзакции, очищает транзакционный кэш после операции, обнуляет счетчик загруженных ячеек.

`session.commitWorkAuto` - используется при обработке больших объемов данных, чтобы избежать превышения лимита загружаемых ячеек. Работает аналогично commitWork, но имеет параметр nPercentTx и отрабатывает только, когда количество используемых ячеек достигает указанного в параметре процента от лимита. (По умолчанию 50% от лимита ячеек)

`session.flush` - применяет изменения сессии к базе данных без завершения транзакции. Есть параметр cleanTransactCache, если true очищает транзакционный кэш (загруженные объекты) после операции, обнуляет счетчик загруженных ячеек. Если false (по умолчанию), то происходит только синхронизация данных в БД, загруженные объекты остаются в кеше.

Внимание

Рекомендуется использовать session.commitWork вместо session.flush(true)

`session.rollback()` - откатывает текущую транзакцию и полностью очищает память.

### Оптимальная стратегия коммитов:

PostgreSQL плохо работает с длинными транзакциями, так как:

- Долгие транзакции удерживают соединения и могут привести к нехватке пула соединений.

- Долгая незавершенная транзакция может блокировать работу других пользователей.

Поэтому рекомендуется использовать коммиты:

- по окончанию логически завершенного блока операций с изменениями в данных;

- перед открытием модального окна;

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

При обработке больших объемов данных, чтобы избежать превышения лимита загружаемых ячеек, надо сбрасывать пачками данные в БД с очисткой транзакционного кэша при помощи `commitWorkAuto`.

Пример:

          for (rv <- new ASelect {
            val id: NLongColumn = asNLong("id")
            val idBalAcc: NLongColumn = asNLong("idBalAcc")
            val nSum: NNumberColumn = asNNumber("nSum")
            SQL(sQuery)
            on("dExecDate" -> dExecDate, "idaDep" -> LongPgArray(idaDep), "idaEmployee" -> LongPgArray(idaEmployee), "idaAcc"-> LongPgArray(idaAcc.toList), "idBisObj" -> idBisObj, "idDepOwner" -> idDepOwner)
          }) {
            insertByParent(Asf_InventoryApi().load(idpInventory)) :/ { rop =>
              setnOrder(rop, counter.nn)
              setidInvNumb(rop, rv.id())
              setidAcc(rop, rv.idBalAcc())
              setnSum(rop, rv.nSum())
              setnQtyRemain(rop, 1.nn)
              rop
            }
            session.commitWorkAuto()
          }

          session.commitWork()

## Отличия commit и flush

Действие | flush() | flush(true) | commit() | commitWork()
--- | --- | --- | --- | ---
Запись в БД | Да | Да | Да | Да
Фиксация транзакции | Нет | Нет | Да | Нет
Очистка памяти | Нет | Частично | Да | Да
Возможность отката | Да | Да | Нет | Да

## Очистка кэша без коммита

Для очистки кеша и синхронизации данных с БД рекомендуется использовать метод commitWork(). Он полезен в случаях, когда:

- Требуется освободить ресурсы без изменения данных в БД.

- Обрабатывается большой объем данных и важно избежать накопления записей в памяти.

- Необходимо минимизировать риск потери транзакционной целостности из-за раннего коммита.

Пример использования:

Загрузка объектов из БД частями. Используя реляционный запрос получает значения первичных ключей для массовой прогрузки объектов в память. Количество одновременно загружаемых объектов указывает в параметре chunkSize. Разработчик должен сам очищать память (вызывать commitWorkAuto(), commitWork() или commit()) внутри переданного обработчика.

      {{{
      val ids: List[NLong] = ...

      Btk_BulkProcessPkg().chunkedQuery(
        SomeClassApi(),
        500,
        "select id from SomeClass where id = Any({ids})",
        Seq("ids" -> LongPgArray(ids)),
        Seq(Btk_ObjectAta) // прогрзука коллекций
      ) { rops =>
        rops.foreach { rop =>
          //обработка объекта
          SomeClassApi().setsText(rop, svSomeText)
          //очистка памяти
          session.commitWorkAuto()
        }
        //очистка памяти
        session.commitWork()
      }
      }}}

## Автокоммиты

Автокоммит может происходить в нескольких случаях:

- После выполнения пользовательских операций, если они вызвали изменения в базе данных. Операция выполняется, вызывается `flush()` для записи изменений в БД, а затем происходит `commit()`.

- При обновлении данных `refresh()`. Если в памяти есть несохраненные изменения и вызывается `refresh()`, система сначала выполняет `flush()`, фиксирует изменения в памяти и затем отправляет запрос в БД, что приводит к коммиту.

- На `onRefresh()` или `onRefreshExt()` c реляционным запросом. На открытии выборки происходит коммит, если используется реляционный запрос на `onRefresh` без аннотации `@FlushBefore(mode = FlushBeforeMode.Disabled)`. Из-за чего могут возникать ситуации, когда происходит неочевидный коммит при открытии отображения с закладкой или выпадающим списком, на которых не указана аннотация.

Примечание

Автокоммит для реляционных запросов на `onRefresh()` или `onRefreshExt()` происходит, чтобы в БД отобразились внесенный пользователем изменения и выполняемый запрос мог их считать.