Запрос гибернации: объединение по ключу карты

#hibernate #dictionary #join #key

#гибернация #словарь #Присоединиться #Клавиша

Вопрос:

У меня есть следующие классы:

 public abstract class Entity<T> implements Serializable {
    protected long id;
    protected Map<Language,EntityMetaData> metaData;
...
public class Service extends Entity<Long> implements Serializable {
    protected String name;
  

Сопоставления XML:

 <class name="Service" table="SERVICES" schema="EDRIVE" dynamic-update="false" dynamic-insert="true" batch-size="30">
    <meta attribute="class-description">
        This class contains definitions of relations between entities
    </meta>
    <id name="id" type="long" column="id">
        <generator class="identity" />
    </id>
    <map name="metaData" table="ServiceMetaData">
        <key column="serviceid"/>
        <map-key-many-to-many column="languageId" class="com.core.domain.Language"/>
        <one-to-many entity-name="serviceMetaData"/>
    </map>
</class>

<class name="EntityMetaData" entity-name="serviceMetaData" table="SERVICESMETADATA" lazy="false">
    <id name="id" type="long" column="id" access="field">
        <generator class="identity" />
    </id>
    <property name="name" column="name" type="string" length="256" />
    <property name="description" column="description" type="string" length="512"/>
</class>
  

Я хочу загружать serviceMetadata только для определенного языка, т.Е. ru, en, uk и т. Д:

 Service serviceEntity = (Service) databaseUtilities
                .getSession()
                .createQuery("FROM Service s"
                          " JOIN FETCH s.metaData m"
                          " WHERE s.id = :id AND m.language = :language")
                .setParameter("id", serviceId)
                .setParameter("language", language)
                .uniqueResult();
  

там, где serviceId длинный, language является экземпляром класса Language .

Когда я пытаюсь использовать описанный выше подход, я получаю следующее исключение:

org.hibernate.Исключение QueryException: не удалось разрешить свойство: язык: serviceMetadata [ИЗ com.core.domain.service.Служба s ПРИСОЕДИНЯЕТСЯ к ВЫБОРКЕ s.Метаданные m, ГДЕ s.id = :идентификатор И m.язык = :язык]

В другом случае:

 Service serviceEntity = (Service) databaseUtilities
                .getSession()
                .createQuery("FROM Service s"
                          " WHERE s.id = :id AND s.metaData['language'] = :language")
                .setParameter("id", serviceId)
                .setParameter("language", language)
                .uniqueResult();
  

исключение

org.postgresql.util.Исключение PSQLException: ОШИБКА: недопустимый синтаксис ввода для типа integer: позиция «language»: 235

и ПЕРЕКРЕСТНОЕ ОБЪЕДИНЕНИЕ!

Хорошо, удалены все условия, ему принадлежит только сервис все метаданные, чтобы посмотреть, как он сконструирован. Вот сериализованный в JSON результирующий набор:

 "service": {
        "id": 5,
        "entityType": null,
        "entityKey": null,
        "metaData": {
            "com.core.domain.Language@57d41bfc": {
                "id": 2145,
                "entityType": null,
                "entityKey": null,
                "metaData": null,
                "miscMetaData": null,
                "name": "Заправка топливом",
                "description": null
            },
            "com.core.domain.Language@99ca0b20": {
                "id": 2144,
                "entityType": null,
                "entityKey": null,
                "metaData": null,
                "miscMetaData": null,
                "name": "Fueling",
                "description": null
            }
        },
        "miscMetaData": null,
        "name": "FUELING",
        "serviceType": {
            "id": 23,
            "entityType": null,
            "entityKey": null,
            "metaData": null,
            "miscMetaData": null,
            "value": "RECURRENT"
        }
    }
  

как вы можете видеть здесь, метаданные состоят из двух объектов (которые на самом деле являются PersistentMap с ключом = Language).

Итак, вопрос в том, как получить конкретное значение карты по языку с помощью гибернации?

Комментарии:

1. Попробуйте KEY(m) . См. Ссылку

Ответ №1:

JPA требует, чтобы состояние управляемых объектов и базы данных были синхронизированы в конце транзакции, поэтому выборка только частичной коллекции объекта по существу приведет к удалению невыбранных элементов, поэтому JPA этого не допускает. Однако вы можете сделать это с помощью гибернации, но я бы не рекомендовал это, потому что это может привести к удалениям.

Итак, чтобы получить то, что вы хотите, вам нужно написать запрос HQL и получить данные в DTO. В запросе вы выбираете все нужные вам поля и можете определить условие соединения по своему усмотрению. Что-то вроде следующего:

 SELECT s.id, s.name, m.id, m.name
FROM Service s
LEFT JOIN s.metadata m ON KEY(m) = 'en'
  

Я думаю, что это идеальный вариант использования для представлений сущностей с сохранением Блейза.

Я создал библиотеку, чтобы обеспечить простое сопоставление между моделями JPA и моделями, определяемыми пользовательским интерфейсом или абстрактным классом, что-то вроде весенних прогнозов данных на стероидах. Идея заключается в том, что вы определяете свою целевую структуру (модель домена) так, как вам нравится, и сопоставляете атрибуты (геттеры) через выражения JPQL с моделью сущности.

Модель DTO для вашего варианта использования может выглядеть следующим образом с Blaze-Persistence Entity-Views:

 @EntityView(Service.class)
public interface ServiceDto {
    @IdMapping
    Long getId();
    String getName();
    @Mapping("metadata['en']")
    LanguageDto getLanguage();

    @EntityView(EntityMetaData.class)
    interface LanguageDto {
        @IdMapping
        Long getId();
        String getName();
    }

    // Other mappings
}
  

Запрос — это вопрос применения представления сущности к запросу, простейшим из которых является просто запрос по идентификатору.

ServiceDto a = entityViewManager.find(entityManager, ServiceDto.class, id);

Интеграция данных Spring позволяет использовать его почти как проекции данных Spring: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Комментарии:

1. Это именно то, что мне нужно. Спасибо!