Есть сущность Лицо.
Есть сущность хранящая историю изменения лица. (Исходники во вложении) Задача:
вытащить полное имя лица (Склеить Ф + И + О) в зависимости от:
Текущего момента «в машине времени», брать соответствующую строку
Текущего языка сессии пользователя, брать соответствующие колонки
Реализация в виде мета-колонки не позволяет сортировать и фильтровать по данной вычисляемой колонке в JPQL источниках данных и компонентах типа Table
Другими словами, скажите пожалуйста каком образом в cuba platform 6.8 реализовать такие подзадачи:
Как сделать вычисляемую колонку-свойство fullName состоящую из простой конкатенации ФИО;
Как сделать вычисляемую колонку-свойство, которая вместо коллекции возвращала бы один объект-строку, в зависимости от текущего параметра (дата) в сессии пользователя и колонок startDate и endDate сущности для которой используется мягкое удаление;
Как сделать вычисляемую колонку-свойство, которая берёт ту или иную колонку-свойство в зависимости от параметра сессии пользователя (язык);
…по которым (колонкам) можно сортировать и фильтровать в JPQL запросах
Использовать EntityListener нельзя потому, что на момент сохранения не известно какие параметры будут при чтении (дата и язык сессии) которые меняются динамически при работе
Решение вашей задачи зависит от технических требований.
Если сущность Лицо содержит немного записей - скажем 100 или 1000 - тогда вы можете загружать все сущности в память и реализовать фильтрацию и сортировку по любым вычисляемым свойствам в памяти.
Для этого нужно будет детально разобраться в классе CollectionDatasourceImpl, реализовать своего наследника этого класса с необходимой логикой. Например сортировка по колонке выполняется в методе CollectionDatasourceImpl#sort
Если же справочник большой и обрабатывать весь список сущностей в памяти не получится, то у вас нет других вариантов, кроме как добавлять дополнительные поля в модель данных, в базу данных.
Дело в том, что постраничная загрузка данных (пейджинг), фильтрация, сортировка по столбцу - чтобы эти операции быстро отрабатывали на больших справочниках, они должны выполняться в базе данных.
Сортировка транслируется в JPQL запросе как “sort by e.columnName”.
Фильтрация - в “where e.fieldName = :value”
Пейджинг - в “query.setFirstResult() / setMaxResults()”.
Тогда ORM формирует хороший SQL запрос к базе данных и быстро выполняет загрузку.
В вашем случае вы можете сделать так называемую денормализацию схемы БД - т.е. добавить дополнительные вычисляемые столбцы в таблицу, которые дублируют уже имеющиеся данные, но позволяют более удобно и быстро делать запросы к БД.
Например fullName - это отдельная колонка, которая может заполняться в CUBA приложении entity listener-ом или триггером в БД.
“вычисляемую колонку-свойство, которая вместо коллекции возвращала бы один объект-строку в зависимости от текущего параметра (дата)” - здесь посложнее, но я думаю что можно будет что-то придумать, если очень нужно.
“вычисляемую колонку-свойство, которая берёт ту или иную колонку-свойство в зависимости от параметра сессии пользователя (язык)” - набор языков в вашем приложении ограничен, не так ли? Русский, английский, еще какой-нибудь. Вы можете сделать 3-4 отдельных поля DESCRIPTION_RU, DESCRIPTION_EN, DESCRIPTION_KZ для всех локализаций, заполнять их через entity listeners и потом показывать одно из них в таблице в зависимости от языка текущей пользовательской сессии.
Если сущность … содержит немного записей - скажем 100 или 1000 … CollectionDatasourceImpl …
Понял. Буду использовать как-нибудь в другом месте, потому что …
Если же справочник большой …
Да. В одном из проектов 35 000 лиц (работники, кандидаты и т.д.)
… то у вас нет других вариантов, кроме как добавлять дополнительные поля в модель данных, в базу данных
вот этого то и хотелось бы избежать, потому что даже на текущий момент может быть как минимум 3 поля: shortName (Имя и Фамилия), fullName (ФИО) и fullNameNumber (ФИО и табельный номер)
и это всё на 2-х языках (минимум). Уже 6 колонок.
Вообще вопрос конечно я на форум задал в целом понять можно ли и как в платформе использовать калькулируемые на стороне БД колонки.
Типа добавляем на свойство сущности некую аннотацию
(a)Formula(expression = "last_name || ’ ’ || first_name || ’ ’ || middle_name || ’ (’ || employee_number || ‘)’ ")
и это бы выражение в ядре кубы подставлялось бы в SQL запросы фильтрации, сортировки и извлечения (select). Лица (работники) это просто пример. А так мы мы это решение использовали бы в куче мест.
вычисляемую колонку-свойство, которая вместо коллекции возвращала бы один объект-строку в зависимости от текущего параметра (дата)” - здесь посложнее, но я думаю что можно будет что-то придумать, если очень нужно
Очень нужно.
У нас в модулях HR очень много сущностей с историей изменения и с возможностью посмотреть состояние сущности на любой исторический момент времени.
Типа сейчас работник Иванова, но на 1 января она была Петрова, потому что ещё не вышла замуж и не сменила фамилию.
Или сейчас она старший бухгалтер. А на 1 января была младший бухгалтер. и т.д.
Вы можете сделать 3-4 отдельных поля … и потом показывать одно из них в таблице в зависимости от языка текущей пользовательской сессии.
Этот вариант у нас есть как обходной (workaround).
Но не хотелось бы его использовать как основной.
Представьте, у нас есть browse форма с 10 колонками. из которых 7 колонок это справочники (мультиязычные). У нас в системе заложено максимум 5 языков.
тогда нам в browse форме (в xml) пришлось бы выводить 3 + 7 * 5 = 38 колонок.
И потом в Java скрывать 7 * 4 = 28 колонок для языков несоответствующих текущему языку сессии.
И так по всей системе. Не совсем удобно в каждой browse форме дублировать колонки для мультиязычных справочников (они у нас все мультиязычные)
Вы можете добавлять колонки к таблице программно из контроллера экрана, нет нужды сразу добавлять все и потом скрывать неактуальные.
Бегло посмотрев “eclipselink calculated column” в поисковике, боюсь что вычисляемые столбцы в EclipseLink не поддерживаются.
Тут предлагают создать VIEW в базе данных и маппить на него сущность:
Тогда по этой сущности можно будет выполнять запросы на чтение.
Думаю, что это тоже можно реализовать через database view.
Глядя со стороны, в вашем случае будет успехом, если просто удастся реализовать данные функциональные требования. Удобно / неудобно в коде, уже второстепенный вопрос
Как хранить многоязычные подписи - это интересный вопрос, его можно решить по-разному.
Можно просто дублировать столбцы: NAME_RU, NAME_EN, …
Это легко ложится на ORM маппинг и быстро по скорости выборки, но много дублирования в коде, и подписи размазаны по всей схеме.
Другой вариант - создать отдельную таблицу для хранения всех локализованных подписей в системе:
create table localized_caption (
id serial,
text_ru varchar(512),
text_en varchar(512),
text_kz varchar(512),
primary key (id)
);
И потом изо всех справочников ссылаться на эту таблицу.
Здесь будет больше порядка в структуре данных, все подписи в одном месте, и можно реализовать в проекте единый механизм отображения / ввода локализованных подписей.
Но при загрузке данных во все списки добавятся лишние joins.
Третий вариант - это например использовать нестандартный тип данных. В postgres это например JSONB. Хранить подписи как словарь: ключ - это язык, значение - локализованная подпись. В этом случае понадобится реализовывать JPA converter и расширять CubaPostgreSQLPlatform, чтобы сохранять и читать атрибуты нестандартного типа jsonb. Но зато избегаются лишние joins при загрузке.
я б вообще сделал не text_ru, text_en а lang и text но это уже дело вкуса и необходимости
а по поводу доставания локализованных сотрудников, как вариант сделать отдельною сущьность LocalizedEmployee и сервис/датасорс который будет смореть локально пользователя и время и их исторических данных строить ее
с json тож интересное решение,но лично я не люблю всякие новые плюшки в базах
@budarov
допустим есть view которая извлекает записи нужного языка на нужную дату.
Тогда вопрос: Как передать в БД параметры сессии язык и дата, когда у каждого пользователя они свои, а сервер приложений использует пул сессий в БД а не выделенные ?
Язык никак не передать, локализацию можно реализовать через несколько отдельных столбцов или несколько отдельных database view. Считаем, что поддерживаемых языков в системе немного.
А дата будет учитываться в where.
Если есть история изменения, то можно составить database view со структурой вроде:
employee_id
surname
change_date
Тогда при запросе вы пишете
select * from surname_history
where employee_id = :employee_id
and change_date < :date
limit 1
и получите последнюю версию фамилии на переданную дату
Если на view surname_history ориентировать сущность, то получится делать и JPQL-запросы к сущности.