#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. Это именно то, что мне нужно. Спасибо!