Hibernate left join fetch 对子集执行附加查询

Posted

技术标签:

【中文标题】Hibernate left join fetch 对子集执行附加查询【英文标题】:Hibernate left join fetch performing additional query for subset 【发布时间】:2021-11-26 09:20:45 【问题描述】:

我想在一个查询中查询 date_to 等于 null 的具有分配的角色。 我需要返回所有角色,即使是那些没有分配任务的角色。

角色实体中的关系已设置:

 @OneToMany(mappedBy = "role")
     private List<Assignment> assignment;

Assignment Entity 与 Permission 有关系:

@ManyToMany
    @JoinTable(
            name = "assignment_version_permission",
            joinColumns = @JoinColumn(name = "assignment_version_id"),
            inverseJoinColumns = @JoinColumn(name = "permission_id"))
    private Set<Permission> permissions;

@Column(name = "date_to")
    private LocalDateTime dateTo;

并来自权限实体:

@ManyToMany
    @JoinTable(
            name = "assignment_version_permission",
            joinColumns = @JoinColumn(name = "permission_id"),
            inverseJoinColumns = @JoinColumn(name = "assignment_version_id"))
    @ToString.Exclude
    private List<Assignment> assignments;

我尝试仅在 date_to 等于 null 的情况下查询所有角色及其分配。

@Query("from Role r left join Assignment av on r.dbid = av.role.dbid and av.dateTo is null left join av.permissions p where r.name in :names")
    List<Role> readByRoleNameInOnlyActiveAssignments (List<String> names);

上述查询有效,但在 role.getAssignemts() 之后 如果没有分配,我只想接收 date_to 为 null 或 null 的分配。

现在 role.getAssignemts() 对所有与角色相关的分配进行额外查询。当一个角色没有活动的分配时,我不想收到任何一个。我怎样才能实现它?

我尝试使用 EntityGraph,但没有选项可以在 assignemns 上添加条件(仅返回 assignment date_to 为 null 的这些)

@EntityGraph(attributePaths = "assignments", "assignments.permissions")
    List<Role> readByNameIn(List<String> names);

【问题讨论】:

【参考方案1】:

这不是直接可能的,因为仅获取集合的子集是不安全的。如果您更改集合,被过滤掉的元素将在集合重新创建事件中被删除,这就是为什么不允许对获取连接应用 ON 条件的原因。您看到的第二个查询是因为 Hibernate 不知道您加入的作业对应于 assignments 关联。

实现这一点的唯一方法是使用 DTO 方法,我认为这是 Blaze-Persistence Entity Views 的完美用例。

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

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

@EntityView(Role.class)
public interface RoleDto 
    @IdMapping
    Long getId();
    String getName();
    @Mapping("assignments[dateTo IS NULL]")
    Set<AssignmentDto> getAssignments();

    @EntityView(Assignment.class)
    interface AssignmentDto 
        @IdMapping
        Long getId();
        Set<PermissionDto> getPermissions();
    
    @EntityView(Permission.class)
    interface PermissionDto 
        @IdMapping
        Long getId();
        String getAction();
    

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

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

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

Page<RoleDto> findAll(Pageable pageable);

或者在你的情况下

List<RoleDto> findAllByNameIn(List<String> names);

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

【讨论】:

以上是关于Hibernate left join fetch 对子集执行附加查询的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate中,left joininner join以及left join fetch区别(转)

Hibernate left join fetch 对子集执行附加查询

使用JPA和Hibernate时JOIN和JOIN FETCH有啥区别

Hibernate 在第一次 fetch-joining 然后 simple-joining 时不生成 JOIN

即使使用 @Fetch(FetchMode.JOIN),JPA + Hibernate 也会出现太多查询问题

HIbernate fetch join 发出额外的 sql 语句