【问题标题】:How to fetch exact matching records in Spring JPA @Query如何在 Spring JPA @Query 中获取完全匹配的记录
【发布时间】:2025-12-04 04:35:01
【问题描述】:

我有两个实体

1.请求

@Getter
@Setter
@Entity
@Table(name = "request")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Request {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "status")
    private String status;

    @OneToMany(mappedBy = "requestId", fetch = FetchType.LAZY)
    private Collection<Service> services;
}

2。服务

@Getter
@Setter
@Entity
@Table(name = "service")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Service{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "request_id")
    private Integer requestId;

    @Column(name = "status")
    private String status;
}

现在,为了获取具有特定状态服务请求,我在@Query之后写了RequestDao 文件。

@Query(value = "SELECT DISTINCT req FROM request req JOIN req.services srvs WHERE (srvs.status = :serviceStatus)")
List<Request> getAll(@Param("serviceStatus") String serviceStatus)

因此,如果任何关联的服务与“where”条件匹配,我将在“request”对象中获取所有服务。而我期望“request”仅包含与“匹配”的那些服务where' 标准。

有人可以帮忙吗? 提前致谢。

【问题讨论】:

    标签: java mysql hibernate jpa spring-data-jpa


    【解决方案1】:

    这并不容易,因为 Hibernate/JPA 试图保证实体模型与数据库状态同步。您显然想要一个很可能不应该与数据库保持同步的投影。如果您确实必须这样做,您可以使用以下查询,但请注意,这可能会导致删除集合中不符合条件的服务元素:

    @Query(value = "SELECT DISTINCT req FROM request req JOIN FETCH req.services srvs WHERE (srvs.status = :serviceStatus)")
    List<Request> getAll(@Param("serviceStatus") String serviceStatus);
    

    这通常通过引入 DTO 来处理,我认为这是 Blaze-Persistence Entity Views 的完美用例。

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

    使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

    @EntityView(Request.class)
    public interface RequestDto {
        @IdMapping
        Integer getId();
        String getStatus();
        @Mapping("services[status = :serviceStatus]")
        Set<ServiceDto> getServices();
    
        @EntityView(Service.class)
        interface ServiceDto {
            @IdMapping
            Integer getId();
            Integer getRequestId();
            String getStatus();
        }
    }
    

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

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

    Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

    List<RequestDto> findAll(@OptionalParam("serviceStatus") String serviceStatus);
    

    最好的部分是,它只会获取实际需要的状态!

    【讨论】:

    • 谢谢@christian,但我不能使用投影,因为我的实体包含很多列和更深层次的关系,我也需要投影。我只是想知道为什么当我提到标准时JPA会返回所有记录,有没有更好的方法来处理它?
    • 那么,为嵌套关系创建 DTO 呢?就像我写的那样,您可以使用JOIN FETCH 告诉 Hibernate,联接应该用于初始化关联,但要注意这可能会导致删除,因为 Hibernate 会尝试使实体对象与数据库保持同步。如果您不更改集合,您可能会很幸运并且不会发生删除,但如果您确实必须使用这种方法,我建议您在检索后立即分离实体以避免出现问题。
    • 似乎唯一安全的解决方案是通过应用 foreach 循环手动删除不相关的项目并检查状态匹配...
    【解决方案2】:

    尝试将您的查询更新为:

    @Query(value = "SELECT DISTINCT req FROM request req LEFT JOIN req.services srvs WHERE (srvs.status = :serviceStatus)")
    List<Request> getAll(@Param("serviceStatus") String serviceStatus)
    

    【讨论】: