Языки разработки
Contents
Языки разработки#
Основными языками разработки являются
scala
Для программирования автономной и интерактивной бизнес логики.jexl
Для программирования динамических скриптов.PL/pgSQL
Для обработки данных в БД
Scala#
Язык разработки используемый для программирования бизнес логики сервера приложения.
Гайд по коллекциям
Может быть полезен, так как данные библиотеки очень удобны при манипуляциях со строками, к примеру операции сортировки и фильтрации
Прикладные разработчики в основном используют процедурную парадигму, что позволяет избежать
сложного порога вхождения со стороны разработчиков java
При этом scala
увеличивает производительность программистов за счет:
Наличие удобной библиотеки коллекций
Инлайн классов
Позволяет использовать специализированную арифметику при работе с null типами, при этом не перегружая сборщик мусораИнтерполяции строк
Облегчает работу с sql запросамиТрейтов
Позволяют реализовать наследование отображенийПрозрачного механизма инъекций зависимости
//При этом автоматически пробрасывается контекст сессии базы данных. //Использование фабрик позволяет прозрачно перекрывать контроллеры бизнес логики //в итоговом решении при необходимости SomeApi().load(id)
Фабрик контроллеров
За счет того что обращения к любому контроллеру идет не статично а через фабрику(SomeApi()
) возможно динамическое аспектирование(к примеру запись макросов) и проектное расширение кода(подмена базового контроллера его
проектным наследником)
Обработка исключений#
При обработки исключений нельзя перехватывать системные сообщения. Системные исключения всегда должны быть проброшены на верх. Это в том числе позволяет при необходимости прерывать сессию.
Для упрощения корректной обработки исключений введены вспомогательные функции и классы.
Объявление исключений#
Прикладные исключения объявляются только как наследники от класса AppException или от потомков класса AppException.
При объявлении класса исключения нужно создать класс, и фабрику.
class ApiException(params:AnyRef) extends AppException(params)
object ApiException extends ExceptionFactory(new ApiException(_))
Выбрасывания исключения#
Для выбрасывания исключения используется конструкция
throw AppException(args)
Внимание
Все прикладные исключения должны создаваться только от фабрики.
Перехват исключений#
Перехватываться могут только прикладные исключения, если необходима
принудительная очистка ресурсов в любом случае используйте секцию
finally
Пример:
try App{
println("start")
throw AppException("Ups")
}catch {
case e:AppException =>
e.raise("Ups2")
}finally{
println("end")
}
Внимание
Используйте
try app
Это позволит гарантировано получатьAppException
При необходимости продолжить выбрасывания сообщения используйте конструкцию
e.raise
При необходимости выбросить новый тип сообщения используйте конструкцию
e.raise(ApiException,"Ups2")
Данная практика позволит избегать потери стека.
Конвертация java исключений в прикладные исключения#
Все прикладные исключения являются наследниками от AppException
.
При необходимости типизированной обработки java исключений в прикладном коде, необходимо выполнять конвертацию java исключений в прикладные исключения, что делается в функции:
ru.bitec.app.gtk.exception.App#apply
Jexl скрипты#
Для выполнения динамического кода, существует jexl расширение.
Возможно выполнить jexl скрипт из:
Ssh консоли
login user/password@db jexl doSomeThing() /
Api
JexlScript().eval(“doSomeThing”)
Бизнес приложения, если у пользователя есть привелегии администратора
Используйте пункт меню
Сервис > Инструменты > Выполнить jexl скрипт
Обработка исключений в jexl#
Jexl в ядре не поддерживает исключения, механизм исключений реализован через аннотации
пример:
@begin{
logInfo("ok")
}@exception function(e){
rollback()
} end
Для бросания исключения можно воспользоваться командой raise
:
raise(“Текст исключения”);
Добавленные методы#
lpad
Дополнение строки символами слеваrpad
Дополнение строки символами справаflush()
Выполняет session.flushcommit()
Выполняет session. commitrollback()
Выполняет session. rollbackraise
Бросает исключение AppException с указанным текстомnvl
Возвращает первый параметр, если он не null, иначе второй параметр. Поддерживает nullable-типыisNull
Проверка на null указанного параметраisNotNull
Проверка, что параметр не nullforeachRop
Обход записей, возвращенных объектным запросом, напримерbyParent
pgArrayToNLongList
Преобразует массив Long в список NLong
Примечание
Для просмотра списка поддерживаемых методов смотрите:\
ru.bitec.app.gtk.jexl.session.JexlScriptContextExtension#ScopeBuilder
Работа с датами#
Методы работы с датами:
sysDate()
Текущая датаtruncDate
Дата на начало дня от переданнойtruncYear
Дата на начало года от переданнойtoDate
Переводит строку в дату по указанному формату. Если формат не указан, то используется стандартный. Стандартный форматdd.MM.yyyy
иdd.MM.yyyy HH:mm:ss
toString
Переводит дату в строку по указанному формату. Если формат не указан, то используется стандартный. Стандартный форматdd.MM.yyyy
иdd.MM.yyyy HH:mm:ss
+
,-
Используются для изменения даты, операторы добавляют или отнимают от указанной даты количество дней. НапримерsysDate() + 1
вернет дату на один день больше текущей
Работа с sql#
Для работы с sql используется команды
sql(sqlText:String)
Для запросов на чтениеtsql(sqlText:String)
Для запросов на запись
Основные функции:
execute
Выполняет sql коммандуasList
Выполняет запрос, и возвращает список строкasSingle
Возвращает записьforeach
Принимает на вход функцию для потоковой обработки запроса
Пример
var l= sql("select 1 d").asList()
for(r:l){
logInfo(r.d)
}
Работа с массивами объектов навигации (Rop)#
Для обхода записей, возвращенных функцией byParent используется метод toJRops Основные функции:
asList
Возвращает список строкasSingle
Возвращает записьforeach
Принимает на вход функцию для потоковой обработки запроса
Пример
var l= toJRops(Btk_SomeEntityApi.byParent(rop)).asList()
for(r:l){
logInfo(r.id);
logInfo(r.sSystemName)
}
Работа в контексте выборки#
Контекст выборки (JexlSelScript) – расширяет контекст бизнес-логики возможностью работы с выборками и пользовательским интерфейсом. Используется в тех случаях, когда необходимо получить данные из полей для пользовательского ввода. Пример методов, доступных из jexl-скрипта контекста выборки:
varExists
Проверка наличия переменной в выборке(если не найдено, пойдет поиск в мастер-выборках)selfVarExists
Проверка наличия переменной только в текущей выборкеsetVar
Установить значение переменной(если не найдено, пойдет поиск в мастер-выборках)setSelfVar
Установить значение переменной только в текущей выборкеgetVar
Получить значение переменной текущей выборки(если не найдено, пойдет поиск в мастер-выборках)getSelfVar
Получить значение переменной только из текущей выборкиaddVar
Добавить переменную в выборку с установкой значенияnewForm
Создание форм
Пример:
//Получаем значение из атрибута "id" выборки, приводим его к типу NString и пишем в переменную idTree
var idTree = getVar("id").asNString()
//Получаем атрибут DGLOBALENDDATE из мастер-выборки
var dEndDate = getVar("super$DGLOBALENDDATE").asNDate()
//Вызываем метод из пакета с передачей параметров
//Обратите внимание, для обращения к Api или пакетам используются их короткие имена(без скобок)
var fltCond = Act_UniversalReportPkg.getUniFilterCondByIdTree(idTree, dEndDate)
//Вызывается ещё один метод, возвращающий объект scala-класса immutable.Map[NString, Any]
var filters = Act_UniversalReportPkg.getFilterValues(idTree)
/*Определение Map-ы внутри jexl-скрипта. Отметим, что Map внутри jexl и объект scala-класса
Map (неважно mutable или immutable) - это разные объекты. Наиболее важным
фактом является то, что передать scala-объект Map, полученный в предыдущей строке,
в пакетный метод, принимающий scala-объект Map, в jexl-скрипте напрямую нельзя,
именно поэтому приходится получать значения из переменной filters, перезаписывать
их в jexl-Map param и потом передавать param в scala-метод.
var param = {"flt_idDepOwner" : filters['flt_idDepOwner'],
"flt_idAcc" : filters['flt_idAcc'],
"flt_dFrom" : filters['flt_dFrom'],
"flt_dTo" : filters['flt_dTo'],
"flt_idAdjustMethod" : filters['flt_idAdjustMethod'],
"flt_idAccCor" : filters['flt_idAccCor'],
"uniFilterCondition_dz": fltCond}
*/
//Открываем новую форму с переданными параметрами
Act_TransAvi.defList().newForm().params(param).open()
Контекстная справка#
В выборке выполнения jexl-скрипта есть возможность открытия выборки контекстной помощи, при нажатии на операцию «Помощь». В этой выборке в левой части поле ввода команд помощи, а права отображает справку.
Если методы справки вызывать из ssh-консоли, то результат будет напечатан в консоль.
Команды#
help
Справка по глобальным методам;listObj
Список доступных Api и Pkgdescribe
Описание Api иPkg
PL/pgSQL#
Язык обработки бизнес логики на стороне базы данных. Применяется в случаи если выполнение логики на сервере приложений не рационально в следствие высокой нагрузки на сеть или диск. Типичными примерами является:
Выполнение агрегаций
Построения аналитических запросов
При фильтрации и сортировки больших данных
Требования к масштабируемости#
При проектировании архитектуры приложения необходимо стремиться, чтобы горизонтальный рост системы не приводил к нелинейному увеличению потребностей в ресурсах.
Типичным узким местом в такой ситуации является увеличение запросов в базу данных при увеличении данных, когда код написан таким образом что при обработки бизнес логики на каждую строчку данных идет запрос в базу данных.
Типовыми вариантами сохранения масштабируемости методов являются:
Работа через объектный кэш с возможностью его предварительной загрузки
При этом кэш должен загружаться batch запросами, что значительно уменьшает нагрузку на базуМассовая обработка объектов
При этом обработка разбивается на порции, каждая порция данных вытаскивается и сохраняется batch запросами
Общие принципы наименования#
Global3-FrameWork является регистро-зависимым. Это означает что имена сущностей, свойств, методов и т.д. нужно указывать строго так, как они объявлены.
Предупреждение
Btk_Class и BTK_Class это разные сущности с точки зрения фреймворка
При написании кода очень важно соблюдать общие правила, это позволяет улучшить взаимодействие между людьми и повысить эффективность.
Общие принципы по работе с кодом:
Помните! Код чаще читается, чем пишется, поэтому не экономьте на понятности и чистоте кода ради скорости набора.
Не используйте подчеркивание для отделения слов внутри идентификаторов. Подчеркивание используется только после имени модуля.
Старайтесь не использовать сокращения лишний раз, помните о тех, кто читает код.
Старайтесь делать имена идентификаторов как можно короче (но не в ущерб читабельности). Главное, чтобы смысл идентификатора был понятен в используемом контексте.
Когда придумываете название для нового наименования, старайтесь не использовать имена, потенциально или явно конфликтующие со стандартными идентификаторами.
Предпочтительно использовать имена, которые ясно и четко описывают предназначение и/или смысл сущности.
Старайтесь использовать имена с простым написанием. Их легче читать и набирать. Избегайте (в разумных пределах) использования слов с двойными буквами, сложным чередованием согласных. Прежде, чем остановиться в выборе имени, убедитесь, что оно легко пишется и однозначно воспринимается на слух. Если оно с трудом читается, и вы ошибаетесь при его наборе, возможно, стоит выбрать другое.
Конвенция для разработки на scala#
Стандартный код именуется в соответствии с руководством по языку
Сокращения#
В основном сокращения использовать либо в горячих методах, либо в объектах базы данных, которые имеют ограничения.
Старайтесь не использовать аббревиатуры или неполные слова в идентификаторах, если только они не являются общепринятыми. Например, пишите getClass, а не getCls. (Исключением являются базовые понятия модуля, на которые имеет смысл придумать аббревиатуры, т.к. изначально сложно предсказать каким количеством коллекций они могут обладать, что может привести к вынужденным сокращениям в дальнейшем)
Не используйте акронимы, если они не общеприняты в области информационных технологий.
Широко распространенные акронимы используйте для замены длинных фраз. Например, UI вместо User Interface или Olap вместо On-line Analytical Processing.
Если имеется идентификатор длиной менее трех букв, являющийся сокращением, то его записывают заглавными буквами. Имена длиннее двух букв записывайте в стиле Паскаль Xml, xmlDocument.
Если, все-таки, сокращение необходимо из-за ограничений в длине наименования или по другим причинам, старайтесь сокращать наименее информативные части в имени и наиболее часто используемые (легко запомнить). К примеру, в таблице настроек есть поле idEnergyVolWithLostParamType -Тип показателя расхода электроэнергии с потерями. Что из этого можно сократить:
Energy - так как мы находимся в модуле энерго-учета, это поле обладает не большой информативностью и его можно сократить до En, по крайней мере, в контексте энерго-учета, легко вспомнить, что это электроэнергия.
VolWithLost — это основной смысл настройки, и его сокращать нельзя ни в коем случае
ParamType - тип параметра, так как в таблице настроек лежат в основном аналитики параметра учета, то на них можно придумать аббревиатуры. Название легко будет восстановить, помня о возможных аналитиках параметра учета.
В итоге, получаем сокращение idEnVolWithLostPT, с которым намного проще работать, чем с idEnergyVwltParamType
Scala типы для работы с данными#
Для удобной обработки данных язык скала расширен null типами. Null типы, добавляют null логику к стандартным java типам, а так же расширяют их функциональность.
Задачи null типов:
Определить стандартные операции
Исключить
Null pointer exception
Сделать арифметику более компактной
Для большей наглядности достаточно взглянуть на методы типов, к примеру для NNumber
:
def round(value: NLong): NNumber = {
if (this.isNull.isTrue || value.isNull.isTrue) this else NNumber(this.get.setScale(value.get.intValue(), RoundingMode.HALF_UP))
}
Альтернативой null типам в scala является класс 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
)
Любая колонка в таблице обрабатывается соответствующим ей null типом. При этом, происходит перегрузка стандартных операторов языка, создавая, таким образом, dsl расширение.
Для создания констант используются соответствующие фабрики:
NLong#
Используется для работы с идентификаторами.
Фабрика: nl
NNumber#
Используется для работы с числами
Фабрика: nn
NGid#
Используется для работы с глобальными идентификаторами
Фабрика: ng
NDate#
Используется для работы с датами.
Фабрика: nd
NString#
Используется для работы со строками.
Фабрика: ns
NBigDecimal#
Используется для работы с номерами. Внимание, обязательно используйте BigDecimal для выполнения расчетов. Использование чисел с двоичной арифметикой, может приводить к ошибкам.
Фабрика: nn
NDuration#
Используется для арифметики дат, к примеру:
dvNewDate = dvDate + (10.hours+5.mins)
Фабрики: seconds, minutes, hours, second, minute, hour
Наименование переменных для nullable типов и атрибутов#
Необходимо очень четко прослеживать, где работа идет с dsl, а где со стандартным типом. Наиболее яркий пример возможной, это операция сравнения.
If ( (idvSome1 === idvSome2 ) &&(nvNumber > 0.nn) {
}
В данном примере, если не использовать null логику, операция nvNumber
>0.nn
выдаст исключение, при nvNumber равном null.
Поэтому, использование null логики требует ввести эквивалент операции сравнения.
Так как, невозможно перегрузить оператор ==
, если он возвращает другой тип или
используется значимый класс.
Создан стандартный оператор ===
.
Внимание
Очень легко перепутать ===
и ==
поэтому использование
расширенных стандартов наименования для null типов обязательно.
Правила наименование переменной:
[Variable][a][t][Scope][Name][Suffix]
где:
[Variable]
Определяет типn
- Числоs
- Строкаj
- Строка в формате jsond
- Датаr
,x
- Записьu
,cur
- Курсорl
,blob
- Бинарные данные (bytea, blob)с
- Символьные данные (text, clob)b
- Булево значениеid
- Идентификаторgid
- Глобальный идентификатор
[a]
Определяет является ли переменная последовательностью[t]
Определяет пользовательский тип[Scope]
Область действияv
- Переменная процедурыg
- Переменная пакета (Как различать константы)p
- параметр процедуры
[Name]
Имя переменной[Suffix]
Суффикс_dz
- Системные атрибуты_z
- Проектные атрибуты
Примеры задания параметров#
dStart
- дата начала в таблицеdvStart
- дата начала переменной процедурыdgMaxDate
- максимальная дата переменной пакета которую нельзя менять.tvdaDate
- тип коллекции дат объявленный в процедуреdavDate
- коллекция дат объявленная в процедуреuvStudents
- курсор объявленный в процедуре.
Наименования Scala-пакетов#
Всегда в нижнем регистре.
Правильно:
ru.bitec.deepspace
ru.bitec.deep_space
Не правильно:
ru.bitec.deepSpace
Правила наименования сущностей#
Наименование классов#
[Имя модуля]_[Аббревиатура логического пакета][Системное имясущности]
Так как, имя класса привязано к таблице, необходимо согласовывать наименование классов, с наименованием таблицы, что накладывает ограничение на имена.
Наименование пакетов#
По аналогии с классами:
[Имя модуля]_[Аббревиатура логического пакета][Наименование сущности][pkg]
Наименование процедур и функций#
Используйте глаголы или комбинацию глагола и существительных и прилагательных для имен методов.
Старайтесь избегать неоднозначных глаголов:
Не правильно:
сheckItem
- проверить элемент
Абсолютно не сообщает, что произойдет в результате отработки данного метода, толи будет выдано исключения, толи возращен результат,
Правильно:
validateItem
- гарантировать корректность элемента
в случае несоответствия генерирует исключениеisItemValid
- возвращает истину если элемент корректенitemErrCode
- Возвращает код ошибкиdeleteItem
- удалить элементcreateOrder
- создать заказ
Наименование параметра#
Из имени и типа параметра должны быть понятны его назначение и смысл.
Не усложняйте методы параметрами, которые, возможно, будут использоваться в будущих версиях.
Если в будущем понадобится новый параметр, можно использовать перегрузку методов и значения по умолчанию.
Наименование временных таблиц#
[Имя модуля]_[Аббревиатура логического пакета][Системное имя][gtt]
Наименование выборок#
[Имя модуля]_[Аббревиатура логического пакета][Системное имя]
Данный способ наименование позволяет, более легко выполнять рефакторинг, при перемещении выборки между пакетами.
Наименование отображений#
[Системное имя]_[группа зависимости]
Где:
[группа зависимости]
Опциональный параметр в случаи если данные выборки имеют внешнии зависимости
Примеры зависимостей:
idDoc
Выборка отображает детализацию по документуTurnOver
Детализация к оборотной ведомости
Не следует называть группу зависимости в несоответствии с тем, что реально происходит, так как это вводит в заблуждения, к примеру, название idDoc говорит о том, что можно использовать эту детализацию в любых выборках для детализации документа, однако, если запрос заточен под конкретную выборку мастера, это невозможно.
Формат комментария в систему контроля версий#
format ::= (attention | label)? text
attention ::= add | rem | fix
label ::= (feat| test| doc | err ..)*
text ::= Краткий текст комментария. dp (при наличии)
dp ::= #id - Документ поддержки
Стандартные сокращения#
Odm (Object domain model)#
Доменная модель сущности. Используется для описания метаданных классов фреймворка: Обозначение, наименование, тип класса, атрибутный состав, ссылочность и т.д.
Orm (Object-Relational Mapper)#
Объектно-реляционное представление сущности. Сопоставляет объектную доменную модель и реляционную базу данных. Используется в EclipseLink
Pojo#
Java объект на сервере приложения. Хранит значения атрибутов объекта класса (строки таблицы БД) в оперативной памяти.
Dpi#
Доменная автономная бизнес логика (создается кода генератором). Обеспечивает для сущности базовую обвязку методами фреймворка.
Dvi#
Доменная интерактивная бизнес логика (создается кода генератором). Обеспечивает для сущности базовые методы работы с пользовательским интерфейсом.
Api#
Прикладная автономная бизнес-логика по классу. Содержит методы прикладной обработки данных для конкретной сущности.
Pkg#
Автономная бизнес логика. Содержит методы прикладной обработки данных. В отличии от api не привязана к какой либо конкретной сущности.
Avi#
Прикладная интерактивная бизнес логика. Содержит методы работы с пользовательским интерфейсом.
Dvm (Domain view markup)#
Доменная разметка выборки (создается автоматически). Шаблон базового представления сущности в системе.
Avm (Application view markup)#
Прикладная разметка выборки. Описывает базовое представление сущности в системе.
Ata (Application table)#
Прикладная таблица. Определение таблицы на языке Scala. Что позволяет использовать подсказчик кода, и статическую проверку при написании объектных запросов.
Aro (Application row)#
Прикладная строка. Определяет правила взаимодействия с сущностью (pojo) в EclipseLink. Предоставляет адаптированный для фреймворка способ взаимодействия с объектами EclipseLink.
Rop (Row provider)#
Провайдер строки. Используется для гарантированного доступа к строке (Aro) через кэш. При переходе к редактору rop проверяет наличие строки в кэше, и, если ее нет загружает строку из базы.