Создание выборки без класса
Contents
Создание выборки без класса#
Первичное создание файла выборки#
В нужной дирректории проекта (
имя_модуля/src/main/scala/ru/bitec/app/имя_модуля/...
) создайте Scala класс, задайте ему имя в формате<имя модуля>_<имя выборки>Avi
, напримерMct_ExampleAvi
.Класс должен наследоваться от
EntityAvi
.В файле должен присутствовать объект-компаньон, с его помощью в дальнейшем будут создаваться объекты целевых выборок в системе.
У класса должна присутствовать аннотация avm-файла, где указывается файл с разметкой выборок. Как создать avm-файл.
Общий вид файла должен быть следующим:
package ... import ... object Mct_ExampleAvi extends Mct_ExampleAvi @AvmFile(name = "Mct_Example.avm.xml") class Mct_ExampleAvi extends EntityAvi{ Код выбороки }
Создание трейта и методы получения данных, выводимых трейтом#
В классе создается трейт, который в дальнейшем будет использован для открытия экземпляра выборки.
Этот трейт должен наследоваться от одного или более базовых трейтов из
EntityAvi
, или от созданных вами, например:trait List extends Default with super.List trait MyList extends List
Для каждого трейта должна быть создана функция-компаньон, причем если имя вашего трейта совпадает с именем базового трейта из
BaseAvi
(наследуется вEntityAvi
) -Card
,List
,Lookup
, то функцию компаньон надо переопределять, а если имя иное, то функцию нужно создать:override def list(): List = { new List { override def meta = this } } def myList(): MyList = { new MyList { override def meta = this } }
Имя функции-компаньона начинается с прописной буквы, имя трейта с заглавной.
Если трейт создается для отображения данных из базы, тогда необходимо определить метод получения данных для выборки. Для этого существует два подхода:
Реляционный запрос:#
Базово определяется в методе
selectStatement
.Тело метода является обычным sql запросом, помещенным в scala-строку.
override protected def selectStatement: String = { s""" |select | st.id | ,st.sCaption | ,st.sCode | ,ot.sDescription as otherDescription |from someTable st |join otherTable ot on st.someParam = ot.otherParam |""".stripMargin }
Если итоговый набор данных требуется отфильтровать при помощи данных,
переданных извне
(обычно из фильтров и параметров выборок), и запрос имеет линейную структуру(нет вложенных запросов, временных таблиц и т.д., в которых требуется фильтрацияпо внешним данным
), тогда:Переопределяем метод
onRefresh
следующим образом:override protected def onRefresh: Recs = { prepareSelectStatement(s"&DefFltReferenceMacro and someStatement and ${someScalaCode}") }
DefFltReferenceMacro
- стандартное имя макроса соответствующего трейта из разметки avm-файла. Вы можете задать любое другое удобное вам имя макроса.someStatement
- дополнительные условия в формате sql.someScalaCode
- любой Scala-код, результатом работы которого являетсябулевое значение
или некие данные, которые подставляются в логическое выражение sql с одной из сторон:s".... ... t.idState = ${getSelfVar("idState").asNLong} ...."
Объектный запрос:#
Базово определяется в методе
onRefresh
.Примечание
Метод onRefresh возвращает как строку в виде sql-запроса, так и набор Scala-объектов, определяющих набор данных выборки
В теле метода создается некоторая коллекция однотипных объектов:
override protected def onRefresh: Recs = { //***_SomeClassApi().load(id) //***_SomeClassApi().byParent(parentRop[или parentId])[.toList[.filter[Not][.orderBy]]] //someTXIndex().byKey(TXIndexKey)[.toList[.filter[Not][.orderBy]]] new OQuery(***_SomeClassAta.Type) { where(t.someClassAttr === someScalaVAlue [and[or] ....]) } }
При помощи метода load - если требуется получить единственный объект (обычно используется в карточных выборках).
При помощи метода byParent - если класс является коллекцией, с передачей в него id или rop родительского объекта.
При помощи транзакционного индекса по переданному ключу - если класс не является коллекцией, или если нужно получить элементы коллекции не в разрезе предка, а в разрезе иного(-ых) атрибута(-ов) класса.
При помощи запроса OQuery.
Последние три подхода выбирайте на ваше усмотрение, в зависмиости от обстоятельств. Полученный в итоге набор rop класса можно финально отфильтровать при помощи методов filter[Not], отсортировать при помощи orderBy.
В отличие от реляционного запроса, предыдущие четыре подхода формируют набор данных в формате списка rop определенного класса и содержат только данные этого класса. Если требуется добавить данные, не принадлежащие классу, то:
Если нужно получить данные без обработки методами api, тогда можете пеоерпределить метод
onRefreshExt
:override protected def onRefreshExt: String = { s""" with t as ( select :idParent as idParent :gid/*@NString*/ as gid ..... ) SELECT t1.sHeadline as idParentHL ,..... FROM t join parent_table t1 on t.idParent = t1.id.... """ }
Во временной таблице t указываем, какие данные забираем из основного набора данных. Этот запрос выполняется для каждой отдельной rop-ы. Далее при помощи обычного sql запроса вытягиваем дополнительные данные.
Для gid атрибутов указываем аннотацию
/*@NString*/
, что бы парсер корректно передал данные с типом NGid.
Если нужно получить данные обработанные методами api:
В текущем трейте или его предке создается case class с атрибутами, которые вам требуется добавить на выборки:
case class AdditionalInfo( var someIdParameter: NLong, var someIdParameterHL: NString, var someStrParameter: NString, var someDateParameter: NDate, var someBoolParameter: NNumber, var someNumParameter: NNumber )
Обычно класс называется AdditionalInfo, но вы можете выбрать и иное имя.
someIdParameter
- ссылочный атрибут, добавляется для полноты данных выборки, обычно является скрытым (смотри пояснение свойстваeditorType
, настройкиchangeableAttr
), пользователю не виден.someIdParameterHL
[илиMC
] - заголовок ссылочного атрибута (MC
для кодов/мнемокодов), данные ссылочного атрибута, отображаемые пользователю.someStrParameter
- иные строковые данные.someDateParameter
- дата.someBoolParameter
- булевые данные, обычно на выборку передаются в виде данных в формате NNumber: 0 - ложное значение, остальные - истина. Используются в атрибутах с редактором check (смотри пояснение свойстваeditorType
).someNumParameter
- иные числовые данные.
После создается метод
getAdditionalInfo(rop)
:def getAdditionalInfo(rop: ***_SomeClassApi#ApiRop): AdditionalInfo = { //Получение и обработка данных AdditionalInfo( someIdParameter = ... someIdParameterHL = ... ....................... someNumParameter = ... ) }
Дополнительные данные добавляются в кортеж к изначальным данным выборки:
override protected def onRefresh: Recs = { val rop = ***_SomeClassApi().load(id) (rop, getAdditionalInfo(rop)) } или override protected def onRefresh: Recs = { ***_SomeClassApi().byParent(parentRop)//Или любые другие из трех перечисленных методов получения набора данных класса .map(rop => (rop, getAdditionalInfo(rop))) }
Создание трейта для получения данных от пользователя#
Иногда требуется создать выборку, единственной целью которой является получение данных от пользователя. В таком случае:
Метод
onRefresh
выглядит следующим образом:override protected def onRefresh: Recs = { "select 1 as id" }
Тк у выборки всегда должен присутствовать атрибут
id
для корректной работыУ выборки в avm-разметке создаем фильтры, которые будет заполнять пользователи.
После введения данных пользователем существует несколько вариантов их обработки:
Пользователь закрывает выборку
Если введенные пользователем данные не требуется возвращать в метод, вызвавший открытие текущей формы, то на закрытие выборки по согласию пользователя срабатывает метод
closeFormOk()
, соответственно в его переопределении в вашем трейте можно обработать введенные данные.Если данные требуется вернуть в метод, вызвавщий открытие текущей формы, то на закрытие выборки переопределяем метод
beforeCloseForm
:override def beforeCloseForm(event: BeforeCloseFormEvent): Unit = { event.lookupResultObject = someObjectWithUserData super.beforeCloseForm(event) }
Где
someObjectWithUserData
- некая структура, хранящая пользовательские данные. Может быть List-ом, кортежем, case class-ом и т.д. на ваш выбор.Получить такие данные можно в методе, вызвавшем открытие выборки, определив тип возвращаемых данных:
val data = Mct_ExampleAvi.myList().newForm().params(Map(...)).results(List(attributesName)).openLookup() val res = data.getResultObject.asInstanceOf[someObjectWithUserData]
Примечание
Здесь
someObjectWithUserData
- либо тот же самый тип, если есть возможность типизировать возвращаемое значение тем же классом, либо новая структура, схожая по строению, если, например, вlookupResultObject
сохранен объект case class-а, недоступного в месте вызова выборки, тогда достаточно типизировать этот объект кортежем(или, например, листом, если вlookupResultObject
записан лист), у которого вложенные типы следуют в том же порядке, что и у case class-а:saved case class example(NLong, Boolean, List[NString]) -> Tuple3(NLong, Boolean, List[NString])
saved List[(case class example, NLong, …)] -> List[(Tuple3(NLong, Boolean, List[NString]), NLong, …)]
Пользователь вызывает кастомную операцию на текущей выборке:
В этом случае требуется создать данную операцию, если ее еще нет, в текущем трейте или его предках, либо же переопределить такую операцию и забирать введенные пользователем данные напрямую из выборки при помощи методов
getVar("...")
/getSelfVar("...")
и обрабатывать любым удобным вам способом.