Урок 1. Архитектура, средства и языки разработки#

Цель данного урока:

  • Познакомить слушателей с архитектурой фреймворка и принципами разработки в нем

  • Познакомить слушателей с базой данных Postgresql.

  • Дать понять какие знания и навыки по работе с языком sql потребуются для дальнейшей работы

  • Ознакомить со структурой документации Postgresql

  • Дать понять какие знания и навыки по работе c языками scala и jexl потребуются для дальнейшей работы

  • Ознакомить со структурой документации scala и jexl

Данный урок предоставляет:

  • Обзор архитектуры

  • Основные понятия фреймворка

  • Основные понятия по работе с базой данных

  • Упражнения по языку sql

  • Упражнения по scala

  • Упражнения по jexl

  • Литературу для самостоятельного изучения

Архитектура системы#

G3System

Для изучения смотри:

Общий принцип работы#

Пользователь подключается с помощью браузера к серверу приложения в режиме терминального доступа. Сервер приложения возвращает 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 позволяют гибкое использование дискового пространства для хранения объектов системы.

Классы#

Определяет правила хранения и обработки таблицы базы данных.

Для изучения смотри:

Выборки#

Определяет правило получения, отображение данных и обеспечивает взаимодействие с пользователем Для изучения смотри:

Модуль#

Модуль это неделимая группа файлов которая может включатся в произвольный прикладной проект.

Бизнес логика разрабатываемая с помощью фреймворка компонуется между прикладными проектами с помощью модулей.

Где:

  • Прикладной проект
    Набор модулей собираемых в конечное решение. Прикладной проект компонует бизнес логику модулей и связывает их между собой посредством инъекций зависимости(плагины, проектные переопределения).

  • Решение
    Набор исполняемых файлов и ресурсов разворачиваемый на сервере приложения в рамках одной схемы базы данных.

Для изучения смотри:

Dbeaver#

Dbeaver - свободный кроссплатформенный инструмент разработки базы данных.

Используется для написания и отладки комплексных sql запросов.

SQL#

Декларативный язык запросов, применяемый для создания, модификации и управления данными. В отличие от процедурных языков, в которых есть условия, циклы и функции, в sql таких конструкций почти нет. Декларативные выражения представляют собой описание того, что необходимо получить. По данному описанию планировщик Postgres строит алгоритмы для получения результата.

Для изучения языка смотрите:

PL/pgSQL#

PL/pgSQL это процедурное расширение, наследует все пользовательские типы, функции и операторы SQL.

Язык позволяет:

  • создавать функции и триггеры

  • использовать управляющие структуры

  • выполнять сложные вычисления

Функции PL/pgSQL могут использоваться везде, где допустимы встроенные функции. Например, можно создать функции со сложными вычислениями и условной логикой, а затем использовать их при определении операторов или в индексных выражениях.

Для изучения языка смотрите:

Intellij Idea#

Интегрированная среда разработки для написания исходного кода модулей.

Для изучения смотри:

Конфигуратор#

Системное приложение Конфигуратор, развернутое в сервере приложения, предназначено для автоматизации и ускорению выполнения рутинных задач по разработке проекта.

Для изучения смотри:

Язык scala#

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

Для изучения смотри:

Язык jexl#

Java Expression Language - Язык выражений java Используется для выполнения динамической бизнес логики. Скрипты на jexl не требуют перекомпиляции прикладного проекта, поэтому могут исполняться в сервере приложений в любое время.

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

Для изучения смотри:

Настройка рабочего места#

Подготовка БД для урока#

  1. Установите и настройте postgres

  2. Установите и настройте Dbeaver

Примечание

Можно пропустить этот шаг если вы используете курс с опцией virtual-vm и образ start

Установка postgres#

Для установки postgres:

  1. Запустите установщик postgres

    Примечание

    В случае, если вы используете курс с опцией virtual-vm дистрибутив находится в каталоги c:\distr

  2. Следуйте указаниям мастера
    При установке достаточно выбирать опции по умолчанию.

    Внимание

    Имя служебного пользователя должно быть postgres

Для загрузки схемы для обучения:

  1. Положите файл с учебной схемой в c:\distr

    Примечание

    В случае, если вы используете курс с опцией virtual-vm данный шаг можно пропустить

  2. Откройте командный процессор cmd

  3. Перейдите в каталог исполняемых файлов postgres
    В случае, установки по умолчанию 14 версии:

    cd "C:\Program Files\PostgreSQL\14\bin"
    
  4. Запустите скрипт установки

    psql -U postgres -f c:\distr\clubdata.sql -d postgres -x -q
    

    Данный скрипт создаст базу данных exercises

Установка и настройка Dbeaver#

Для установки:

  1. Запустите установщик Dbeaver

    Совет

    В случае, если вы используете курс с опцией virtual-vm дистрибутив находится в каталоги c:\distr

  2. Следуйте указаниям мастера
    При установке достаточно выбирать опции по умолчанию.

Если нет подключения к интернету, настройте драйвер базы данных для автономной работы:

  1. Откройте Dbeaver

  2. Откройте Управление драйверами
    Пункт меню Database > Driver Manager

  3. Найдите драйвер PostgreSQL

  4. Откройте карточку редактирования

  5. Перейдите на закладку Libraries

  6. Удалите все ссылки на библиотеки для закачки

  7. Добавьте jar файл c драйвером

    Примечание

    В случае, если вы используете курс с опцией 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 в мастере соединения

Стандартная настройка#

Для стандартной настройки рабочего места воспользуйтесь инструкцией Руководство разработчика: Начало работы с фреймворком # Настройка рабочего места

Совет

С инструкцией по установке Global3FrameworkStarterKit можно ознакомится в приложении данного урока

Запуск сервера приложения в режиме отладки#

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

Выберите в списке стартовых конфигураций Global3se и нажмите на кнопуку Debug (Shift+F9).

selectDebugConfig

При первом запуске IDEA попросит уточнить настройки отладочной конфигурации. В поле -cp (ClassPath) необходимо указать модуль с наименьшим количеством зависимостей, обычно выбирают модуль проекта с окончанием App.

DebugConfigProps

Подробнее об отладке можно узнать по ссылке.

Запуск конфигуратора#

Внимание

Раздел находится в разработке.

  1. Откройте в браузере http://localhost:8080/

  2. Раскройте Настройки подключения и установите признак Конфигуратор

  3. Для входа используйте логин admin, пароль admin

    loginConfigurator

Создание модуля#

Внимание

Раздел находится в разработке.

  1. Откройте конфигуратор

  2. Откройте список модулей, выполнив операцию на главной панели приложения.

    modules

  3. В списке модулей выполните операцию Создать новый модуль

    createModule

  4. В мастере укажите системное имя модуля lbr, наименование Библиотека

  5. Подключите модули: btk, bs, bts, rpt и завершите работу мастера, выполнив операцию Выбор

    finishCreateModule

Учебная схема для работы с 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 введите

    select * from cd.facilities;  
    
  4. Выполните sql
    Для этого выберите пункт меню Sql Editor > Execute Sql Statement

  5. Посмотрите план запроса
    Для этого выберите пункт меню Sql Editor > Explain Execution Plan

Совет

Понимания плана запроса позволяет принять решение о необходимости дальнейшей оптимизации. Для более подробной информации смотрите оптимизация производительности в документации PostgreSql

Результат:

|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“

Совет

Смотрите соединённые таблицы в документации PostgreSql

Результат:

|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 у.е. Добавьте в вывод:

  • Имя объекта

  • Имя, фамилия участника
    Информация должна быть выведена одной колонкой

  • Стоимость

Отсортируйте вывод по убыванию. Используйте подзапрос для выбора данных и запрос для сортировки, это позволит уменьшить текстовое дублирование.

Примечание

Гость имеет отличную от участника стоимость(Стоимость учитывается на 1 слот, который равняется получасу). Идентификатор гостевого пользователя всегда 0.

Совет

Смотрите:

Для расчета стоимости используйте атрибуты:

  • bookings.slots

  • facilities.guestcost

  • facilities.membercost

Результат:

|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. Колонки списка:

  • Идентификатор объекта

  • Номер месяца

  • Количество слотов

Отсортируйте результат по идентификатору и номеру месяца.

Совет

Смотрите:

Результат:

|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        |

Оконные функции#

Найдите три лучших объекта, приносящих доход

Совет

Смотрите оконные функции в документации PostgreSql

Результат:

|name          |rank|
|--------------|----|
|Massage Room 1|1   |
|Massage Room 2|2   |
|Tennis Court 2|3   |

Рекурсия#

Найдите восходящую цепочку рекомендаций для участника(A) с идентификатором 27 : то есть участника(B), который рекомендовал участника A и участника(C) который рекомендовал участника (B) и так далее.

Верните идентификатор участника, имя и фамилию. Порядок по убыванию идентификатора участника.

Совет

Смотрите рекурсивные запросы в документации PostgreSql

Результат:

|recommender|firstname|surname|
|-----------|---------|-------|
|20         |Matthew  |Genting|
|5          |Gerald   |Butters|
|1          |Darren   |Smith  |

Json#

Напишите запрос возвращающий одну строку(используя агрегацию) содержащую json массив для первых 5 записей по членам клуба. Записи в массиве должны быть отсортированы по идентификатору члена клуба. Указывайте формат даты явным образом(to_char(joindate, 'DD.MM.YYYY HH24:MI:SS')), чтобы избежать зависимостей от настроек базы данных.

Совет

Смотрите:

{"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#

Совет

Прочитайте:

Создание класса с тестами#

  1. Перейдите в окно проекта

  2. Выберете целевой модуль

  3. Перейдите в папку с исходными кодами
    [module_name]/src/test/scala

    Совет

    Создать недостающую папку можно из контекстного меню в idea New > Directory

  4. Создайте пакет ru.bitec.app.[module_name]

  5. Создайте тестовый класс

     class Lesson1Test extends LangFunSuite{
       test("HelloWorld"){
           println("hello world")
       }
     }
    

Совет

Запустить тест можно из контекстного меню, для этого:

  1. Переведите курсор на декларацию функции

  2. В контекстном меню выполните операцию „Debug“ Подробнее смотрите выполнение тестов в руководстве idea.

Для дополнительной информации смотрите также библиотеку unit тестирования: scalatest

Примечание

Существуют два специализированных базовых класса для тестовых случаев:

  • LangFunSuite
    Используется для тестов которые не нуждаются в контроллерах бизнес логики.

    Данные тесты не могут использовать Api, Pkg, и не имеют подключения к базе данных по умолчанию.

  • ApiTest
    В данном тексте доступен контекст автономной бизнес логики, тестовые случаи могут использовать Api,Pkg объекты. При этом запуск теста становится медленней из за необходимости инициализировать контекст.

Объявление null типов#

Смотри: Руководство разработчика: языки разработки # scala типы

Пример объявления null типов показан в следующей тестовой функции:

  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:

//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
)

Работа с коллекциями#

Совет

Для более подробной информации по работе с коллекциями смотрите:

ArrayBuffer#

ArrayBuffer используется тогда, когда нужна изменяемая индексированная последовательность общего назначения. Поскольку ArrayBuffer индексирован, произвольный доступ к элементам выполняется быстро.

val nums = ArrayBuffer(1, 2, 3)   // ArrayBuffer(1, 2, 3)
nums += 4                         // ArrayBuffer(1, 2, 3, 4)
nums ++= List(5, 6)

Совет

Если есть возможность(добавление идет только в конец) используйте ArrayBuffer вместо ListBuffer, так как это экономит оперативную память и процессорное время.

Массив#

Массив содержит фиксированный набор элементов, массивы удобно применять в константных последовательностях так как они потребляют минимальное количество оперативной памяти, и обладаю высоким быстродействием при переборе значений.

val numbers = Array(1, 2, 3, 4)

Совет

Если вы неуверены в том какую коллекцию выбрать используйте массив.

Набор#

Наборы не содержат одинаковых элементов.

//Эквивалентно Set(1, 2)
val set = Set(1, 1, 2)

Кортеж#

Кортеж объединяет простые логические элементы коллекции без использования классов.

//Создание кортежа
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)

Совет

Более подробно смотрите кортежи

Карты#

Карты используются для организации структуры позволяющей быстро получить значение по ключу.

val map = Map("foo" -> "bar")

Опция#

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

Опция часто используется как результат работы метода который может ничего не вернуть к примеру:

val numbers = Map("one" -> 1, "two" -> 2)
numbers.get("two") match {
  case Some(v)=>
  case _ => AppException("Ошибка в коде".ns).raise()
}

Комбинаторы#

Комбинаторы позволяют выполнять операции преобразования и обхода коллекций. Пример работы с коллекциями:

//Обявление массива номеров
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 в модель:

"""{
  "values":[
    {"id":1},
    {"id":2},
    {"id":1},
    {"id":3}
  ]
}""".ns.jsonToValue(classOf[Data])

Модель Data должна быть объявлена внешними классами:

/*
*Класс, типизирующий какую либо строку(из базы данных, или массива json) с полем id.
*Может быть несколько полей с разными типами.
*/
class Rows{
  var id: NNumber = _
}
/*
*Класс предоставляющий модель данных.
*/
class Data{
  val values: Array[Rows] = Array.empty
}

Примечание

Модель Data необходимо так как коллекции в java не хранят информации о вложенных объектах, соответственно выражение classOf[Array[Member]] не может предоставить достаточной информации для типизированной десериализации.

Упражнения по scala#

Создание функции#

Создайте функцию прибавляющую 1 час к дате.

Создание класса#

Создайте scala класс для хранения данных по членам клуба, назовите атрибуты класса в соответствии с примером на 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 в модель скала классов:

{"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
        Участник

        Совет

        Для участника вы можете использовать класс созданные в предыдущем задании.

Поиск последнего участника#

Напишите выражение возвращающее члена клуба который присоединился к клубу последним.

В качестве входных данных используйте результат полученный в задании Загрузка классов из json

Для определение последнего присоединившегося необходимо использовать поле joindate

Совет

Смотрите методы доступные для работы с коллекциями

Сортировка при помощи операции#

Напишите scala функцию сравнивающую двух членов клуба по выражению member1.memid < member2.memid. Отсортируйте членов клуба с помощью данной функции.

Совет

Смотрите методы доступные для работы с коллекциями

Практика работа с jexl#

Выполнение произвольного jexl.#

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)
}

Совет

Для дополнительной информации по синтаксису jexl смотрите справку по синтаксису jexl.

Для понимания типов используемых в выражении смотрите Арифметику jexl

Упражнения по jexl#

Функция сравнения на jexl#

Напишите scala функцию которая используя выражения jexl сравнит двух участников по выражению member1.memid < member2.memid

Примечание

Для чтения свойства скала класса в jexl необходимо к имени свойства добавить скобки.

m1.memid()

Сортировка через jexl#

Напишите сортировку списка участников через jexl выражение member1.memid > member2.memid

Самостоятельное изучение#