仅根据查询结果返回实体中的部分对象列表 - spring boot hibernate

问题描述

我有一个事件实体,如下所示。每个事件可能有一个 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 进行 Eager / Lazy 获取,而不考虑查询不想返回列表的所有值.

这是突出问题的查询 -

@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”在内的详细信息

如果有人有任何想法,那将不胜感激

解决方法

这就是它的工作原理。在您的特定情况下,该集合是延迟加载的,即您的加入无关紧要。通常,实体总是在映射时反映数据库状态。如果您从集合中过滤掉元素,这可能会导致您可能不想要的元素被删除。解决方案是使用 DTO。

我认为这是 Blaze-Persistence Entity Views 的完美用例。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义的模型之间轻松映射,例如类固醇上的 Spring Data Projections。这个想法是,您可以按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

您的用例的 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();
    }
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 查询。

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

Spring Data 集成允许您像使用 Spring Data Projections 一样使用它: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>
}