Сессия приложения#
Сессия приложения создается на поток прикладной бизнес логики и предоставляет доступ к сессии базы данных, EclipseLink кэшу, серверу приложения. А так же содержит необходимые инъекции зависимости для работы прикладной бизнес логики.
Сессия приложения создается:
на каждую mdi форму открытую в приложении
на rest запрос к серверу приложения
Примечание
Для ускорения rest запросов возможна настройка пула сессий
Сессия приложения не привязана к сессии базы данных. Сессия базы данных выделяется на запрос либо на транзакцию.
Каждый контроллер прикладной бизнес логики содержит контекст сессии который представляет из себя фасад для быстрого доступа к сессии приложения и стандартным языковым примитивам фреймворка.
Сессия базы данных#
Физическое соединение с базой.
Фреймворк использует механизм коротких транзакций в БД. Сессия БД выделяется для получения или изменения данных и возвращается в пул после окончания работы с данными.
Число прикладных сессий не равно числу соединений с БД.
Сессия базы данных выделяется из пула при:
Начале транзакции
Выполнении запроса из прикладного кода.
Рабочее пространство (workspace)#
Рабочее пространство содержит транзакционный кэш чтения, а так же перечень изменений для отправки в базу данных.
Рабочее пространство создается при любом действии с базой данных. Чтение, создание\удаление\обновление. И удаляется при commit или rollback.
Точки сохранения#
При создании точки сохранения, происходит сброс всех изменений из рабочего пространства в базу данных. При откате к точке сохранения, рабочее пространство удаляется.
Пользовательский сеанс#
Пользовательский сеанс начинается в момент открытия формы в новой сессии, и заканчивается при закрытии формы.
Примечание
При открытии модального окна в той же сессии, пользовательские блокировки работают в сеансе дочерней формы.
Пользовательские сеансы можно просмотреть:
Меню приложения > помощь > блокировки
Завершение сессий из прикладного кода#
В разделе описывается завершение рабочей сессии и сессии приложения.
Рабочая сессия представляет работу пользователя на сервере приложений. Она регистрируется на узле кластера и определяется идентификатором sid. Для завершения рабочей сессии из прикладного кода используется Btk_WorkSessionPkg#killSession().
Сессия приложения — это контекст выполнения прикладной бизнес-логики. Такая сессия создаётся, например, для открытой MDI-формы или REST-запроса и предоставляет прикладному коду доступ к данным, транзакции и сервисам приложения. Для её закрытия используются Session#close() и SessionUtils#closeSession().
При использовании описанных ниже методов активная транзакция всегда завершается: изменения фиксируются или откатываются.
Закрытие рабочей сессии#
Btk_WorkSessionPkg#killSession()
Метод завершает выбранную рабочую сессию. Сессия определяется по узлу кластера и идентификатору sid.
После завершения рабочей сессии у пользователя открывается форма входа в систему.
Btk_WorkSessionPkg().killSession(clusterNode, sid)
где:
clusterNode— имя узла кластера, на котором открыта рабочая сессия;sid— идентификатор завершаемой рабочей сессии.
Для массового завершения сессий сформируйте список имён пользователей, получите их активные рабочие сессии и вызовите killSession() для каждой найденной сессии:
val savUser = <список имён пользователей, сессии которых нужно закрыть>
val targetSessions = Btk_WorkSessionPkg()
.getWorkingSessionByCluster(true)
.filter(s =>
s.user.isNotNullOrEmpty && savUser.contains(s.user)
)
targetSessions.foreach { session =>
Btk_WorkSessionPkg().killSession(session.clusterNode, session.sid)
}
В примере завершаются все рабочие сессии пользователей, имена которых содержатся в savUser.
Закрытие сессии приложения#
ru.bitec.app.gtk.eclipse.Session#close()
Метод предназначен для закрытия текущей сессии приложения из прикладного кода. Это прикладная версия CoreSession#close().
При вызове метода сессия закрывается, связанные с ней ресурсы освобождаются, а сама сессия дерегистрируется из JdbcContext рабочего сеанса. JdbcContext используется для отслеживания JDBC-соединений, выделенных для формы, рабочей сессии и GTK-сессии.
Активная транзакция при закрытии сессии всегда завершается. Метод не принимает параметров для явного выбора между фиксацией и откатом транзакции.
SessionUtils#closeSession()
Метод является обёрткой над ru.bitec.app.gtk.eclipse.Session#close(). Перед закрытием сессии он выполняет фиксацию или откат транзакции, после чего вызывает ru.bitec.app.gtk.eclipse.Session#close().
При вызове без параметра commit выполняется откат транзакции:
SessionUtils.closeSession(session)
Для фиксации транзакции перед закрытием передайте commit = true:
SessionUtils.closeSession(session, commit = true)
Сессия закрывается и при ошибке фиксации или отката транзакции. Возникшее исключение передаётся вызывающему коду.
Пользовательская блокировка#
Пользовательская блокировка позволяет блокировать бизнес-объект вне транзакции базы данных. Такая блокировка работает в разрезе пользовательского сеанса. Элементы заблокированного бизнес-объекта не могут быть изменены каким-либо другим пользовательским сеансом.
Пользовательскую блокировку можно снять, при этом все заблокированные сессией объекты становятся доступны для блокирования.
Подробнее можно почитать здесь.
Логические блокировки#
Логические блокировки являются транзакционными, и снимаются на commit или rollback.
Для получения блокировки используется метод Btk_LogicLockPkg.request(),
в который передается имя блокировки.
Сессия при получении блокировки будет ожидать фиксацию или откат данных другой сессии, которая уже получила эту блокировку до этого.
Оптимистические блокировки#
По умолчанию класс использует оптимистическую блокировку, для этого
в таблице создается служебное поле nVersion_dz.
В случае, если версия загруженного в транзакционный кэш перед изменением объекта
отличается от версии в строке таблицы базы данных при сохранении изменений,
возникает ошибка сохранения.
Преимущества оптимистической блокировки:
Не требует начало транзакции базы данных
Недостатки оптимистической блокировки:
Ошибка проявляется только при сохранении изменений в базу
В момент возникновения ошибки, сложнее отловить ее источник.Возможно появления лишней нагрузки на базу данных
От момента правки строки, до момента возникновения ошибки может пройти существенный набор действий который в итоге будет откачен.
Пессимистическая блокировка#
Транзакционная блокировка с помощью которой можно заблокировать строку базы данных. При необходимости данная блокировка может быть выполнена с помощью:
-
Совет
Смотрите Явные блокировки
OQuery c опцией
forUpdateBtk_Pkg().lockObjects
Преимущества пессимистической блокировки
Конфликт определяется в момент выполнения блокировки
Недостатки пессимистической блокировки:
Требует транзакции базы данных
Бизнес логика#
На каждый контроллер бизнес логики создается объект в контексте сессии приложения. Таким образом бизнес логика работает в контексте сессии что позволяет:
Хранить переменные объекта в разрезе сессии
Выполнять объектные запросы в базу данных с учетом сессионного кэша
Работать в контексте транзакции сессии
Ключевые стратегии создания масштабируемого решения#
Минимизируйте количество запросов к базе данных
Для этого используйте:bulk загрузку кэша
транзакционные индексы
разделяемые классы
кэширование запросов для разделяемых классов
Ограничивайте размер транзакции
Для этого:разбивайте бизнес логику на части
К примеру разбейте массив бизнес объектов на порции для обработки.Используйте
commitwork
Это позволяет сбросить данные в базу без завершения транзакции, при этом происходит очистка оперативной памяти.
Api#
Содержит бизнес логику по обработке данных в таблице класса(сущности). Каждому классу соответствуют два файла с кодом бизнес логики данного класса:
Xxx_XxxxDpi
Содержит авто формируемый прикладной код. Данный код пересоздаётся системой формирования кода и не должен меняться разработчикамиXxx_XxxxApi
Содержит прикладной код, изменяемый разработчиками
Пример API#
class Bs_GdsCostDeviationTypeApi extends Bs_GdsCostDeviationTypeDpi[Bs_GdsCostDeviationTypeAro, Bs_GdsCostDeviationTypeApi, Bs_GdsCostDeviationTypeAta] {
override protected def entityAta: Bs_GdsCostDeviationTypeAta = Bs_GdsCostDeviationTypeAta
def register(spCode: NString
, spCaption: NString
, spDescription: NString): Unit = {
val ropSelf = get(findByMnemoCode(spCode)).getOrElse {
insert() :/ { rop =>
setsCode(rop, spCode)
rop
}
}
setsCaption(ropSelf, spCaption)
setsDescription(ropSelf, spDescription)
}
}
Pkg#
Пакеты создаются разработчиками по необходимости. Пакеты содержат методы бизнес-логики, которые не возможно отнести к какому-либо классу.
class Xxx_XxxxPkg extends Pkg{}
object Xxx_XxxxPkg extends PkgFactory[Xxx_XxxxPkg]