Рекомендации по написанию скриптов

Общие правила

Работа с объектами

Запоминать полученный объект или метакласс в переменную при использовании его в скрипте более 1 раза

Эффективно "+":

def mc = api.metainfo.getMetaClass(subject);

prop.attr1 = mc.getAttribute(attr1);

prop.attr2 = mc.getAttribute(attr2);

prop.attr3 = mc.getAttribute(attr3);

prop.attr4 = mc.getAttribute(attr4);

Неэффективно "-":

prop.attr1 = api.metainfo.getMetaClass(subject).getAttribute(attr1);

prop.attr2 = api.metainfo.getMetaClass(subject).getAttribute(attr2);

prop.attr3 = api.metainfo.getMetaClass(subject).getAttribute(attr3);

prop.attr4 = api.metainfo.getMetaClass(subject).getAttribute(attr4);

Не рекомендуется получать объект по UUID без необходимости

Получение объекта по идентификатору — это тяжелая операция, рекомендуется не поднимать объект, там где это не нужно.

Эффективно "+":

def str = emplUUID;

А если нужно получить объект, то

def str = utils.get(emplUUID);

Неэффективно "-":

def emplUUID = 'employee$1234';

def str = utils.get('employee', ['uuid', emplUUID]).UUID;

Рекомендуется получать код метакласса напрямую у объекта, а не через метакласс

Эффективно "+":

def code = obj.metaClass.toString();

Неэффективно "-":

def queryMetaClass = api.metainfo.getMetaClass(obj); def code = queryMetaClass.code;

Работа с методами

utils.edit

Рекомендуется минимизировать количество вызовов операции utils.edit(...)

Замену нескольких атрибутов одного объекта необходимо выполнять в одной операции edit.

Эффективно "+":

utils.edit(subject, ['attr1' : object1, 'attr2' : object2, 'attr3' : object3]);

Неэффективно "-":

utils.edit(subject, ['attr1' : object1]);

utils.edit(subject, ['attr2' : object2]);

utils.edit(subject, ['attr3' : object3]);

utils.get

Метод utils.get всегда должен находить только один объект. Если при поиске через utils.get скрипт находит больше одного объекта, он выдает ошибку.

Например, если при использовании utils.get('employee', ['lastName' : 'Петров']) вернется несколько объектов, то при выполнении скрипта появится сообщение об ошибке, например, "Many objects in result".

В этом случае необходимо использовать или большее количество атрибутов для поиска, или метод utils.find(...).

Например, если заменить utils.get на utils.findFirst. Этот метод если найдет несколько объектов, то вернет один из них. Если не найдет ни одного, вернет так же null.

utils.find

Если utils.find() будет возвращать более 1000 объектов, то нужно проанализировать скрипт на возможность оптимизации. В качестве альтернативы рекомендуется рассмотреть использование api.db.query.

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

utils.find('serviceCall', [:], sp.limit(2).offset(5)))

Для подсчета количества объектов следует использовать метод utils.count(...), так как операция подсчета объектов в результирующей коллекции utils.find() может привести к большому количеству обращений к БД.

Неверное использование utils.find() может быть опасным и приводить к снижению работоспособности системы из-за нехватки памяти, например, если с помощью этого метода в скрипте получить все запросы, а затем получить какое-нибудь из свойств запроса и подсчитать количество запросов обладающих определенным свойством.

Использование utils.find() не будет приводить к снижению работоспособности, если в дальнейшем используется все коллекция, например, для записи в атрибут:

def scs = utils.find('serviceCall', [:]);

utils.edit(sc1, ['links':scs]);

Неважно сохраняется ли полученная коллекция предварительно в переменную или же используется напрямую в вызове utils.edit.

utils.edit(sc1, ['links':utils.find('serviceCall', [:])]);

Работа с большими коллекциями объектов

В описанных ниже случаях коллекция может содержать больше нескольких тысяч объектов и в работе скрипта может произойти ошибка из-за ограничений базы данных и особенностей работы Hibernate.

  • При создании или редактировании объектов (редактирование атрибута типа "Набор ссылок на БО").
  • При редактировании атрибута типа "Набор ссылок на БО" через hql.
  • При настройке условий фильтрации атрибутов через api.filtration, если среди условий используется вхождение в коллекцию.
  • При настройке условий фильтрации для списков, если среди условий используется вхождение в коллекцию.

При работе с большими коллекциями рекомендуется делить их на части по 1000, используя метод collate(), и выполнять операции по очереди.

Пример.

Copy
//для большого списка users вывести все запросы не в статусе closed:
 def builder = api.web.defineListLink(false)
 builder
     .setTitle("Не закрытые задачи на пользователях")
     .setClassCode('serviceCall')
     .setAttrGroup('system')
 def filter = builder.filter()
 filter.AND(filter.OR('state', 'notContains', new ru.naumen.core.shared.dto.SimpleDtObject('task:closed', )))
 def empList = users.collate(1000)
 def counter = 0
 def orFilters = []
 while(counter < empList.size())
 {
     def orFilter = builder.filter()
     orFilter = filter.OR('responsible', 'containsInSet', empList[counter])
     orFilters.add(orFilter)
     counter++
 }
 filter.AND(*orFilters)
 builder.setDaysToLive(1)
 return api.web.list(builder)

Работа с атрибутами, которые могут быть не заполнены

При обращении к атрибуту, который не является обязательным и может быть не заполнен у каких-то объектов, чтобы избежать ошибки NullPointerException (NPE) следует добавить оператор безопасной навигации "?"

Пример использования в скрипте:

def immediateSupervisor = subject?.responsibleEmployee?.immediateSupervisor // получаем руководителя сотрудника, ответственного за запрос
def head = immediateSupervisor ? immediateSupervisor : utils.get('root', [:]).head // если руководителя - нет, то - директора компании
return head?.title // получаем название

Работа со статическим контекстом

Из-за особенностей работы Groovy использование статических полей (static) в скриптах может приводить к ошибкам.

Например, ошибка возникнет при компиляции следующего кода:

Copy
@groovy.transform.Field static final String CONSTANT = 'ok'
class SomeClass
{
    static def someField = CONSTANT
}

Во избежание ошибок рекомендуется оборачивать статические поля в классы:

Copy
class SomeConstants
{
    static final String CONSTANT = 'ok'
}
class SomeClass
{
    def someField = SomeConstants.CONSTANT
}