Практика код
Contents
Практика код#
Вычисление суммы без использования буфера#
Пример, где подсчитывается сумма по записям коллекции Oil_PlanFactoryShipPos:
//получаем все записи коллекции по родителю (объект rop)
Oil_PlanFactoryShipPosApi().byParent(rop)
//получаем только значение поля nQtyLoad
//в случае незаполненности поля необходимо иметь значение 0, иначе в дальнейшем сумма с null = null
.map(_.get(_.nQtyLoad).nvl(0.nn))
//сложение, результат которого будет обернут безопасной конструкцией Option
.reduceOption(_ + _)
//распаковка Option
//если внутри был null или в коллекции не было записей, то вернётся указанное значение 0.nn
.getOrElse(0.nn)
Группировка объектов с использованием null-типов#
При группировке объектов коллекций, в которых используется null-типы нужно учитывать, что метод groupBy
считает хэши группируемых объектов,
поэтому перед группировкой, нужно убедиться, что параметр, используемый для группировки, не равен null.
Пример, с NPE:
List(
asd(123.nl, "asd".ns),
asd(None.nl, "asd".ns), // У NLong с underlying = null хэш не посчитается
).groupBy(_.id).foreach { case (_, name) =>
println(name)
}
Перед использованием groupBy
необходимо отфильтровать значения или использовать метод nvl
.
Сравнение диапазона дат#
Допустим документы имеют поля dBeginDoc и dEndDoc.
Фильтр имеет 2 поля dBeginFilter и dEndFilter.
Чтобы найти все документы, которые началом или окончанием входят в диапазон, указанный в фильтре, нужно использовать следующее условие:
dBeginDoc <= dEndFilter && dEndDoc >= dBeginFilter
.distinct или .toSet для scala-коллекций и особенности применения#
Если необходимо в scala-коллекции держать только уникальные объекты, можно использовать методы:
.distinct
.toSet
Перед использованием метода .distinct
scala-коллекцию необходимо подготовить: убрать значения null. Иначе в ходе выполнения программы выпадет ошибка java.lang.NullPointerException
.
Пример использования и демонстрация поведения методов:
test("distinctOrToSet") {
val data: Seq[NString] = Seq("h".ns, "i".ns, "i".ns, None.ns, None.ns)
println("Результат работы .distinct с null внутри scala-коллекции:")
try {
println(data.distinct)
} catch {
case e: Throwable => println("Ошибка:\n" + "java.lang.NullPointerException")
} finally {
println("\nРезультат работы .filter(_.isNotNull).distinct:")
println(data.filter(_.isNotNull).distinct)
try {
println("\nРезультат работы .toSet с null внутри scala-коллекции:")
println(data.toSet)
} catch {
case e: Throwable => println("Ошибка:\n" + "java.lang.NullPointerException\n")
}
}
}
Результат:
Примечание
Результат работы .distinct с null внутри scala-коллекции: Ошибка: java.lang.NullPointerException
Результат работы .filter(_.isNotNull).distinct: List(h, i)
Результат работы .toSet с null внутри scala-коллекции: Set(h, i, Null)
immutable.Map.builder
вместо mutable.Map
#
Если по результату сформированной Map она больше не изменяется, то для формирования лучше использовать конструктор immutable.Map.builder
, чем mutable.Map
.
Пример:
val map = Map.newBuilder[NString, NString]
map ++= Map("1" -> "11")
map += "2" -> "22"
map.result() //Map("1" -> "11", "2" -> "22")
Признак наличия модуля на проекте#
val isInstallProModule = session.sbtClassLoader.getModuleMap.containsKey("pro")
Вернёт true, если такой модуль есть в проекте, иначе false.
Когда использовать ASQL/ ASelect/ OQuery/ TxIndex/ refreshByParent и byParent у коллекций#
Про данные инструменты можно почитать здесь.
ASQL#
Выполнение реляционного запроса на чтение.
Внимание
Вызывает транзакцию в БД, не учитывая значения в кэше.
Удобен для получения значения по одному столбцу результата запроса.
val idaWagonByTrain = ASQL"""
select string_agg(cast(rwt.idWagon as varchar), ', ')
from Rzd_TrainWagon rwt
where rwt.idTrain = $idpTask
"""
//берется 1ый столбец результата запроса, как NString
//если результат запроса вернул несколько строк, то будет взята первая в конструкции Option,
// но предполагается, что результат вернёт максимум 1 строку
.as(nStr(1).singleOpt)
//если Option пустой, т.е. в результате запроса не было строк, то вернётся None.ns
.getOrElse(None.ns)
$idpTask
это подстановка значения из переменной scala idpTask
в запрос связанной переменной (binding).
Внимание
Нельзя использовать внутри цикла. Это приведёт к многочилсенным транзакциям в БД.
Вместо использования ASQL
внутри цикла с множественными транзакциями в БД нужно перед циклом одной транзакцией сформировать массив данных, который дальше будет использоваться внутри цикла.
ATSQL#
Выполнение реляционного запроса с изменением данных или блокировками.
Используется редко, в основном в ядровых процедурах, потому что минует серверную логику, записи в системные миксины, ведение аудитов и другое.
ASelect#
Выполнение реляционного запроса на чтение/запись.
Внимание
Вызывает транзакцию в БД, не учитывая значения в кэше.
Используется в основном для чтения, потому что при изменении данных минует серверную логику, записи в системные миксины, ведение аудитов и другое.
Удобен для получения значений по нескольким столбцам результата запроса.
val mapPerson: Map[NLong, NString] = new ASelect {
val sCode = asNString("sCode")
val id = asNLong("id")
SQL"""
select p.id
,p.sCode
from Bs_Person p
where idObjectType = $idvObjectType
"""
}.map { rv => //rv представляет собой одну строку результата запроса
rv.id() -> rv.sCode()
}.toMap
$idvObjectType
это подстановка значения из переменной scala idvObjectType
в запрос связанной переменной (binding).
Внимание
Нельзя использовать внутри цикла. Это приведёт к многочилсенным транзакциям в БД.
Вместо использования ASelect
внутри цикла с множественными транзакциями в БД нужно перед циклом одной транзакцией сформировать массив данных, который дальше будет использоваться внутри цикла.
Когда использовать ASQL, а когда ASelect#
ASQL удобен для получения значения по одному столбцу результата запроса.
ASelect удобен для получения значений по нескольким столбцам результата запроса.
Подстановка связанных переменных (binding)#
Актуально для инструментов ASQL, ATSQL, ASelect.
Примечание
Использование ASQL с подстановкой связанных переменных является полезной практикой, потому что запрос остаётся неизменным, меняется лишь значение параметра. Тем самым запрос не воспринимается системой, как новый, и будет записан в системную таблицу запросов единожды, что не приводит к распуханию БД.
Инструмент ASQL"""<текст запроса>"""
подставляет бинды с учётом типа данных переменной.
Если бы была подстановка
NString
, тоASQL
сам обернул бы значение в одинарные кавычки, т.е. нет необходимости их указывать вручную.Если
NString
подставляется в текстs"""<текст запроса>"""
, то одинарные кавычки необходимо указывать вручную.
Если текст запроса для ASQL"""<текст запроса>"""
собирается динамически средствами scala, в том числе название таблицы для select формируется переменной, то его необходимо подставлять через #$bind
, чтобы ASQL не обернул подставляемое значение в одинарные кавычки.
val sNameTb = {
if (a = 1) "Bs_Goods".ns
else "Bs_Person".ns
}
val ida: List[NLong] = ASQL"""
select t.id
from #$sNameTb
where idObjectType = $idvObjectType
""".as(nLong(1).*)