Я использую Grails с базой данных Oracle. Большая часть данных в моем приложении является частью иерархии, которая выглядит примерно так (каждый элемент содержит следующий):
- Направление
- * Группа 1006 *
- Строительная площадка
- Договор
- Осмотр
- Несоответствие
Данные, видимые пользователю, фильтруются в соответствии с его доступом, который может быть на уровне Направления, Группы или Строительной площадки в зависимости от роли пользователя.
Мы легко достигли этого, создав метод listWithSecurity для класса домена BuildingSite, который мы используем вместо списка в большей части системы. Мы создали еще один метод listWithSecurity для Contract. Это в основном делает Contract.findAllByContractIn (BuildingSite.listWithSecurity). И так далее с другими классами. Преимущество заключается в том, что вся логика доступа хранится в BuildingSite.listWithsecurity.
Проблема возникла, когда мы начали получать реальные данные в системе. Мы быстро нажали «ora-01795, максимальное количество выражений в списке - 1000». Справедливо сказать, передача списка из более чем 1000 литералов - не самая эффективная вещь, поэтому я попробовал другие способы, хотя это означало, что мне пришлось бы депортировать логику безопасности на каждый контроллер.
Казалось, очевидный способ - использовать такие критерии (для простоты я поставил здесь только доступ к уровню Направления):
def c = NonConformity.createCriteria()
def listToReturn = c.list(max:params.max, offset: params.offset?.toInteger() ?: 0)
{
inspection {
contract {
buildingSite {
group {
'in'("direction",listOfOneOrTwoDirections)
}
}
}
}
}
Я ожидал, что Grails создаст один запрос с объединениями, который позволит избежать ошибки ora-01795, но, похоже, он вызывает отдельный запрос для каждого уровня и передает результат обратно в Oracle как литерал в «in» для запроса другой уровень. Другими словами, он делает именно то, что делал я, поэтому получаю ту же ошибку.
На самом деле, это может немного оптимизировать. Кажется, это решает проблему, но только для одного уровня. В предыдущем примере я не получил бы ошибку для 1001 инспекции, но я бы получил ее для 1001 контракта или строительной площадки.
Я также пытался сделать в основном то же самое с findAll и одним оператором HQL where, которому я передал единственное направление, чтобы получить несоответствия в одном запросе. То же самое. Это решает первые уровни, но я получаю ту же ошибку для других уровней.
Мне удалось исправить это, разделив мои критерии «in» на множество «in» внутри «или», поэтому ни один из списков литералов не может быть длиннее 1000, но это ужасно уродливый код. Один findAllBy […] In становится более 10 строк кода. И в долгосрочной перспективе это, вероятно, вызовет проблемы с производительностью, поскольку мы застряли при выполнении запросов с очень большим количеством параметров.
Кто-нибудь сталкивался и решал эту проблему более элегантно и эффективно?