# Урок 1. Архитектура, средства и языки разработки Цель данного урока: - Познакомить слушателей с архитектурой фреймворка и принципами разработки в нем - Познакомить слушателей с базой данных `Postgresql`. - Дать понять какие знания и навыки по работе с языком `sql` потребуются для дальнейшей работы - Ознакомить со структурой документации `Postgresql` - Дать понять какие знания и навыки по работе c языками `scala` и `jexl` потребуются для дальнейшей работы - Ознакомить со структурой документации `scala` и `jexl` Данный урок предоставляет: - Обзор архитектуры - Основные понятия фреймворка - Основные понятия по работе с базой данных - Упражнения по языку sql - Упражнения по `scala` - Упражнения по `jexl` - Литературу для самостоятельного изучения ## Архитектура системы ![G3System](books/AppGuide/img/system_architecture.png) Для изучения смотри: - [Руководство разработчика: Введение](books/AppGuide/020_введение.md) ### Общий принцип работы Пользователь подключается с помощью браузера к серверу приложения в режиме терминального доступа. Сервер приложения возвращает web страницу с рабочим столом на котором пользователь может открывать экранные формы и просматривать\загружать отчеты. Протокол терминального доступа позволяет работать через тонкий канал за счет того что пользователю передается только видимая часть данных. Для предоставления пользователю требуемой бизнес логики, сервер приложения запускает решение, которое состоит из набора модулей. Где модуль это неделимая часть решения. Каждый модуль содержит автономную(классы) и интерактивную(выборки) бизнес логику. ## База данных ### О базе данных Для работы система `Global` использует базу данных `PostgreSQL`. `PostgreSQL` - это свободно распространяемая объектно-реляционная система управления базами данных (`ORDBMS`), с наиболее богатым sql из открытых СУБД и являющаяся реальной альтернативой коммерческим базам данных. `PostgreSQL` произносится как `post-gress-Q-L` (сокр. `Postgres`). `Postgres` — это реляционная система управления базами данных (РСУБД). Где данные хранятся в виде строк в таблицах, с возможностью ссылаться на другие таблицы. Все строки таблицы имеют одинаковый набор именованных столбцов, при этом каждому столбцу назначается определённый тип данных. Хотя порядок столбцов во всех строках фиксирован, важно помнить, что SQL не гарантирует какой-либо порядок строк в таблице (хотя их можно явно отсортировать при выводе). Все изменения данных в `Postgres` проходит по средствам транзакций (логически операция, которая может быть совершена только полностью). ### Надежность Надежность `PostgreSQL` обеспечивается следующими возможностями: - `ACID` - Atomicity,Consistency,Isolation,Durability - *Atomicity* - Атомарность \ Транзакция рассматривается как единая логическая единица, все ее изменения или сохраняются целиком, или полностью откатываются - *Consistency* - Согласованность \ Транзакция переводит базу данных из одного непротиворечивого состояния (на момент старта транзакции) в другое непротиворечивое состояние (на момент завершения транзакции). Непротиворечивым считается состояние базы, когда выполняются все ограничения физической и логической целостности базы данных, при этом допускается нарушение ограничений целостности в течение транзакции, но на момент завершения все ограничения целостности, как физические, так и логические, должны быть соблюдены. - *Isolation* - Изоляция \ Изменения данных при конкурентных транзакциях изолированы друг от друга на основе системы версионности - *Durability* - Устойчивость \ `PostgreSQL` заботится о том, что результаты успешных транзакций гарантировано сохраняются на жесткий диск вне зависимости от сбоев аппаратуры. - `MVCC` - Multiversion Concurrency Control \ Один из механизмов СУБД для обеспечения параллельного доступа к базам данных, заключающийся в предоставлении каждому пользователю так называемого «снимка» базы, обладающего тем свойством, что вносимые пользователем изменения невидимы другим пользователям до момента фиксации транзакции. Этот способ управления позволяет добиться того, что пишущие транзакции не блокируют читающих, и читающие транзакции не блокируют пишущих. - `WAL` - Write Ahead Logging \ Общепринятый механизм протоколирования всех транзакций, что позволяет восстановить систему после возможных сбоев. Основная идея WAL состоит в том, что все изменения должны записываться в файлы на диск только после того, как эти записи журнала, описывающие эти изменения будут и гарантировано записаны на диск. Это позволяет не сбрасывать страницы данных на диск после фиксации каждой транзакции, так как мы знаем и уверены, что сможем всегда восстановить базу данных используя журнал транзакций. - `PITR` - Point in Time Recovery\ Предоставляет возможность восстановления базы данных (используя WAL) на любой момент в прошлом, что позволяет осуществлять непрерывное резервное копирование кластера PostgreSQL. Репликация также повышает надежность PostgreSQL. - `Integrity` - Целостность \ PostgreSQL поддерживает целостность данных на уровне схемы - это внешние ключи (foreign keys), ограничения (constraints). ### Производительность `Postgres` обеспечивает высокую высокую масштабируемость за счет следующих возможностей: - Поддержка индексов \ Индексы — это традиционное средство увеличения производительности БД. Используя индекс, сервер баз данных может находить и извлекать нужные строки гораздо быстрее, чем без него. Однако с индексами связана дополнительная нагрузка на СУБД в целом, поэтому применять их следует обдуманно. `Postgres` поддерживает: - Стандартные индексы - Частичные индексы - Функциональные индексы - Планировщик запросов \ Планирует выполнение запроса основывается на стоимости различных планов, учитывая множество факторов таким образом чтобы минимизировать используемые ресурсы сервера. - Система блокировок \ Блокировка поддерживается на уровне таблиц и записей. Блокировка оптимизирована под конкретную ОС и архитектуру. - Кэширование \ Управление буферами и кэширование используют развитые алгоритмы для поддержания эффективности использования выделенных ресурсов. - Табличные пространства \ Tablespaces позволяют гибкое использование дискового пространства для хранения объектов системы. ## Классы Определяет правила хранения и обработки таблицы базы данных. Для изучения смотри: - [Руководство разработчика:Класс](books/AppGuide/030_class/060_класс.md) ## Выборки Определяет правило получения, отображение данных и обеспечивает взаимодействие с пользователем Для изучения смотри: - [Руководство разработчика:Выборка](books/AppGuide/040_selection/080_выборка.md) ## Модуль `Модуль` это неделимая группа файлов которая может включатся в произвольный прикладной проект. Бизнес логика разрабатываемая с помощью фреймворка компонуется между прикладными проектами с помощью модулей. Где: - `Прикладной проект` \ Набор модулей собираемых в конечное решение. Прикладной проект компонует бизнес логику модулей и связывает их между собой посредством инъекций зависимости(плагины, проектные переопределения). - `Решение` \ Набор исполняемых файлов и ресурсов разворачиваемый на сервере приложения в рамках одной схемы базы данных. Для изучения смотри: - [Руководство разработчика: Введение # Прикладной проект](books/AppGuide/020_common/030_начало_работы.md#Прикладной-проект) ## Dbeaver [Dbeaver](https://dbeaver.io/) - свободный кроссплатформенный инструмент разработки базы данных. Используется для написания и отладки комплексных sql запросов. ## SQL Декларативный язык запросов, применяемый для создания, модификации и управления данными. В отличие от процедурных языков, в которых есть условия, циклы и функции, в sql таких конструкций почти нет. Декларативные выражения представляют собой описание того, что необходимо получить. По данному описанию планировщик `Postgres` строит алгоритмы для получения результата. Для изучения языка смотрите: - [Учебное пособие](https://www.postgresqltutorial.com/) - [Русскоязычная документация от PostgresPro](https://www.postgrespro.ru/docs/postgrespro/14/sql) ## PL/pgSQL `PL/pgSQL` это процедурное расширение, наследует все пользовательские типы, функции и операторы `SQL`. Язык позволяет: - создавать функции и триггеры - использовать управляющие структуры - выполнять сложные вычисления Функции PL/pgSQL могут использоваться везде, где допустимы встроенные функции. Например, можно создать функции со сложными вычислениями и условной логикой, а затем использовать их при определении [операторов](https://www.postgresql.org/docs/14/functions.html) или в индексных выражениях. Для изучения языка смотрите: - [Учебное пособие](https://www.postgresqltutorial.com/postgresql-plpgsql/) - [Официальная документация](https://www.postgrespro.ru/docs/postgrespro/14/plpgsql) ## Intellij Idea Интегрированная среда разработки для написания исходного кода модулей. Для изучения смотри: - [Руководство по Intellij Idea](https://www.jetbrains.com/help/idea/getting-started.html) - [Руководство разработчика: Работа в IntelliJ IDEA](books/AppGuide/050_tools/100_intellij_idea.md) ## Конфигуратор Системное приложение `Конфигуратор`, развернутое в сервере приложения, предназначено для автоматизации и ускорению выполнения рутинных задач по разработке проекта. Для изучения смотри: - [Руководство разработчика: Конфигуратор](books/AppGuide/050_tools/090_конфигуратор.md) ## Язык scala Язык разработки используемый для программирования бизнес логики сервера приложения. Для изучения смотри: - [Основы языка](https://docs.scala-lang.org/ru/tour/tour-of-scala.html) - [Структуры управления](https://docs.scala-lang.org/ru/scala3/book/control-structures.html) - [Гайд по коллекциям](https://docs.scala-lang.org/ru/overviews/collections-2.13/arrays.html) - [Показатели производительности коллекций](https://docs.scala-lang.org/ru/overviews/collections-2.13/performance-characteristics.html) - [Руководство разработчика: Языки разработки # Scala](books/AppGuide/020_common/040_языки_разработки.md#scala) - [Библиотека юнит тестирования в скала](https://www.scalatest.org/) ## Язык jexl Java Expression Language - Язык выражений java Используется для выполнения динамической бизнес логики. Скрипты на jexl не требуют перекомпиляции прикладного проекта, поэтому могут исполняться в сервере приложений в любое время. Данный язык широко используется в скриптах миграции, а так же в событиях рабочих процессов настраиваемых на проекте. Для изучения смотри: - [Обзор](https://commons.apache.org/proper/commons-jexl/) - [Синтаксис](https://commons.apache.org/proper/commons-jexl/reference/syntax.html) - [Руководство разработчика: Языки разработки # Jexl скрипты](books/AppGuide/020_common/040_языки_разработки.md#jexl-скрипты) ## Настройка рабочего места ### Подготовка БД для урока 1. Установите и настройте [`postgres`](#установка-postgres) 2. Установите и настройте [`Dbeaver`](#установка-и-настройка-dbeaver) ```{note} Можно пропустить этот шаг если вы используете курс с опцией `virtual-vm` и образ [start](005_предисловие.md#virtual-vm) ``` #### Установка postgres Для установки `postgres`: 1. Запустите установщик [postgres](https://www.postgresql.org/download/) ```{note} В случае, если вы используете курс с опцией `virtual-vm` дистрибутив находится в каталоги `c:\distr` ``` 2. Следуйте указаниям мастера \ При установке достаточно выбирать опции по умолчанию. ```{attention} Имя служебного пользователя должно быть `postgres` ``` Для загрузки схемы для обучения: 1. Положите [файл](https://pgexercises.com/dbfiles/clubdata.sql) с учебной схемой в `c:\distr` ```{note} В случае, если вы используете курс с опцией `virtual-vm` данный шаг можно пропустить ``` 2. Откройте командный процессор [cmd](https://learn.microsoft.com/ru-ru/windows-server/administration/windows-commands/cmd) 3. Перейдите в каталог исполняемых файлов postgres \ В случае, установки по умолчанию 14 версии: ```Batchfile cd "C:\Program Files\PostgreSQL\14\bin" ``` 4. Запустите скрипт установки ```Batchfile psql -U postgres -f c:\distr\clubdata.sql -d postgres -x -q ``` Данный скрипт создаст базу данных `exercises` #### Установка и настройка Dbeaver Для установки: 1. Запустите установщик [Dbeaver](https://dbeaver.io/download/) ```{tip} В случае, если вы используете курс с опцией `virtual-vm` дистрибутив находится в каталоги `c:\distr` ``` 2. Следуйте указаниям мастера \ При установке достаточно выбирать опции по умолчанию. Если нет подключения к интернету, настройте драйвер базы данных для автономной работы: 1. Откройте `Dbeaver` 2. Откройте `Управление драйверами` \ Пункт меню `Database > Driver Manager` 3. Найдите драйвер `PostgreSQL` 4. Откройте карточку редактирования 5. Перейдите на закладку `Libraries` 6. Удалите все ссылки на библиотеки для закачки 7. Добавьте [jar файл c драйвером](https://jdbc.postgresql.org/download/) ```{note} В случае, если вы используете курс с опцией `virtual-vm` файл находится в каталоги `c:\distr` ``` Для создания нового подключения к основной базе: 1. Создайте новое подключение \ Пункт меню `Database > New connection` 2. Выберите драйвер `PostgreSQL` \ Если вы устанавливали `postgres` с настройками по умолчанию, созданное соединение будет работать 3. Протестируйте соединение \ Нажмите кнопку `Test connection` в мастере соединения Для создания нового подключения к базе `exercises`: 1. Создайте новое подключение \ Пункт меню `Database > New connection` 2. Выберите драйвер `PostgreSQL` \ Если вы устанавливали `postgres` с настройками по умолчанию, созданное соединение будет работать 3. Задайте базу подключения `exercises` 4. Протестируйте соединение \ Нажмите кнопку `Test connection` в мастере соединения ### Стандартная настройка Для стандартной настройки рабочего места воспользуйтесь инструкцией [Руководство разработчика: Начало работы с фреймворком # Настройка рабочего места](books/AppGuide/020_common/030_начало_работы.md#Настройка-рабочего-места) ```{tip} С инструкцией по установке [Global3FrameworkStarterKit](930_приложение_4.md) можно ознакомится в приложении данного урока ``` ## Запуск сервера приложения в режиме отладки После настройки рабочего места и сборки проекта можно запустить локальный сервер приложений в режиме отладки. Выберите в списке стартовых конфигураций `Global3se` и нажмите на кнопуку `Debug (Shift+F9)`. ![selectDebugConfig](/img/startConfigurations.png) При первом запуске IDEA попросит уточнить настройки отладочной конфигурации. В поле -cp (ClassPath) необходимо указать модуль с наименьшим количеством зависимостей, обычно выбирают модуль проекта с окончанием `App`. ![DebugConfigProps](/img/startConfigurationsProps.png) Подробнее об отладке можно узнать по [ссылке](https://www.jetbrains.com/help/idea/debugging-your-first-java-application.html). ## Запуск конфигуратора ```{attention} Раздел находится в разработке. ``` 1. Откройте в браузере [http://localhost:8080/](http://localhost:8080/) 2. Раскройте `Настройки подключения` и установите признак `Конфигуратор` 3. Для входа используйте `логин` admin, `пароль` admin ![loginConfigurator](/img/loginConfigurator.png) ## Создание модуля ```{attention} Раздел находится в разработке. ``` 1. Откройте конфигуратор 2. Откройте список модулей, выполнив операцию на главной панели приложения. ![modules](/img/Configurator_modules.png) 3. В списке модулей выполните операцию `Создать новый модуль` ![createModule](/img/Configurator_CreateModule.png) 4. В мастере укажите системное имя модуля `lbr`, наименование `Библиотека` 5. Подключите модули: `btk, bs, bts, rpt` и завершите работу мастера, выполнив операцию `Выбор` ![finishCreateModule](/img/Configurator_CreateModuleFinish.png) ## Учебная схема для работы с sql Схема для практики и упражнений состоит из набора данных для недавно созданного загородного клуба с перечнем участников. Данные содержат: - Участников клуба - Теннисные корты(объекты) - Историю бронирования этих объектов ```{attention} Не воспринимайте набор как пример хорошего дизайна. Этот набор данных разработан исключительно для поддержки упражнений. Схема базы данных имеет ряд недостатков. ``` Схема: ```sql -- Участники CREATE TABLE cd.members ( --Идентификатор --memid = 0 служебная запись, для всех гостей memid integer NOT NULL, --Фамилия surname character varying(200) NOT NULL, --Имя firstname character varying(200) NOT NULL, --Адрес address character varying(300) NOT NULL, --Почтовый код zipcode integer NOT NULL, --Телефон telephone character varying(20) NOT NULL, --memid участника который рекомендовал текущего участника recommendedby integer, --Дата вступления joindate timestamp NOT NULL, CONSTRAINT members_pk PRIMARY KEY (memid), CONSTRAINT fk_members_recommendedby FOREIGN KEY (recommendedby) REFERENCES cd.members(memid) ON DELETE SET NULL ); -- Объекты CREATE TABLE cd.facilities ( --Идентификатор facid integer NOT NULL, --Наименование name character varying(100) NOT NULL, --Стоимость слота(bookings.slots) для участника membercost numeric NOT NULL, --Стоимость слота(bookings.slots) для гостя( memid = 0 ) guestcost numeric NOT NULL, --Первоначальные затраты initialoutlay numeric NOT NULL, --Затраты на ежемесячное обслуживание monthlymaintenance numeric NOT NULL, CONSTRAINT facilities_pk PRIMARY KEY (facid) ); -- История заказов CREATE TABLE cd.bookings ( --Идентификатор bookid integer NOT NULL, --Ссылка на объекты facid integer NOT NULL, --Ссылка на участников memid integer NOT NULL, --Время начала использования объекта starttime timestamp NOT NULL, --Интервал использования объекта, выражается слотами, 1 слот - полчаса slots integer NOT NULL, CONSTRAINT bookings_pk PRIMARY KEY (bookid), CONSTRAINT fk_bookings_facid FOREIGN KEY (facid) REFERENCES cd.facilities(facid), CONSTRAINT fk_bookings_memid FOREIGN KEY (memid) REFERENCES cd.members(memid) ); ``` ## Практика работы с sql ### Просмотр учебной схемы В навигаторе базы данных(см. пункт меню `Windows > Database Navigator`): 1. Перейдите к учебной схеме `exercises \ Databases \ exercises \ Schemas \ cd` 2. В контекстном меню выберите `View schema` 3. В открывшимся окне перейдите на закладку `ER diagram` ### Выполнение запроса 1. Создайте новый запрос \ Для этого выберите пункт меню `Sql Editor > New Sql Script`. При этом откроется окно для ввода sql 2. Установите активное соединение `exercises` \ Для этого выберете пункт меню `Sql Editor > Set Active Connection` 3. В окне ввода sql введите ```sql select * from cd.facilities; ``` 4. Выполните sql \ Для этого выберите пункт меню `Sql Editor > Execute Sql Statement` 5. Посмотрите план запроса \ Для этого выберите пункт меню `Sql Editor > Explain Execution Plan` ```{tip} Понимания плана запроса позволяет принять решение о необходимости дальнейшей оптимизации. Для более подробной информации смотрите [оптимизация производительности](https://www.postgrespro.ru/docs/postgrespro/14/performance-tips) в документации PostgreSql ``` Результат: ```text |facid|name |membercost|guestcost|initialoutlay|monthlymaintenance| |-----|---------------|----------|---------|-------------|------------------| |0 |Tennis Court 1 |5 |25 |10,000 |200 | |1 |Tennis Court 2 |5 |25 |8,000 |200 | |2 |Badminton Court|0 |15.5 |4,000 |50 | |3 |Table Tennis |0 |5 |320 |10 | |4 |Massage Room 1 |35 |80 |4,000 |3,000 | |5 |Massage Room 2 |35 |80 |4,000 |3,000 | |6 |Squash Court |3.5 |17.5 |5,000 |80 | |7 |Snooker Table |0 |5 |450 |15 | |8 |Pool Table |0 |5 |400 |15 | ``` ## Упражнения по языку sql ### Простое соединение таблиц Напишите запрос получения списка времени начала использования объекта для участников с именем и фамилией 'David Farrell' ```{tip} Смотрите [соединённые таблицы](https://www.postgrespro.ru/docs/postgrespro/14/queries-table-expressions) в документации PostgreSql ``` Результат: ```text |starttime | |-----------------------| |2012-09-18 09:00:00.000| |2012-09-18 13:30:00.000| |2012-09-18 17:30:00.000| |2012-09-18 20:00:00.000| |2012-09-19 09:30:00.000| |2012-09-19 12:00:00.000| |2012-09-19 15:00:00.000| |2012-09-20 11:30:00.000| |2012-09-20 14:00:00.000| |2012-09-20 15:30:00.000| |2012-09-21 10:30:00.000| |2012-09-21 14:00:00.000| |2012-09-22 08:30:00.000| |2012-09-22 17:00:00.000| |2012-09-23 08:30:00.000| |2012-09-23 17:30:00.000| |2012-09-23 19:00:00.000| |2012-09-24 08:00:00.000| |2012-09-24 12:30:00.000| |2012-09-24 16:30:00.000| |2012-09-25 15:30:00.000| |2012-09-25 17:00:00.000| |2012-09-26 13:00:00.000| |2012-09-26 17:00:00.000| |2012-09-27 08:00:00.000| |2012-09-28 09:30:00.000| |2012-09-28 11:30:00.000| |2012-09-28 13:00:00.000| |2012-09-29 10:30:00.000| |2012-09-29 13:30:00.000| |2012-09-29 14:30:00.000| |2012-09-29 16:00:00.000| |2012-09-29 17:30:00.000| |2012-09-30 14:30:00.000| ``` ### Подзапросы Создайте запрос, возвращающий список заказов на день 14.09.2012 которые будет стоить участнику (или гостью) более 30 у.е. Добавьте в вывод: - Имя объекта - Имя, фамилия участника \ Информация должна быть выведена одной колонкой - Стоимость Отсортируйте вывод по убыванию. Используйте подзапрос для выбора данных и запрос для сортировки, это позволит уменьшить текстовое дублирование. ```{note} Гость имеет отличную от участника стоимость(Стоимость учитывается на 1 слот, который равняется получасу). Идентификатор гостевого пользователя всегда 0. ``` ```{tip} Смотрите: - [Подзапросы](https://www.postgrespro.ru/docs/postgrespro/14/queries-table-expressions) - [Функции](https://www.postgrespro.ru/docs/postgrespro/14/functions) в документации PostgreSql. Для расчета стоимости используйте атрибуты: - `bookings.slots` - `facilities.guestcost` - `facilities.membercost` ``` Результат: ```text |member |facility |cost| |---------------|--------------|----| |GUEST GUEST |Massage Room 2|320 | |GUEST GUEST |Massage Room 1|160 | |GUEST GUEST |Massage Room 1|160 | |GUEST GUEST |Massage Room 1|160 | |GUEST GUEST |Tennis Court 2|150 | |Jemima Farrell |Massage Room 1|140 | |GUEST GUEST |Tennis Court 1|75 | |GUEST GUEST |Tennis Court 2|75 | |GUEST GUEST |Tennis Court 1|75 | |Matthew Genting|Massage Room 1|70 | |Florence Bader |Massage Room 2|70 | |GUEST GUEST |Squash Court |70 | |Jemima Farrell |Massage Room 1|70 | |Ponder Stibbons|Massage Room 1|70 | |Burton Tracy |Massage Room 1|70 | |Jack Smith |Massage Room 1|70 | |GUEST GUEST |Squash Court |35 | |GUEST GUEST |Squash Court |35 | ``` ### Агрегация Создайте запрос возвращающий список количества слотов заказанных каждый месяц в 2012. Колонки списка: - Идентификатор объекта - Номер месяца - Количество слотов Отсортируйте результат по идентификатору и номеру месяца. ```{tip} Смотрите: - [Группировку строк](https://www.postgrespro.ru/docs/postgrespro/14/queries-table-expressions#QUERIES-GROUP) - [Функции для работы с датами](https://www.postgrespro.ru/docs/postgrespro/14/functions-datetime) в документации PostgreSql. ``` Результат: ```text |facid|month|Total Slots| |-----|-----|-----------| |0 |7 |270 | |0 |8 |459 | |0 |9 |591 | |1 |7 |207 | |1 |8 |483 | |1 |9 |588 | |2 |7 |180 | |2 |8 |459 | |2 |9 |570 | |3 |7 |104 | |3 |8 |304 | |3 |9 |422 | |4 |7 |264 | |4 |8 |492 | |4 |9 |648 | |5 |7 |24 | |5 |8 |82 | |5 |9 |122 | |6 |7 |164 | |6 |8 |400 | |6 |9 |540 | |7 |7 |156 | |7 |8 |326 | |7 |9 |426 | |8 |7 |117 | |8 |8 |322 | |8 |9 |471 | ``` ### Оконные функции Найдите три лучших объекта, приносящих доход ```{tip} Смотрите [оконные функции](https://www.postgrespro.ru/docs/postgrespro/14/tutorial-window) в документации PostgreSql ``` Результат: ```text |name |rank| |--------------|----| |Massage Room 1|1 | |Massage Room 2|2 | |Tennis Court 2|3 | ``` ### Рекурсия Найдите восходящую цепочку рекомендаций для участника(A) с идентификатором 27 : то есть участника(B), который рекомендовал участника A и участника(C) который рекомендовал участника (B) и так далее. Верните идентификатор участника, имя и фамилию. Порядок по убыванию идентификатора участника. ```{tip} Смотрите [рекурсивные запросы](https://www.postgrespro.ru/docs/postgrespro/14/queries-with#QUERIES-WITH-RECURSIVE) в документации PostgreSql ``` Результат: ```text |recommender|firstname|surname| |-----------|---------|-------| |20 |Matthew |Genting| |5 |Gerald |Butters| |1 |Darren |Smith | ``` ### Json Напишите запрос возвращающий одну строку(используя агрегацию) содержащую json массив для первых 5 записей по членам клуба. Записи в массиве должны быть отсортированы по идентификатору члена клуба. Указывайте формат даты явным образом(`to_char(joindate, 'DD.MM.YYYY HH24:MI:SS')`), чтобы избежать зависимостей от настроек базы данных. ```{tip} Смотрите: - [Функции по работе с json](https://www.postgrespro.ru/docs/postgrespro/14/functions-json) в документации PostgreSql. ``` ```json {"members":[ {"memid":0,"surname":"GUEST","firstname":"GUEST","address":"GUEST","zipcode":0,"telephone":"(000) 000-0000","recommendedby":null,"joindate":"01.07.2012 00:00:00"}, {"memid":1,"surname":"Smith","firstname":"Darren","address":"8 Bloomsbury Close, Boston","zipcode":4321,"telephone":"555-555-5555","recommendedby":null,"joindate":"02.07.2012 12:02:05"}, {"memid":2,"surname":"Smith","firstname":"Tracy","address":"8 Bloomsbury Close, New York","zipcode":4321,"telephone":"555-555-5555","recommendedby":null,"joindate":"02.07.2012 12:08:23"}, {"memid":3,"surname":"Rownam","firstname":"Tim","address":"23 Highway Way, Boston","zipcode":23423,"telephone":"(844) 693-0723","recommendedby":null,"joindate":"03.07.2012 09:32:15"}, {"memid":4,"surname":"Joplette","firstname":"Janice","address":"20 Crossing Road, New York","zipcode":234,"telephone":"(833) 942-4710","recommendedby":1,"joindate":"03.07.2012 10:25:05"} ]} ``` ## Практика работы с scala ```{tip} Прочитайте: - Главу [основы scala](https://docs.scala-lang.org/ru/tour/basics.html) - Главу [структуры управления](https://docs.scala-lang.org/ru/scala3/book/control-structures.html) ``` ### Создание класса с тестами 1. Перейдите в окно проекта 2. Выберете целевой модуль 3. Перейдите в папку с исходными кодами \ `[module_name]/src/test/scala` ```{tip} Создать недостающую папку можно из контекстного меню в idea `New > Directory` ``` 4. Создайте пакет `ru.bitec.app.[module_name]` 5. Создайте тестовый класс ```scala class Lesson1Test extends LangFunSuite{ test("HelloWorld"){ println("hello world") } } ``` ```{tip} Запустить тест можно из контекстного меню, для этого: 1. Переведите курсор на декларацию функции 2. В контекстном меню выполните операцию 'Debug' Подробнее смотрите [выполнение тестов](https://www.jetbrains.com/help/idea/performing-tests.html) в руководстве idea. Для дополнительной информации смотрите также библиотеку unit тестирования: [scalatest](https://www.scalatest.org/) ``` ```{note} Существуют два специализированных базовых класса для тестовых случаев: - `LangFunSuite` \ Используется для тестов которые не нуждаются в контроллерах бизнес логики. Данные тесты не могут использовать `Api`, `Pkg`, и не имеют подключения к базе данных по умолчанию. - `ApiTest` \ В данном тексте доступен контекст автономной бизнес логики, тестовые случаи могут использовать `Api`,`Pkg` объекты. При этом запуск теста становится медленней из за необходимости инициализировать контекст. ``` ### Объявление null типов Смотри: [Руководство разработчика: языки разработки # scala типы](books/AppGuide/020_common/040_языки_разработки.md#scala-типы-для-работы-с-данными) Пример объявления null типов показан в следующей тестовой функции: ```scala test("NullTypes"){ val nvNumber = "1.3".nn val nvNullNumber = None.nn val nvDate = "21.11.2003 00:00:00".nd val nvNullString = None.nn val nvEmptyString = "".ns assert(nvNullString != nvEmptyString) assert((nvNumber + 1.nn) === "2.3".nn) assert((nvNullNumber + 1.nn).isNull) assert( (nvDate+1.day).asNDate() === "22.11.2003".nd) } ``` Null типы являются доменным аналогом scala типа опций(`Option`), они используются для более компактной записи выражений. Пример использования null типов и option: ```scala //Null типы val a1 = 1.nn val b1 = None.nn assert( (a1+b1).isNull ) //Option val a2 = Some(1) val b2: Option[Int] = None assert( (for(a<-a2;b<-b2) yield a+b).isEmpty ) ``` ### Работа с коллекциями ```{tip} Для более подробной информации по работе с коллекциями смотрите: - [Гайд по коллекциям](https://docs.scala-lang.org/ru/overviews/collections-2.13/introduction.html) - [Руководство scala: Типы коллекций](https://docs.scala-lang.org/ru/scala3/book/collections-classes.html) - [Выбор последовательности](https://docs.scala-lang.org/ru/scala3/book/collections-classes.html#%D0%B2%D1%8B%D0%B1%D0%BE%D1%80-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8) ``` #### ArrayBuffer `ArrayBuffer` используется тогда, когда нужна изменяемая индексированная последовательность общего назначения. Поскольку `ArrayBuffer` индексирован, произвольный доступ к элементам выполняется быстро. ```scala val nums = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3) nums += 4 // ArrayBuffer(1, 2, 3, 4) nums ++= List(5, 6) ``` ```{tip} Если есть возможность(добавление идет только в конец) используйте `ArrayBuffer` вместо `ListBuffer`, так как это экономит оперативную память и процессорное время. ``` #### Массив Массив содержит фиксированный набор элементов, массивы удобно применять в константных последовательностях так как они потребляют минимальное количество оперативной памяти, и обладаю высоким быстродействием при переборе значений. ```scala val numbers = Array(1, 2, 3, 4) ``` ```{tip} Если вы неуверены в том какую коллекцию выбрать используйте массив. ``` #### Набор Наборы не содержат одинаковых элементов. ```scala //Эквивалентно Set(1, 2) val set = Set(1, 1, 2) ``` #### Кортеж Кортеж объединяет простые логические элементы коллекции без использования классов. ```scala //Создание кортежа val ingredient = ("Sugar", 25) //Распаковка кортежа val (name, quantity) = ingredient val ingredients = Array(ingredient) //Распаковка в анонимной функции ingredients.foreach{case (name, quantity) => println(s"$name:$quantity") } //Распаковка из опции ingredients.headOption match { case Some((name3, quantity3))=> case _ => } ``` Кортежи часто используются при работе с картами для комбинации `(key,value)` ```{tip} Более подробно смотрите [кортежи](https://docs.scala-lang.org/ru/tour/tuples.html) ``` #### Карты Карты используются для организации структуры позволяющей быстро получить значение по ключу. ```scala val map = Map("foo" -> "bar") ``` #### Опция Опция представляет собой контейнер, который хранит какое-то значение или не хранит ничего совсем. Опция часто используется как результат работы метода который может ничего не вернуть к примеру: ```scala val numbers = Map("one" -> 1, "two" -> 2) numbers.get("two") match { case Some(v)=> case _ => AppException("Ошибка в коде".ns).raise() } ``` #### Комбинаторы Комбинаторы позволяют выполнять операции преобразования и обхода коллекций. Пример работы с коллекциями: ```scala //Обявление массива номеров val numbers = Array(1.nn, 2.nn, 3.nn, 4.nn) //Преобразования массива номеров в массив строк val strings = numbers.map(_.toString()) // Объявление карты val map = Map("steve" -> 100.nn, "bob" -> 101.nn, "joe" -> 201.nn) // Печать элементов массива strings.foreach{v => println(v) } //Печать элементов карты //Выражение case ( k,v ) преобразовывает входящий кортеж //в переменные k,v map.foreach{case ( k,v )=> println(s"$k:$v") } //Фильтрация номеров val someNumbers = numbers.filter(_ >= 3.nn) //Группировка номеров по 2 элемента в группу val groupedNumbers = numbers.grouped(2).toArray //Поиск номера val num = numbers.find(_ === 2.nn).getOrElse(None.nn) //Сортировка номеров по убыванию val sortedNumbers = numbers.sortBy(_.desc) //Сумирование номеров с лева на право //Первый аргумент задает начальное значение val sum = numbers.foldLeft(0.nn)(_+_) //Преобразование коллекции в строку через запятую и вывод на экран println(sortedNumbers.mkString(",")) ``` ### Json Загрузка json в модель: ```scala """{ "values":[ {"id":1}, {"id":2}, {"id":1}, {"id":3} ] }""".ns.jsonToValue(classOf[Data]) ``` Модель `Data` должна быть объявлена внешними классами: ```scala /* *Класс, типизирующий какую либо строку(из базы данных, или массива json) с полем id. *Может быть несколько полей с разными типами. */ class Rows{ var id: NNumber = _ } /* *Класс предоставляющий модель данных. */ class Data{ val values: Array[Rows] = Array.empty } ``` ```{note} Модель `Data` необходимо так как коллекции в java не хранят информации о вложенных объектах, соответственно выражение `classOf[Array[Member]]` не может предоставить достаточной информации для типизированной десериализации. ``` ## Упражнения по scala ### Создание функции Создайте функцию прибавляющую 1 час к дате. ### Создание класса Создайте scala класс для хранения данных по членам клуба, назовите атрибуты класса в соответствии с примером на json: ```json { "memid":0, "surname":"GUEST", "firstname":"GUEST", "address":"GUEST", "zipcode":0, "telephone":"(000) 000-0000", "recommendedby":null, "joindate":"01.07.2012 00:00:00" } ``` ### Загрузка классов из json Загрузите json в модель скала классов: ```json {"members":[ {"memid":0,"surname":"GUEST","firstname":"GUEST","address":"GUEST","zipcode":0,"telephone":"(000) 000-0000","recommendedby":null,"joindate":"01.07.2012 00:00:00"}, {"memid":1,"surname":"Smith","firstname":"Darren","address":"8 Bloomsbury Close, Boston","zipcode":4321,"telephone":"555-555-5555","recommendedby":null,"joindate":"02.07.2012 12:02:05"}, {"memid":2,"surname":"Smith","firstname":"Tracy","address":"8 Bloomsbury Close, New York","zipcode":4321,"telephone":"555-555-5555","recommendedby":null,"joindate":"02.07.2012 12:08:23"}, {"memid":3,"surname":"Rownam","firstname":"Tim","address":"23 Highway Way, Boston","zipcode":23423,"telephone":"(844) 693-0723","recommendedby":null,"joindate":"03.07.2012 09:32:15"}, {"memid":4,"surname":"Joplette","firstname":"Janice","address":"20 Crossing Road, New York","zipcode":234,"telephone":"(833) 942-4710","recommendedby":1,"joindate":"03.07.2012 10:25:05"} ]} ``` Описание модели данных: - data \ Используется для типизации модели данных - members \ Типизированный массив участников - Member \ Участник ```{tip} Для участника вы можете использовать класс созданные в предыдущем задании. ``` ### Поиск последнего участника Напишите выражение возвращающее члена клуба который присоединился к клубу последним. В качестве входных данных используйте результат полученный в задании [Загрузка классов из json](#загрузка-классов-из-json) Для определение последнего присоединившегося необходимо использовать поле `joindate` ```{tip} Смотрите [методы](https://www.scala-lang.org/api/2.12.17/scala/collection/TraversableOnce.html) доступные для работы с коллекциями ``` ### Сортировка при помощи операции Напишите scala функцию сравнивающую двух членов клуба по выражению `member1.memid < member2.memid`. Отсортируйте членов клуба с помощью данной функции. ```{tip} Смотрите [методы](https://docs.scala-lang.org/ru/overviews/collections-2.13/seqs.html) доступные для работы с коллекциями ``` ## Практика работа с jexl ### Выполнение произвольного jexl. ```scala val jexl = new JexlBuilder().permissions(JexlPermissions.UNRESTRICTED).create() /** * Выполнение jexl выражения * @param jexlString строка с jexl выражением * @param jexlContext контекст jexl, где контекст это набор переменных и функций, доступных в выражении. * @return результат выполнения jexl */ def evaluateJexl(jexlString: String, jexlContext: MapContext): AnyRef = { val e = jexl.createExpression(jexlString) val o = e.evaluate(jexlContext) o } test("eval_jexl") { //Контекст выполнения содержит переменные доступные jexl выражению //в момент выполнения val jc = new MapContext() //Задает переменную a в контексте выполнения jc.set("a",1) //Задает переменную b в контексте выполнения jc.set("b",2) assert(evaluateJexl("a + b", jc) == 3) } ``` ```{tip} Для дополнительной информации по синтаксису jexl смотрите [справку по синтаксису jexl](https://commons.apache.org/proper/commons-jexl/reference/syntax.html). Для понимания типов используемых в выражении смотрите [Арифметику jexl](https://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/JexlArithmetic.html) ``` ## Упражнения по jexl ### Функция сравнения на jexl Напишите scala функцию которая используя выражения jexl сравнит двух участников по выражению `member1.memid < member2.memid` ````{note} Для чтения свойства скала класса в jexl необходимо к имени свойства добавить скобки. ```{code} m1.memid() ``` ```` ### Сортировка через jexl Напишите сортировку списка участников через jexl выражение `member1.memid > member2.memid` ## Самостоятельное изучение - [Официальная документация postgreSQL на русском](https://www.postgrespro.ru/docs/postgrespro/14/index) - [Перечень полезной документации](https://www.postgresql.org/docs/) - [Руководстве прикладного разработчика : Введение](books/AppGuide/020_введение.md) - [Руководстве прикладного разработчика : Языки разработки](books/AppGuide/020_common/040_языки_разработки.md) - [scala](https://docs.scala-lang.org/ru/tour/tour-of-scala.html) - [jexl](https://commons.apache.org/proper/commons-jexl/reference/syntax.html)