У меня следующие занятия:

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.

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

org.hibernate.QueryException: не удалось разрешить свойство: язык: serviceMetaData [FROM com.core.domain.service.Service s JOIN FETCH s.metaData m WHERE s.id =: id AND m.language =: language]

В противном случае:

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: ОШИБКА: недопустимый синтаксис ввода для целого типа: "язык" Позиция: 235

И CROSS JOIN!

Хорошо, удалили все условия, только служба + все метаданные принадлежат ему, чтобы увидеть, как он построен. Вот сериализованный набор результатов 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).

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

0
Green Root 4 Окт 2020 в 22:09

1 ответ

Лучший ответ

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

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

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

Я думаю, что это идеальный вариант использования для Blaze-Persistence Entity Views .

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

Вот пример кода для исходного итератора:

@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 Data позволяет использовать ее почти как Spring Data Projection: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

1
Christian Beikov 5 Окт 2020 в 06:26