Возвращает только частичный список объектов в сущности на основе результатов запроса — spring boot hibernate

#hibernate #spring-data-jpa #spring-repositories

#спящий режим #spring-data-jpa #spring-репозитории

Вопрос:

У меня есть объект события, который выглядит следующим образом. Каждое событие может иметь список EventDetails, который также показан ниже. (Я максимально упростил эти объекты для объяснения)

 @Entity(name = "event")
data class Event(

    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    val id: UUID = UUID.randomUUID(),

    @OneToMany(
        cascade = [CascadeType.ALL],
        orphanRemoval = true
    )
    @JoinColumn(
        name = "event_id",
        nullable = false
    )
    val eventDetails: List<EventDetails>
)
 
 @Entity(name = "event_details")
data class EventDetails(

    @Id
    @GeneratedValue
    @Type(type = "uuid-char")
    val id: UUID = UUID.randomUUID(),

    val type: String
)
 

У меня есть запрос, в котором я хочу вернуть все события, которые содержат сведения определенных типов, но более того, для каждого события я хочу возвращать только список сведений с предоставленными типами — в этом случае hibernate возвращает полный список, который мне не нужен.

Поэтому я в основном хочу вернуть частичный список сведений о событиях на основе того, что передается в запрос, но полный список объектов возвращается каждый раз. По-видимому, это связано с тем, что hibernate извлекает события, соответствующие запросу, и впоследствии выполняет быструю / отложенную выборку всех EventDetails в зависимости от того, что я установил, не принимая во внимание, что запрос не хотел возвращать все значения списка.

Вот запрос, который выявил проблему —

 @Repository
interface EventRepository : CrudRepository<Event, UUID> {
    @Query(
        """
            select e.* from event e
            join event_details ed on e.id = ed.event_id
            where ed.type in :types
        """, nativeQuery = true
    )
    fun findEventsByDetails(
        @Param("types") types: List<String>
    ): List<AwardEvent>
}

 

Вот пример, подчеркивающий проблему. если у меня есть событие как таковое —

 Event(eventDetails = listOf(EventDetails(type = "TEST"), EventDetails("OTHER")))
 

и я запускаю следующую команду —

 eventRepository.findEventsByDetails(listOf("TEST"))
 

затем он должен вернуть список одного события —

 Event(eventDetails = listOf(EventDetails(type = "TEST")))
 

без подробностей, включая тип «OTHER_TEST»

Если у кого-нибудь есть какие-либо идеи, это было бы очень ценно

Ответ №1:

Вот как это работает. В вашем конкретном случае коллекция загружается с задержкой, т.Е. Ваше соединение не имеет значения. В общем, сущности всегда отражают состояние базы данных по мере их отображения. Если вы отфильтровываете элементы из коллекции, это может привести к удалению элементов, которые вы, вероятно, не хотите. Решение этой проблемы заключается в использовании DTO.

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

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

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

 @EntityView(Event.class)
public interface AwardEvent {
    @IdMapping
    UUID getId();
    String getName();
    @Mapping("eventDetails[type IN :types]")
    Set<EventDetailsDto> getEventDetails();

    @EntityView(EventDetails.class)
    interface EventDetailsDto {
        @IdMapping
        UUID getId();
        String getType();
    }
}
 

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

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

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

 @Repository
interface EventRepository : CrudRepository<Event, UUID> {
    fun findAllEvents(
        @OptionalParam("types") types: List<String>
    ): List<AwardEvent>
}